mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 11:07:42 +00:00
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:
commit
90eb44a589
@ -1034,10 +1034,10 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hir::ExprAssign(ref l, ref r) => {
|
hir::ExprAssign(ref l, ref r) => {
|
||||||
// see comment on lvalues in
|
// see comment on places in
|
||||||
// propagate_through_lvalue_components()
|
// propagate_through_place_components()
|
||||||
let succ = self.write_lvalue(&l, succ, ACC_WRITE);
|
let succ = self.write_place(&l, succ, ACC_WRITE);
|
||||||
let succ = self.propagate_through_lvalue_components(&l, succ);
|
let succ = self.propagate_through_place_components(&l, succ);
|
||||||
self.propagate_through_expr(&r, succ)
|
self.propagate_through_expr(&r, succ)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1047,11 +1047,11 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
|||||||
let succ = self.propagate_through_expr(&l, succ);
|
let succ = self.propagate_through_expr(&l, succ);
|
||||||
self.propagate_through_expr(&r, succ)
|
self.propagate_through_expr(&r, succ)
|
||||||
} else {
|
} else {
|
||||||
// see comment on lvalues in
|
// see comment on places in
|
||||||
// propagate_through_lvalue_components()
|
// propagate_through_place_components()
|
||||||
let succ = self.write_lvalue(&l, succ, ACC_WRITE|ACC_READ);
|
let succ = self.write_place(&l, succ, ACC_WRITE|ACC_READ);
|
||||||
let succ = self.propagate_through_expr(&r, succ);
|
let succ = self.propagate_through_expr(&r, succ);
|
||||||
self.propagate_through_lvalue_components(&l, succ)
|
self.propagate_through_place_components(&l, succ)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1121,14 +1121,14 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
|||||||
|
|
||||||
hir::ExprInlineAsm(ref ia, ref outputs, ref inputs) => {
|
hir::ExprInlineAsm(ref ia, ref outputs, ref inputs) => {
|
||||||
let succ = ia.outputs.iter().zip(outputs).rev().fold(succ, |succ, (o, output)| {
|
let succ = ia.outputs.iter().zip(outputs).rev().fold(succ, |succ, (o, output)| {
|
||||||
// see comment on lvalues
|
// see comment on places
|
||||||
// in propagate_through_lvalue_components()
|
// in propagate_through_place_components()
|
||||||
if o.is_indirect {
|
if o.is_indirect {
|
||||||
self.propagate_through_expr(output, succ)
|
self.propagate_through_expr(output, succ)
|
||||||
} else {
|
} else {
|
||||||
let acc = if o.is_rw { ACC_WRITE|ACC_READ } else { ACC_WRITE };
|
let acc = if o.is_rw { ACC_WRITE|ACC_READ } else { ACC_WRITE };
|
||||||
let succ = self.write_lvalue(output, succ, acc);
|
let succ = self.write_place(output, succ, acc);
|
||||||
self.propagate_through_lvalue_components(output, succ)
|
self.propagate_through_place_components(output, succ)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1146,11 +1146,11 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn propagate_through_lvalue_components(&mut self,
|
fn propagate_through_place_components(&mut self,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
succ: LiveNode)
|
succ: LiveNode)
|
||||||
-> LiveNode {
|
-> LiveNode {
|
||||||
// # Lvalues
|
// # Places
|
||||||
//
|
//
|
||||||
// In general, the full flow graph structure for an
|
// In general, the full flow graph structure for an
|
||||||
// assignment/move/etc can be handled in one of two ways,
|
// assignment/move/etc can be handled in one of two ways,
|
||||||
@ -1160,7 +1160,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
|||||||
//
|
//
|
||||||
// The two kinds of graphs are:
|
// The two kinds of graphs are:
|
||||||
//
|
//
|
||||||
// Tracked lvalue Untracked lvalue
|
// Tracked place Untracked place
|
||||||
// ----------------------++-----------------------
|
// ----------------------++-----------------------
|
||||||
// ||
|
// ||
|
||||||
// | || |
|
// | || |
|
||||||
@ -1168,7 +1168,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
|||||||
// (rvalue) || (rvalue)
|
// (rvalue) || (rvalue)
|
||||||
// | || |
|
// | || |
|
||||||
// v || v
|
// v || v
|
||||||
// (write of lvalue) || (lvalue components)
|
// (write of place) || (place components)
|
||||||
// | || |
|
// | || |
|
||||||
// v || v
|
// v || v
|
||||||
// (succ) || (succ)
|
// (succ) || (succ)
|
||||||
@ -1177,25 +1177,25 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
|||||||
//
|
//
|
||||||
// I will cover the two cases in turn:
|
// I will cover the two cases in turn:
|
||||||
//
|
//
|
||||||
// # Tracked lvalues
|
// # Tracked places
|
||||||
//
|
//
|
||||||
// A tracked lvalue is a local variable/argument `x`. In
|
// A tracked place is a local variable/argument `x`. In
|
||||||
// these cases, the link_node where the write occurs is linked
|
// these cases, the link_node where the write occurs is linked
|
||||||
// to node id of `x`. The `write_lvalue()` routine generates
|
// to node id of `x`. The `write_place()` routine generates
|
||||||
// the contents of this node. There are no subcomponents to
|
// the contents of this node. There are no subcomponents to
|
||||||
// consider.
|
// consider.
|
||||||
//
|
//
|
||||||
// # Non-tracked lvalues
|
// # Non-tracked places
|
||||||
//
|
//
|
||||||
// These are lvalues like `x[5]` or `x.f`. In that case, we
|
// These are places like `x[5]` or `x.f`. In that case, we
|
||||||
// basically ignore the value which is written to but generate
|
// basically ignore the value which is written to but generate
|
||||||
// reads for the components---`x` in these two examples. The
|
// reads for the components---`x` in these two examples. The
|
||||||
// components reads are generated by
|
// components reads are generated by
|
||||||
// `propagate_through_lvalue_components()` (this fn).
|
// `propagate_through_place_components()` (this fn).
|
||||||
//
|
//
|
||||||
// # Illegal lvalues
|
// # Illegal places
|
||||||
//
|
//
|
||||||
// It is still possible to observe assignments to non-lvalues;
|
// It is still possible to observe assignments to non-places;
|
||||||
// these errors are detected in the later pass borrowck. We
|
// these errors are detected in the later pass borrowck. We
|
||||||
// just ignore such cases and treat them as reads.
|
// just ignore such cases and treat them as reads.
|
||||||
|
|
||||||
@ -1207,17 +1207,17 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// see comment on propagate_through_lvalue()
|
// see comment on propagate_through_place()
|
||||||
fn write_lvalue(&mut self, expr: &Expr, succ: LiveNode, acc: u32)
|
fn write_place(&mut self, expr: &Expr, succ: LiveNode, acc: u32)
|
||||||
-> LiveNode {
|
-> LiveNode {
|
||||||
match expr.node {
|
match expr.node {
|
||||||
hir::ExprPath(hir::QPath::Resolved(_, ref path)) => {
|
hir::ExprPath(hir::QPath::Resolved(_, ref path)) => {
|
||||||
self.access_path(expr.id, path, succ, acc)
|
self.access_path(expr.id, path, succ, acc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We do not track other lvalues, so just propagate through
|
// We do not track other places, so just propagate through
|
||||||
// to their subcomponents. Also, it may happen that
|
// to their subcomponents. Also, it may happen that
|
||||||
// non-lvalues occur here, because those are detected in the
|
// non-places occur here, because those are detected in the
|
||||||
// later pass borrowck.
|
// later pass borrowck.
|
||||||
_ => succ
|
_ => succ
|
||||||
}
|
}
|
||||||
@ -1363,14 +1363,14 @@ fn check_arm<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, arm: &'tcx hir::Arm) {
|
|||||||
fn check_expr<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, expr: &'tcx Expr) {
|
fn check_expr<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, expr: &'tcx Expr) {
|
||||||
match expr.node {
|
match expr.node {
|
||||||
hir::ExprAssign(ref l, _) => {
|
hir::ExprAssign(ref l, _) => {
|
||||||
this.check_lvalue(&l);
|
this.check_place(&l);
|
||||||
|
|
||||||
intravisit::walk_expr(this, expr);
|
intravisit::walk_expr(this, expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
hir::ExprAssignOp(_, ref l, _) => {
|
hir::ExprAssignOp(_, ref l, _) => {
|
||||||
if !this.tables.is_method_call(expr) {
|
if !this.tables.is_method_call(expr) {
|
||||||
this.check_lvalue(&l);
|
this.check_place(&l);
|
||||||
}
|
}
|
||||||
|
|
||||||
intravisit::walk_expr(this, expr);
|
intravisit::walk_expr(this, expr);
|
||||||
@ -1381,10 +1381,10 @@ fn check_expr<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, expr: &'tcx Expr) {
|
|||||||
this.visit_expr(input);
|
this.visit_expr(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output operands must be lvalues
|
// Output operands must be places
|
||||||
for (o, output) in ia.outputs.iter().zip(outputs) {
|
for (o, output) in ia.outputs.iter().zip(outputs) {
|
||||||
if !o.is_indirect {
|
if !o.is_indirect {
|
||||||
this.check_lvalue(output);
|
this.check_place(output);
|
||||||
}
|
}
|
||||||
this.visit_expr(output);
|
this.visit_expr(output);
|
||||||
}
|
}
|
||||||
@ -1409,7 +1409,7 @@ fn check_expr<'a, 'tcx>(this: &mut Liveness<'a, 'tcx>, expr: &'tcx Expr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||||
fn check_lvalue(&mut self, expr: &'tcx Expr) {
|
fn check_place(&mut self, expr: &'tcx Expr) {
|
||||||
match expr.node {
|
match expr.node {
|
||||||
hir::ExprPath(hir::QPath::Resolved(_, ref path)) => {
|
hir::ExprPath(hir::QPath::Resolved(_, ref path)) => {
|
||||||
if let Def::Local(nid) = path.def {
|
if let Def::Local(nid) = path.def {
|
||||||
@ -1423,7 +1423,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// For other kinds of lvalues, no checks are required,
|
// For other kinds of places, no checks are required,
|
||||||
// and any embedded expressions are actually rvalues
|
// and any embedded expressions are actually rvalues
|
||||||
intravisit::walk_expr(self, expr);
|
intravisit::walk_expr(self, expr);
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,8 @@
|
|||||||
//! | E.comp // access to an interior component
|
//! | E.comp // access to an interior component
|
||||||
//!
|
//!
|
||||||
//! Imagine a routine ToAddr(Expr) that evaluates an expression and returns an
|
//! Imagine a routine ToAddr(Expr) that evaluates an expression and returns an
|
||||||
//! address where the result is to be found. If Expr is an lvalue, then this
|
//! address where the result is to be found. If Expr is a place, then this
|
||||||
//! is the address of the lvalue. If Expr is an rvalue, this is the address of
|
//! is the address of the place. If Expr is an rvalue, this is the address of
|
||||||
//! some temporary spot in memory where the result is stored.
|
//! some temporary spot in memory where the result is stored.
|
||||||
//!
|
//!
|
||||||
//! Now, cat_expr() classifies the expression Expr and the address A=ToAddr(Expr)
|
//! Now, cat_expr() classifies the expression Expr and the address A=ToAddr(Expr)
|
||||||
@ -182,7 +182,7 @@ pub struct cmt_<'tcx> {
|
|||||||
pub id: ast::NodeId, // id of expr/pat producing this value
|
pub id: ast::NodeId, // id of expr/pat producing this value
|
||||||
pub span: Span, // span of same expr/pat
|
pub span: Span, // span of same expr/pat
|
||||||
pub cat: Categorization<'tcx>, // categorization of expr
|
pub cat: Categorization<'tcx>, // categorization of expr
|
||||||
pub mutbl: MutabilityCategory, // mutability of expr as lvalue
|
pub mutbl: MutabilityCategory, // mutability of expr as place
|
||||||
pub ty: Ty<'tcx>, // type of the expr (*see WARNING above*)
|
pub ty: Ty<'tcx>, // type of the expr (*see WARNING above*)
|
||||||
pub note: Note, // Note about the provenance of this cmt
|
pub note: Note, // Note about the provenance of this cmt
|
||||||
}
|
}
|
||||||
@ -517,7 +517,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
|
|||||||
// a bind-by-ref means that the base_ty will be the type of the ident itself,
|
// a bind-by-ref means that the base_ty will be the type of the ident itself,
|
||||||
// but what we want here is the type of the underlying value being borrowed.
|
// but what we want here is the type of the underlying value being borrowed.
|
||||||
// So peel off one-level, turning the &T into T.
|
// So peel off one-level, turning the &T into T.
|
||||||
match base_ty.builtin_deref(false, ty::NoPreference) {
|
match base_ty.builtin_deref(false) {
|
||||||
Some(t) => t.ty,
|
Some(t) => t.ty,
|
||||||
None => {
|
None => {
|
||||||
debug!("By-ref binding of non-derefable type {:?}", base_ty);
|
debug!("By-ref binding of non-derefable type {:?}", base_ty);
|
||||||
@ -603,7 +603,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
|
|||||||
match expr.node {
|
match expr.node {
|
||||||
hir::ExprUnary(hir::UnDeref, ref e_base) => {
|
hir::ExprUnary(hir::UnDeref, ref e_base) => {
|
||||||
if self.tables.is_method_call(expr) {
|
if self.tables.is_method_call(expr) {
|
||||||
self.cat_overloaded_lvalue(expr, e_base, false)
|
self.cat_overloaded_place(expr, e_base, false)
|
||||||
} else {
|
} else {
|
||||||
let base_cmt = self.cat_expr(&e_base)?;
|
let base_cmt = self.cat_expr(&e_base)?;
|
||||||
self.cat_deref(expr, base_cmt, false)
|
self.cat_deref(expr, base_cmt, false)
|
||||||
@ -631,7 +631,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
|
|||||||
// The call to index() returns a `&T` value, which
|
// The call to index() returns a `&T` value, which
|
||||||
// is an rvalue. That is what we will be
|
// is an rvalue. That is what we will be
|
||||||
// dereferencing.
|
// dereferencing.
|
||||||
self.cat_overloaded_lvalue(expr, base, true)
|
self.cat_overloaded_place(expr, base, true)
|
||||||
} else {
|
} else {
|
||||||
let base_cmt = self.cat_expr(&base)?;
|
let base_cmt = self.cat_expr(&base)?;
|
||||||
self.cat_index(expr, base_cmt, expr_ty, InteriorOffsetKind::Index)
|
self.cat_index(expr, base_cmt, expr_ty, InteriorOffsetKind::Index)
|
||||||
@ -983,27 +983,27 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
|
|||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cat_overloaded_lvalue(&self,
|
fn cat_overloaded_place(&self,
|
||||||
expr: &hir::Expr,
|
expr: &hir::Expr,
|
||||||
base: &hir::Expr,
|
base: &hir::Expr,
|
||||||
implicit: bool)
|
implicit: bool)
|
||||||
-> McResult<cmt<'tcx>> {
|
-> McResult<cmt<'tcx>> {
|
||||||
debug!("cat_overloaded_lvalue: implicit={}", implicit);
|
debug!("cat_overloaded_place: implicit={}", implicit);
|
||||||
|
|
||||||
// Reconstruct the output assuming it's a reference with the
|
// Reconstruct the output assuming it's a reference with the
|
||||||
// same region and mutability as the receiver. This holds for
|
// same region and mutability as the receiver. This holds for
|
||||||
// `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`.
|
// `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`.
|
||||||
let lvalue_ty = self.expr_ty(expr)?;
|
let place_ty = self.expr_ty(expr)?;
|
||||||
let base_ty = self.expr_ty_adjusted(base)?;
|
let base_ty = self.expr_ty_adjusted(base)?;
|
||||||
|
|
||||||
let (region, mutbl) = match base_ty.sty {
|
let (region, mutbl) = match base_ty.sty {
|
||||||
ty::TyRef(region, mt) => (region, mt.mutbl),
|
ty::TyRef(region, mt) => (region, mt.mutbl),
|
||||||
_ => {
|
_ => {
|
||||||
span_bug!(expr.span, "cat_overloaded_lvalue: base is not a reference")
|
span_bug!(expr.span, "cat_overloaded_place: base is not a reference")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let ref_ty = self.tcx.mk_ref(region, ty::TypeAndMut {
|
let ref_ty = self.tcx.mk_ref(region, ty::TypeAndMut {
|
||||||
ty: lvalue_ty,
|
ty: place_ty,
|
||||||
mutbl,
|
mutbl,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1019,7 +1019,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
|
|||||||
debug!("cat_deref: base_cmt={:?}", base_cmt);
|
debug!("cat_deref: base_cmt={:?}", base_cmt);
|
||||||
|
|
||||||
let base_cmt_ty = base_cmt.ty;
|
let base_cmt_ty = base_cmt.ty;
|
||||||
let deref_ty = match base_cmt_ty.builtin_deref(true, ty::NoPreference) {
|
let deref_ty = match base_cmt_ty.builtin_deref(true) {
|
||||||
Some(mt) => mt.ty,
|
Some(mt) => mt.ty,
|
||||||
None => {
|
None => {
|
||||||
debug!("Explicit deref of non-derefable type: {:?}",
|
debug!("Explicit deref of non-derefable type: {:?}",
|
||||||
@ -1386,7 +1386,7 @@ impl<'tcx> cmt_<'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `FreelyAliasable(_)` if this lvalue represents a freely aliasable pointer type.
|
/// Returns `FreelyAliasable(_)` if this place represents a freely aliasable pointer type.
|
||||||
pub fn freely_aliasable(&self) -> Aliasability {
|
pub fn freely_aliasable(&self) -> Aliasability {
|
||||||
// Maybe non-obvious: copied upvars can only be considered
|
// Maybe non-obvious: copied upvars can only be considered
|
||||||
// non-aliasable in once closures, since any other kind can be
|
// non-aliasable in once closures, since any other kind can be
|
||||||
@ -1453,7 +1453,7 @@ impl<'tcx> cmt_<'tcx> {
|
|||||||
"static item".to_string()
|
"static item".to_string()
|
||||||
}
|
}
|
||||||
Categorization::Rvalue(..) => {
|
Categorization::Rvalue(..) => {
|
||||||
"non-lvalue".to_string()
|
"non-place".to_string()
|
||||||
}
|
}
|
||||||
Categorization::Local(vid) => {
|
Categorization::Local(vid) => {
|
||||||
if tcx.hir.is_argument(vid) {
|
if tcx.hir.is_argument(vid) {
|
||||||
|
@ -1112,7 +1112,7 @@ fn resolve_local<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>,
|
|||||||
// I mean that creating a binding into a ref-counted or managed value
|
// I mean that creating a binding into a ref-counted or managed value
|
||||||
// would still count.)
|
// would still count.)
|
||||||
//
|
//
|
||||||
// 3. `ET`, which matches both rvalues like `foo()` as well as lvalues
|
// 3. `ET`, which matches both rvalues like `foo()` as well as places
|
||||||
// based on rvalues like `foo().x[2].y`.
|
// based on rvalues like `foo().x[2].y`.
|
||||||
//
|
//
|
||||||
// A subexpression `<rvalue>` that appears in a let initializer
|
// A subexpression `<rvalue>` that appears in a let initializer
|
||||||
@ -1283,7 +1283,7 @@ fn resolve_local<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>,
|
|||||||
/// | (ET)
|
/// | (ET)
|
||||||
/// | <rvalue>
|
/// | <rvalue>
|
||||||
///
|
///
|
||||||
/// Note: ET is intended to match "rvalues or lvalues based on rvalues".
|
/// Note: ET is intended to match "rvalues or places based on rvalues".
|
||||||
fn record_rvalue_scope<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>,
|
fn record_rvalue_scope<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>,
|
||||||
expr: &hir::Expr,
|
expr: &hir::Expr,
|
||||||
blk_scope: Option<Scope>) {
|
blk_scope: Option<Scope>) {
|
||||||
|
@ -733,13 +733,13 @@ pub enum TerminatorKind<'tcx> {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/// Drop the Place and assign the new value over it. This ensures
|
/// Drop the Place and assign the new value over it. This ensures
|
||||||
/// that the assignment to LV occurs *even if* the destructor for
|
/// that the assignment to `P` occurs *even if* the destructor for
|
||||||
/// place unwinds. Its semantics are best explained by by the
|
/// place unwinds. Its semantics are best explained by by the
|
||||||
/// elaboration:
|
/// elaboration:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// BB0 {
|
/// BB0 {
|
||||||
/// DropAndReplace(LV <- RV, goto BB1, unwind BB2)
|
/// DropAndReplace(P <- V, goto BB1, unwind BB2)
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
@ -747,15 +747,15 @@ pub enum TerminatorKind<'tcx> {
|
|||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// BB0 {
|
/// BB0 {
|
||||||
/// Drop(LV, goto BB1, unwind BB2)
|
/// Drop(P, goto BB1, unwind BB2)
|
||||||
/// }
|
/// }
|
||||||
/// BB1 {
|
/// BB1 {
|
||||||
/// // LV is now unitialized
|
/// // P is now unitialized
|
||||||
/// LV <- RV
|
/// P <- V
|
||||||
/// }
|
/// }
|
||||||
/// BB2 {
|
/// BB2 {
|
||||||
/// // LV is now unitialized -- its dtor panicked
|
/// // P is now unitialized -- its dtor panicked
|
||||||
/// LV <- RV
|
/// P <- V
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
DropAndReplace {
|
DropAndReplace {
|
||||||
|
@ -52,7 +52,7 @@ impl<'a, 'gcx, 'tcx> PlaceTy<'tcx> {
|
|||||||
match *elem {
|
match *elem {
|
||||||
ProjectionElem::Deref => {
|
ProjectionElem::Deref => {
|
||||||
let ty = self.to_ty(tcx)
|
let ty = self.to_ty(tcx)
|
||||||
.builtin_deref(true, ty::LvaluePreference::NoPreference)
|
.builtin_deref(true)
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
bug!("deref projection of non-dereferencable ty {:?}", self)
|
bug!("deref projection of non-dereferencable ty {:?}", self)
|
||||||
})
|
})
|
||||||
|
@ -77,7 +77,7 @@ pub enum Adjust<'tcx> {
|
|||||||
/// Go from a mut raw pointer to a const raw pointer.
|
/// Go from a mut raw pointer to a const raw pointer.
|
||||||
MutToConstPointer,
|
MutToConstPointer,
|
||||||
|
|
||||||
/// Dereference once, producing an lvalue.
|
/// Dereference once, producing a place.
|
||||||
Deref(Option<OverloadedDeref<'tcx>>),
|
Deref(Option<OverloadedDeref<'tcx>>),
|
||||||
|
|
||||||
/// Take the address and produce either a `&` or `*` pointer.
|
/// Take the address and produce either a `&` or `*` pointer.
|
||||||
|
@ -12,7 +12,6 @@ pub use self::Variance::*;
|
|||||||
pub use self::AssociatedItemContainer::*;
|
pub use self::AssociatedItemContainer::*;
|
||||||
pub use self::BorrowKind::*;
|
pub use self::BorrowKind::*;
|
||||||
pub use self::IntVarValue::*;
|
pub use self::IntVarValue::*;
|
||||||
pub use self::LvaluePreference::*;
|
|
||||||
pub use self::fold::TypeFoldable;
|
pub use self::fold::TypeFoldable;
|
||||||
|
|
||||||
use hir::{map as hir_map, FreevarMap, TraitMap};
|
use hir::{map as hir_map, FreevarMap, TraitMap};
|
||||||
@ -2099,21 +2098,6 @@ impl<'tcx> TyS<'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum LvaluePreference {
|
|
||||||
PreferMutLvalue,
|
|
||||||
NoPreference
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LvaluePreference {
|
|
||||||
pub fn from_mutbl(m: hir::Mutability) -> Self {
|
|
||||||
match m {
|
|
||||||
hir::MutMutable => PreferMutLvalue,
|
|
||||||
hir::MutImmutable => NoPreference,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BorrowKind {
|
impl BorrowKind {
|
||||||
pub fn from_mutbl(m: hir::Mutability) -> BorrowKind {
|
pub fn from_mutbl(m: hir::Mutability) -> BorrowKind {
|
||||||
match m {
|
match m {
|
||||||
@ -2193,60 +2177,6 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expr_is_lval(self, expr: &hir::Expr) -> bool {
|
|
||||||
match expr.node {
|
|
||||||
hir::ExprPath(hir::QPath::Resolved(_, ref path)) => {
|
|
||||||
match path.def {
|
|
||||||
Def::Local(..) | Def::Upvar(..) | Def::Static(..) | Def::Err => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hir::ExprType(ref e, _) => {
|
|
||||||
self.expr_is_lval(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
hir::ExprUnary(hir::UnDeref, _) |
|
|
||||||
hir::ExprField(..) |
|
|
||||||
hir::ExprTupField(..) |
|
|
||||||
hir::ExprIndex(..) => {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Partially qualified paths in expressions can only legally
|
|
||||||
// refer to associated items which are always rvalues.
|
|
||||||
hir::ExprPath(hir::QPath::TypeRelative(..)) |
|
|
||||||
|
|
||||||
hir::ExprCall(..) |
|
|
||||||
hir::ExprMethodCall(..) |
|
|
||||||
hir::ExprStruct(..) |
|
|
||||||
hir::ExprTup(..) |
|
|
||||||
hir::ExprIf(..) |
|
|
||||||
hir::ExprMatch(..) |
|
|
||||||
hir::ExprClosure(..) |
|
|
||||||
hir::ExprBlock(..) |
|
|
||||||
hir::ExprRepeat(..) |
|
|
||||||
hir::ExprArray(..) |
|
|
||||||
hir::ExprBreak(..) |
|
|
||||||
hir::ExprAgain(..) |
|
|
||||||
hir::ExprRet(..) |
|
|
||||||
hir::ExprWhile(..) |
|
|
||||||
hir::ExprLoop(..) |
|
|
||||||
hir::ExprAssign(..) |
|
|
||||||
hir::ExprInlineAsm(..) |
|
|
||||||
hir::ExprAssignOp(..) |
|
|
||||||
hir::ExprLit(_) |
|
|
||||||
hir::ExprUnary(..) |
|
|
||||||
hir::ExprBox(..) |
|
|
||||||
hir::ExprAddrOf(..) |
|
|
||||||
hir::ExprBinary(..) |
|
|
||||||
hir::ExprYield(..) |
|
|
||||||
hir::ExprCast(..) => {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn provided_trait_methods(self, id: DefId) -> Vec<AssociatedItem> {
|
pub fn provided_trait_methods(self, id: DefId) -> Vec<AssociatedItem> {
|
||||||
self.associated_items(id)
|
self.associated_items(id)
|
||||||
.filter(|item| item.kind == AssociatedKind::Method && item.defaultness.has_value())
|
.filter(|item| item.kind == AssociatedKind::Method && item.defaultness.has_value())
|
||||||
|
@ -1514,18 +1514,12 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
|
|||||||
///
|
///
|
||||||
/// The parameter `explicit` indicates if this is an *explicit* dereference.
|
/// The parameter `explicit` indicates if this is an *explicit* dereference.
|
||||||
/// Some types---notably unsafe ptrs---can only be dereferenced explicitly.
|
/// Some types---notably unsafe ptrs---can only be dereferenced explicitly.
|
||||||
pub fn builtin_deref(&self, explicit: bool, pref: ty::LvaluePreference)
|
pub fn builtin_deref(&self, explicit: bool) -> Option<TypeAndMut<'tcx>> {
|
||||||
-> Option<TypeAndMut<'tcx>>
|
|
||||||
{
|
|
||||||
match self.sty {
|
match self.sty {
|
||||||
TyAdt(def, _) if def.is_box() => {
|
TyAdt(def, _) if def.is_box() => {
|
||||||
Some(TypeAndMut {
|
Some(TypeAndMut {
|
||||||
ty: self.boxed_ty(),
|
ty: self.boxed_ty(),
|
||||||
mutbl: if pref == ty::PreferMutLvalue {
|
mutbl: hir::MutImmutable,
|
||||||
hir::MutMutable
|
|
||||||
} else {
|
|
||||||
hir::MutImmutable
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
TyRef(_, mt) => Some(mt),
|
TyRef(_, mt) => Some(mt),
|
||||||
|
@ -43,14 +43,14 @@ it is safe with respect to the in-scope loans.
|
|||||||
# Formal model
|
# Formal model
|
||||||
|
|
||||||
Throughout the docs we'll consider a simple subset of Rust in which
|
Throughout the docs we'll consider a simple subset of Rust in which
|
||||||
you can only borrow from lvalues, defined like so:
|
you can only borrow from places, defined like so:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
LV = x | LV.f | *LV
|
P = x | P.f | *P
|
||||||
```
|
```
|
||||||
|
|
||||||
Here `x` represents some variable, `LV.f` is a field reference,
|
Here `x` represents some variable, `P.f` is a field reference,
|
||||||
and `*LV` is a pointer dereference. There is no auto-deref or other
|
and `*P` is a pointer dereference. There is no auto-deref or other
|
||||||
niceties. This means that if you have a type like:
|
niceties. This means that if you have a type like:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
@ -58,7 +58,7 @@ struct S { f: i32 }
|
|||||||
```
|
```
|
||||||
|
|
||||||
and a variable `a: Box<S>`, then the rust expression `a.f` would correspond
|
and a variable `a: Box<S>`, then the rust expression `a.f` would correspond
|
||||||
to an `LV` of `(*a).f`.
|
to an `P` of `(*a).f`.
|
||||||
|
|
||||||
Here is the formal grammar for the types we'll consider:
|
Here is the formal grammar for the types we'll consider:
|
||||||
|
|
||||||
@ -99,7 +99,7 @@ this sort of thing.
|
|||||||
#### Loans and restrictions
|
#### Loans and restrictions
|
||||||
|
|
||||||
The way the borrow checker works is that it analyzes each borrow
|
The way the borrow checker works is that it analyzes each borrow
|
||||||
expression (in our simple model, that's stuff like `&LV`, though in
|
expression (in our simple model, that's stuff like `&P`, though in
|
||||||
real life there are a few other cases to consider). For each borrow
|
real life there are a few other cases to consider). For each borrow
|
||||||
expression, it computes a `Loan`, which is a data structure that
|
expression, it computes a `Loan`, which is a data structure that
|
||||||
records (1) the value being borrowed, (2) the mutability and scope of
|
records (1) the value being borrowed, (2) the mutability and scope of
|
||||||
@ -108,29 +108,29 @@ struct defined in `middle::borrowck`. Formally, we define `LOAN` as
|
|||||||
follows:
|
follows:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
LOAN = (LV, LT, MQ, RESTRICTION*)
|
LOAN = (P, LT, MQ, RESTRICTION*)
|
||||||
RESTRICTION = (LV, ACTION*)
|
RESTRICTION = (P, ACTION*)
|
||||||
ACTION = MUTATE | CLAIM | FREEZE
|
ACTION = MUTATE | CLAIM | FREEZE
|
||||||
```
|
```
|
||||||
|
|
||||||
Here the `LOAN` tuple defines the lvalue `LV` being borrowed; the
|
Here the `LOAN` tuple defines the place `P` being borrowed; the
|
||||||
lifetime `LT` of that borrow; the mutability `MQ` of the borrow; and a
|
lifetime `LT` of that borrow; the mutability `MQ` of the borrow; and a
|
||||||
list of restrictions. The restrictions indicate actions which, if
|
list of restrictions. The restrictions indicate actions which, if
|
||||||
taken, could invalidate the loan and lead to type safety violations.
|
taken, could invalidate the loan and lead to type safety violations.
|
||||||
|
|
||||||
Each `RESTRICTION` is a pair of a restrictive lvalue `LV` (which will
|
Each `RESTRICTION` is a pair of a restrictive place `P` (which will
|
||||||
either be the path that was borrowed or some prefix of the path that
|
either be the path that was borrowed or some prefix of the path that
|
||||||
was borrowed) and a set of restricted actions. There are three kinds
|
was borrowed) and a set of restricted actions. There are three kinds
|
||||||
of actions that may be restricted for the path `LV`:
|
of actions that may be restricted for the path `P`:
|
||||||
|
|
||||||
- `MUTATE` means that `LV` cannot be assigned to;
|
- `MUTATE` means that `P` cannot be assigned to;
|
||||||
- `CLAIM` means that the `LV` cannot be borrowed mutably;
|
- `CLAIM` means that the `P` cannot be borrowed mutably;
|
||||||
- `FREEZE` means that the `LV` cannot be borrowed immutably;
|
- `FREEZE` means that the `P` cannot be borrowed immutably;
|
||||||
|
|
||||||
Finally, it is never possible to move from an lvalue that appears in a
|
Finally, it is never possible to move from a place that appears in a
|
||||||
restriction. This implies that the "empty restriction" `(LV, [])`,
|
restriction. This implies that the "empty restriction" `(P, [])`,
|
||||||
which contains an empty set of actions, still has a purpose---it
|
which contains an empty set of actions, still has a purpose---it
|
||||||
prevents moves from `LV`. I chose not to make `MOVE` a fourth kind of
|
prevents moves from `P`. I chose not to make `MOVE` a fourth kind of
|
||||||
action because that would imply that sometimes moves are permitted
|
action because that would imply that sometimes moves are permitted
|
||||||
from restricted values, which is not the case.
|
from restricted values, which is not the case.
|
||||||
|
|
||||||
@ -239,22 +239,22 @@ live. (This is done via restrictions, read on.)
|
|||||||
|
|
||||||
We start with the `gather_loans` pass, which walks the AST looking for
|
We start with the `gather_loans` pass, which walks the AST looking for
|
||||||
borrows. For each borrow, there are three bits of information: the
|
borrows. For each borrow, there are three bits of information: the
|
||||||
lvalue `LV` being borrowed and the mutability `MQ` and lifetime `LT`
|
place `P` being borrowed and the mutability `MQ` and lifetime `LT`
|
||||||
of the resulting pointer. Given those, `gather_loans` applies four
|
of the resulting pointer. Given those, `gather_loans` applies four
|
||||||
validity tests:
|
validity tests:
|
||||||
|
|
||||||
1. `MUTABILITY(LV, MQ)`: The mutability of the reference is
|
1. `MUTABILITY(P, MQ)`: The mutability of the reference is
|
||||||
compatible with the mutability of `LV` (i.e., not borrowing immutable
|
compatible with the mutability of `P` (i.e., not borrowing immutable
|
||||||
data as mutable).
|
data as mutable).
|
||||||
|
|
||||||
2. `ALIASABLE(LV, MQ)`: The aliasability of the reference is
|
2. `ALIASABLE(P, MQ)`: The aliasability of the reference is
|
||||||
compatible with the aliasability of `LV`. The goal is to prevent
|
compatible with the aliasability of `P`. The goal is to prevent
|
||||||
`&mut` borrows of aliasability data.
|
`&mut` borrows of aliasability data.
|
||||||
|
|
||||||
3. `LIFETIME(LV, LT, MQ)`: The lifetime of the borrow does not exceed
|
3. `LIFETIME(P, LT, MQ)`: The lifetime of the borrow does not exceed
|
||||||
the lifetime of the value being borrowed.
|
the lifetime of the value being borrowed.
|
||||||
|
|
||||||
4. `RESTRICTIONS(LV, LT, ACTIONS) = RS`: This pass checks and computes the
|
4. `RESTRICTIONS(P, LT, ACTIONS) = RS`: This pass checks and computes the
|
||||||
restrictions to maintain memory safety. These are the restrictions
|
restrictions to maintain memory safety. These are the restrictions
|
||||||
that will go into the final loan. We'll discuss in more detail below.
|
that will go into the final loan. We'll discuss in more detail below.
|
||||||
|
|
||||||
@ -263,7 +263,7 @@ that will go into the final loan. We'll discuss in more detail below.
|
|||||||
Checking mutability is fairly straightforward. We just want to prevent
|
Checking mutability is fairly straightforward. We just want to prevent
|
||||||
immutable data from being borrowed as mutable. Note that it is ok to borrow
|
immutable data from being borrowed as mutable. Note that it is ok to borrow
|
||||||
mutable data as immutable, since that is simply a freeze. The judgement
|
mutable data as immutable, since that is simply a freeze. The judgement
|
||||||
`MUTABILITY(LV, MQ)` means the mutability of `LV` is compatible with a borrow
|
`MUTABILITY(P, MQ)` means the mutability of `P` is compatible with a borrow
|
||||||
of mutability `MQ`. The Rust code corresponding to this predicate is the
|
of mutability `MQ`. The Rust code corresponding to this predicate is the
|
||||||
function `check_mutability` in `middle::borrowck::gather_loans`.
|
function `check_mutability` in `middle::borrowck::gather_loans`.
|
||||||
|
|
||||||
@ -288,15 +288,15 @@ MUTABILITY(X, imm) // M-Var-Imm
|
|||||||
|
|
||||||
Fields and boxes inherit their mutability from
|
Fields and boxes inherit their mutability from
|
||||||
their base expressions, so both of their rules basically
|
their base expressions, so both of their rules basically
|
||||||
delegate the check to the base expression `LV`:
|
delegate the check to the base expression `P`:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
MUTABILITY(LV.f, MQ) // M-Field
|
MUTABILITY(P.f, MQ) // M-Field
|
||||||
MUTABILITY(LV, MQ)
|
MUTABILITY(P, MQ)
|
||||||
|
|
||||||
MUTABILITY(*LV, MQ) // M-Deref-Unique
|
MUTABILITY(*P, MQ) // M-Deref-Unique
|
||||||
TYPE(LV) = Box<Ty>
|
TYPE(P) = Box<Ty>
|
||||||
MUTABILITY(LV, MQ)
|
MUTABILITY(P, MQ)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Checking mutability of immutable pointer types
|
### Checking mutability of immutable pointer types
|
||||||
@ -305,8 +305,8 @@ Immutable pointer types like `&T` can only
|
|||||||
be borrowed if MQ is immutable:
|
be borrowed if MQ is immutable:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
MUTABILITY(*LV, imm) // M-Deref-Borrowed-Imm
|
MUTABILITY(*P, imm) // M-Deref-Borrowed-Imm
|
||||||
TYPE(LV) = &Ty
|
TYPE(P) = &Ty
|
||||||
```
|
```
|
||||||
|
|
||||||
### Checking mutability of mutable pointer types
|
### Checking mutability of mutable pointer types
|
||||||
@ -314,15 +314,15 @@ MUTABILITY(*LV, imm) // M-Deref-Borrowed-Imm
|
|||||||
`&mut T` can be frozen, so it is acceptable to borrow it as either imm or mut:
|
`&mut T` can be frozen, so it is acceptable to borrow it as either imm or mut:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
MUTABILITY(*LV, MQ) // M-Deref-Borrowed-Mut
|
MUTABILITY(*P, MQ) // M-Deref-Borrowed-Mut
|
||||||
TYPE(LV) = &mut Ty
|
TYPE(P) = &mut Ty
|
||||||
```
|
```
|
||||||
|
|
||||||
## Checking aliasability
|
## Checking aliasability
|
||||||
|
|
||||||
The goal of the aliasability check is to ensure that we never permit `&mut`
|
The goal of the aliasability check is to ensure that we never permit `&mut`
|
||||||
borrows of aliasable data. The judgement `ALIASABLE(LV, MQ)` means the
|
borrows of aliasable data. The judgement `ALIASABLE(P, MQ)` means the
|
||||||
aliasability of `LV` is compatible with a borrow of mutability `MQ`. The Rust
|
aliasability of `P` is compatible with a borrow of mutability `MQ`. The Rust
|
||||||
code corresponding to this predicate is the function `check_aliasability()` in
|
code corresponding to this predicate is the function `check_aliasability()` in
|
||||||
`middle::borrowck::gather_loans`.
|
`middle::borrowck::gather_loans`.
|
||||||
|
|
||||||
@ -340,11 +340,11 @@ the stack frame.
|
|||||||
Owned content is aliasable if it is found in an aliasable location:
|
Owned content is aliasable if it is found in an aliasable location:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
ALIASABLE(LV.f, MQ) // M-Field
|
ALIASABLE(P.f, MQ) // M-Field
|
||||||
ALIASABLE(LV, MQ)
|
ALIASABLE(P, MQ)
|
||||||
|
|
||||||
ALIASABLE(*LV, MQ) // M-Deref-Unique
|
ALIASABLE(*P, MQ) // M-Deref-Unique
|
||||||
ALIASABLE(LV, MQ)
|
ALIASABLE(P, MQ)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Checking aliasability of immutable pointer types
|
### Checking aliasability of immutable pointer types
|
||||||
@ -353,8 +353,8 @@ Immutable pointer types like `&T` are aliasable, and hence can only be
|
|||||||
borrowed immutably:
|
borrowed immutably:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
ALIASABLE(*LV, imm) // M-Deref-Borrowed-Imm
|
ALIASABLE(*P, imm) // M-Deref-Borrowed-Imm
|
||||||
TYPE(LV) = &Ty
|
TYPE(P) = &Ty
|
||||||
```
|
```
|
||||||
|
|
||||||
### Checking aliasability of mutable pointer types
|
### Checking aliasability of mutable pointer types
|
||||||
@ -362,16 +362,16 @@ ALIASABLE(*LV, imm) // M-Deref-Borrowed-Imm
|
|||||||
`&mut T` can be frozen, so it is acceptable to borrow it as either imm or mut:
|
`&mut T` can be frozen, so it is acceptable to borrow it as either imm or mut:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
ALIASABLE(*LV, MQ) // M-Deref-Borrowed-Mut
|
ALIASABLE(*P, MQ) // M-Deref-Borrowed-Mut
|
||||||
TYPE(LV) = &mut Ty
|
TYPE(P) = &mut Ty
|
||||||
```
|
```
|
||||||
|
|
||||||
## Checking lifetime
|
## Checking lifetime
|
||||||
|
|
||||||
These rules aim to ensure that no data is borrowed for a scope that exceeds
|
These rules aim to ensure that no data is borrowed for a scope that exceeds
|
||||||
its lifetime. These two computations wind up being intimately related.
|
its lifetime. These two computations wind up being intimately related.
|
||||||
Formally, we define a predicate `LIFETIME(LV, LT, MQ)`, which states that
|
Formally, we define a predicate `LIFETIME(P, LT, MQ)`, which states that
|
||||||
"the lvalue `LV` can be safely borrowed for the lifetime `LT` with mutability
|
"the place `P` can be safely borrowed for the lifetime `LT` with mutability
|
||||||
`MQ`". The Rust code corresponding to this predicate is the module
|
`MQ`". The Rust code corresponding to this predicate is the module
|
||||||
`middle::borrowck::gather_loans::lifetime`.
|
`middle::borrowck::gather_loans::lifetime`.
|
||||||
|
|
||||||
@ -391,12 +391,12 @@ The lifetime of a field or box is the same as the lifetime
|
|||||||
of its owner:
|
of its owner:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
LIFETIME(LV.f, LT, MQ) // L-Field
|
LIFETIME(P.f, LT, MQ) // L-Field
|
||||||
LIFETIME(LV, LT, MQ)
|
LIFETIME(P, LT, MQ)
|
||||||
|
|
||||||
LIFETIME(*LV, LT, MQ) // L-Deref-Send
|
LIFETIME(*P, LT, MQ) // L-Deref-Send
|
||||||
TYPE(LV) = Box<Ty>
|
TYPE(P) = Box<Ty>
|
||||||
LIFETIME(LV, LT, MQ)
|
LIFETIME(P, LT, MQ)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Checking lifetime for derefs of references
|
### Checking lifetime for derefs of references
|
||||||
@ -408,8 +408,8 @@ of the borrow is shorter than the lifetime `LT'` of the pointer
|
|||||||
itself:
|
itself:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
LIFETIME(*LV, LT, MQ) // L-Deref-Borrowed
|
LIFETIME(*P, LT, MQ) // L-Deref-Borrowed
|
||||||
TYPE(LV) = <' Ty OR <' mut Ty
|
TYPE(P) = <' Ty OR <' mut Ty
|
||||||
LT <= LT'
|
LT <= LT'
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -417,17 +417,17 @@ LIFETIME(*LV, LT, MQ) // L-Deref-Borrowed
|
|||||||
|
|
||||||
The final rules govern the computation of *restrictions*, meaning that
|
The final rules govern the computation of *restrictions*, meaning that
|
||||||
we compute the set of actions that will be illegal for the life of the
|
we compute the set of actions that will be illegal for the life of the
|
||||||
loan. The predicate is written `RESTRICTIONS(LV, LT, ACTIONS) =
|
loan. The predicate is written `RESTRICTIONS(P, LT, ACTIONS) =
|
||||||
RESTRICTION*`, which can be read "in order to prevent `ACTIONS` from
|
RESTRICTION*`, which can be read "in order to prevent `ACTIONS` from
|
||||||
occurring on `LV`, the restrictions `RESTRICTION*` must be respected
|
occurring on `P`, the restrictions `RESTRICTION*` must be respected
|
||||||
for the lifetime of the loan".
|
for the lifetime of the loan".
|
||||||
|
|
||||||
Note that there is an initial set of restrictions: these restrictions
|
Note that there is an initial set of restrictions: these restrictions
|
||||||
are computed based on the kind of borrow:
|
are computed based on the kind of borrow:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
&mut LV => RESTRICTIONS(LV, LT, MUTATE|CLAIM|FREEZE)
|
&mut P => RESTRICTIONS(P, LT, MUTATE|CLAIM|FREEZE)
|
||||||
&LV => RESTRICTIONS(LV, LT, MUTATE|CLAIM)
|
&P => RESTRICTIONS(P, LT, MUTATE|CLAIM)
|
||||||
```
|
```
|
||||||
|
|
||||||
The reasoning here is that a mutable borrow must be the only writer,
|
The reasoning here is that a mutable borrow must be the only writer,
|
||||||
@ -451,8 +451,8 @@ Restricting a field is the same as restricting the owner of that
|
|||||||
field:
|
field:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
RESTRICTIONS(LV.f, LT, ACTIONS) = RS, (LV.f, ACTIONS) // R-Field
|
RESTRICTIONS(P.f, LT, ACTIONS) = RS, (P.f, ACTIONS) // R-Field
|
||||||
RESTRICTIONS(LV, LT, ACTIONS) = RS
|
RESTRICTIONS(P, LT, ACTIONS) = RS
|
||||||
```
|
```
|
||||||
|
|
||||||
The reasoning here is as follows. If the field must not be mutated,
|
The reasoning here is as follows. If the field must not be mutated,
|
||||||
@ -467,16 +467,16 @@ origin of inherited mutability.
|
|||||||
Because the mutability of owned referents is inherited, restricting an
|
Because the mutability of owned referents is inherited, restricting an
|
||||||
owned referent is similar to restricting a field, in that it implies
|
owned referent is similar to restricting a field, in that it implies
|
||||||
restrictions on the pointer. However, boxes have an important
|
restrictions on the pointer. However, boxes have an important
|
||||||
twist: if the owner `LV` is mutated, that causes the owned referent
|
twist: if the owner `P` is mutated, that causes the owned referent
|
||||||
`*LV` to be freed! So whenever an owned referent `*LV` is borrowed, we
|
`*P` to be freed! So whenever an owned referent `*P` is borrowed, we
|
||||||
must prevent the box `LV` from being mutated, which means
|
must prevent the box `P` from being mutated, which means
|
||||||
that we always add `MUTATE` and `CLAIM` to the restriction set imposed
|
that we always add `MUTATE` and `CLAIM` to the restriction set imposed
|
||||||
on `LV`:
|
on `P`:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
RESTRICTIONS(*LV, LT, ACTIONS) = RS, (*LV, ACTIONS) // R-Deref-Send-Pointer
|
RESTRICTIONS(*P, LT, ACTIONS) = RS, (*P, ACTIONS) // R-Deref-Send-Pointer
|
||||||
TYPE(LV) = Box<Ty>
|
TYPE(P) = Box<Ty>
|
||||||
RESTRICTIONS(LV, LT, ACTIONS|MUTATE|CLAIM) = RS
|
RESTRICTIONS(P, LT, ACTIONS|MUTATE|CLAIM) = RS
|
||||||
```
|
```
|
||||||
|
|
||||||
### Restrictions for loans of immutable borrowed referents
|
### Restrictions for loans of immutable borrowed referents
|
||||||
@ -484,15 +484,15 @@ RESTRICTIONS(*LV, LT, ACTIONS) = RS, (*LV, ACTIONS) // R-Deref-Send-Pointer
|
|||||||
Immutable borrowed referents are freely aliasable, meaning that
|
Immutable borrowed referents are freely aliasable, meaning that
|
||||||
the compiler does not prevent you from copying the pointer. This
|
the compiler does not prevent you from copying the pointer. This
|
||||||
implies that issuing restrictions is useless. We might prevent the
|
implies that issuing restrictions is useless. We might prevent the
|
||||||
user from acting on `*LV` itself, but there could be another path
|
user from acting on `*P` itself, but there could be another path
|
||||||
`*LV1` that refers to the exact same memory, and we would not be
|
`*P1` that refers to the exact same memory, and we would not be
|
||||||
restricting that path. Therefore, the rule for `&Ty` pointers
|
restricting that path. Therefore, the rule for `&Ty` pointers
|
||||||
always returns an empty set of restrictions, and it only permits
|
always returns an empty set of restrictions, and it only permits
|
||||||
restricting `MUTATE` and `CLAIM` actions:
|
restricting `MUTATE` and `CLAIM` actions:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
RESTRICTIONS(*LV, LT, ACTIONS) = [] // R-Deref-Imm-Borrowed
|
RESTRICTIONS(*P, LT, ACTIONS) = [] // R-Deref-Imm-Borrowed
|
||||||
TYPE(LV) = <' Ty
|
TYPE(P) = <' Ty
|
||||||
LT <= LT' // (1)
|
LT <= LT' // (1)
|
||||||
ACTIONS subset of [MUTATE, CLAIM]
|
ACTIONS subset of [MUTATE, CLAIM]
|
||||||
```
|
```
|
||||||
@ -546,7 +546,7 @@ This function is legal. The reason for this is that the inner pointer
|
|||||||
(`*point : &'b Point`) is enough to guarantee the memory is immutable
|
(`*point : &'b Point`) is enough to guarantee the memory is immutable
|
||||||
and valid for the lifetime `'b`. This is reflected in
|
and valid for the lifetime `'b`. This is reflected in
|
||||||
`RESTRICTIONS()` by the fact that we do not recurse (i.e., we impose
|
`RESTRICTIONS()` by the fact that we do not recurse (i.e., we impose
|
||||||
no restrictions on `LV`, which in this particular case is the pointer
|
no restrictions on `P`, which in this particular case is the pointer
|
||||||
`point : &'a &'b Point`).
|
`point : &'a &'b Point`).
|
||||||
|
|
||||||
#### Why both `LIFETIME()` and `RESTRICTIONS()`?
|
#### Why both `LIFETIME()` and `RESTRICTIONS()`?
|
||||||
@ -612,10 +612,10 @@ while the new claimant is live.
|
|||||||
The rule for mutable borrowed pointers is as follows:
|
The rule for mutable borrowed pointers is as follows:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
RESTRICTIONS(*LV, LT, ACTIONS) = RS, (*LV, ACTIONS) // R-Deref-Mut-Borrowed
|
RESTRICTIONS(*P, LT, ACTIONS) = RS, (*P, ACTIONS) // R-Deref-Mut-Borrowed
|
||||||
TYPE(LV) = <' mut Ty
|
TYPE(P) = <' mut Ty
|
||||||
LT <= LT' // (1)
|
LT <= LT' // (1)
|
||||||
RESTRICTIONS(LV, LT, ACTIONS) = RS // (2)
|
RESTRICTIONS(P, LT, ACTIONS) = RS // (2)
|
||||||
```
|
```
|
||||||
|
|
||||||
Let's examine the two numbered clauses:
|
Let's examine the two numbered clauses:
|
||||||
@ -670,7 +670,7 @@ fn foo(t0: &mut i32) {
|
|||||||
|
|
||||||
Remember that `&mut` pointers are linear, and hence `let t1 = t0` is a
|
Remember that `&mut` pointers are linear, and hence `let t1 = t0` is a
|
||||||
move of `t0` -- or would be, if it were legal. Instead, we get an
|
move of `t0` -- or would be, if it were legal. Instead, we get an
|
||||||
error, because clause (2) imposes restrictions on `LV` (`t0`, here),
|
error, because clause (2) imposes restrictions on `P` (`t0`, here),
|
||||||
and any restrictions on a path make it impossible to move from that
|
and any restrictions on a path make it impossible to move from that
|
||||||
path.
|
path.
|
||||||
|
|
||||||
@ -906,7 +906,7 @@ results of a dataflow computation.
|
|||||||
|
|
||||||
The `MovePath` tree tracks every path that is moved or assigned to.
|
The `MovePath` tree tracks every path that is moved or assigned to.
|
||||||
These paths have the same form as the `LoanPath` data structure, which
|
These paths have the same form as the `LoanPath` data structure, which
|
||||||
in turn is the "real world version of the lvalues `LV` that we
|
in turn is the "real world version of the places `P` that we
|
||||||
introduced earlier. The difference between a `MovePath` and a `LoanPath`
|
introduced earlier. The difference between a `MovePath` and a `LoanPath`
|
||||||
is that move paths are:
|
is that move paths are:
|
||||||
|
|
||||||
@ -1132,7 +1132,7 @@ is implied by the relevant moves.
|
|||||||
While writing up these docs, I encountered some rules I believe to be
|
While writing up these docs, I encountered some rules I believe to be
|
||||||
stricter than necessary:
|
stricter than necessary:
|
||||||
|
|
||||||
- I think restricting the `&mut` LV against moves and `ALIAS` is sufficient,
|
- I think restricting the `&mut` P against moves and `ALIAS` is sufficient,
|
||||||
`MUTATE` and `CLAIM` are overkill. `MUTATE` was necessary when swap was
|
`MUTATE` and `CLAIM` are overkill. `MUTATE` was necessary when swap was
|
||||||
a built-in operator, but as it is not, it is implied by `CLAIM`,
|
a built-in operator, but as it is not, it is implied by `CLAIM`,
|
||||||
and `CLAIM` is implied by `ALIAS`. The only net effect of this is an
|
and `CLAIM` is implied by `ALIAS`. The only net effect of this is an
|
||||||
|
@ -104,7 +104,7 @@ impl<'a, 'tcx> GuaranteeLifetimeContext<'a, 'tcx> {
|
|||||||
|
|
||||||
fn scope(&self, cmt: &mc::cmt<'tcx>) -> ty::Region<'tcx> {
|
fn scope(&self, cmt: &mc::cmt<'tcx>) -> ty::Region<'tcx> {
|
||||||
//! Returns the maximal region scope for the which the
|
//! Returns the maximal region scope for the which the
|
||||||
//! lvalue `cmt` is guaranteed to be valid without any
|
//! place `cmt` is guaranteed to be valid without any
|
||||||
//! rooting etc, and presuming `cmt` is not mutated.
|
//! rooting etc, and presuming `cmt` is not mutated.
|
||||||
|
|
||||||
match cmt.cat {
|
match cmt.cat {
|
||||||
|
@ -170,7 +170,7 @@ fn build_borrowck_dataflow_data<'a, 'c, 'tcx, F>(this: &mut BorrowckCtxt<'a, 'tc
|
|||||||
if !force_analysis && move_data.is_empty() && all_loans.is_empty() {
|
if !force_analysis && move_data.is_empty() && all_loans.is_empty() {
|
||||||
// large arrays of data inserted as constants can take a lot of
|
// large arrays of data inserted as constants can take a lot of
|
||||||
// time and memory to borrow-check - see issue #36799. However,
|
// time and memory to borrow-check - see issue #36799. However,
|
||||||
// they don't have lvalues, so no borrow-check is actually needed.
|
// they don't have places, so no borrow-check is actually needed.
|
||||||
// Recognize that case and skip borrow-checking.
|
// Recognize that case and skip borrow-checking.
|
||||||
debug!("skipping loan propagation for {:?} because of no loans", body_id);
|
debug!("skipping loan propagation for {:?} because of no loans", body_id);
|
||||||
return None;
|
return None;
|
||||||
@ -384,9 +384,9 @@ impl ToInteriorKind for mc::InteriorKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This can be:
|
// This can be:
|
||||||
// - a pointer dereference (`*LV` in README.md)
|
// - a pointer dereference (`*P` in README.md)
|
||||||
// - a field reference, with an optional definition of the containing
|
// - a field reference, with an optional definition of the containing
|
||||||
// enum variant (`LV.f` in README.md)
|
// enum variant (`P.f` in README.md)
|
||||||
// `DefId` is present when the field is part of struct that is in
|
// `DefId` is present when the field is part of struct that is in
|
||||||
// a variant of an enum. For instance in:
|
// a variant of an enum. For instance in:
|
||||||
// `enum E { X { foo: u32 }, Y { foo: u32 }}`
|
// `enum E { X { foo: u32 }, Y { foo: u32 }}`
|
||||||
|
@ -153,7 +153,7 @@ pub struct Assignment {
|
|||||||
/// span of node where assignment occurs
|
/// span of node where assignment occurs
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
|
|
||||||
/// id for l-value expression on lhs of assignment
|
/// id for place expression on lhs of assignment
|
||||||
pub assignee_id: hir::ItemLocalId,
|
pub assignee_id: hir::ItemLocalId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,8 +15,8 @@
|
|||||||
|
|
||||||
use rustc::mir::{BasicBlock, Location};
|
use rustc::mir::{BasicBlock, Location};
|
||||||
|
|
||||||
use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals};
|
use dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
|
||||||
use dataflow::{EverInitializedLvals, MovingOutStatements};
|
use dataflow::{EverInitializedPlaces, MovingOutStatements};
|
||||||
use dataflow::{ActiveBorrows, FlowAtLocation, FlowsAtLocation};
|
use dataflow::{ActiveBorrows, FlowAtLocation, FlowsAtLocation};
|
||||||
use dataflow::move_paths::HasMoveData;
|
use dataflow::move_paths::HasMoveData;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
@ -24,19 +24,19 @@ use std::fmt;
|
|||||||
// (forced to be `pub` due to its use as an associated type below.)
|
// (forced to be `pub` due to its use as an associated type below.)
|
||||||
pub(crate) struct Flows<'b, 'gcx: 'tcx, 'tcx: 'b> {
|
pub(crate) struct Flows<'b, 'gcx: 'tcx, 'tcx: 'b> {
|
||||||
pub borrows: FlowAtLocation<ActiveBorrows<'b, 'gcx, 'tcx>>,
|
pub borrows: FlowAtLocation<ActiveBorrows<'b, 'gcx, 'tcx>>,
|
||||||
pub inits: FlowAtLocation<MaybeInitializedLvals<'b, 'gcx, 'tcx>>,
|
pub inits: FlowAtLocation<MaybeInitializedPlaces<'b, 'gcx, 'tcx>>,
|
||||||
pub uninits: FlowAtLocation<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>,
|
pub uninits: FlowAtLocation<MaybeUninitializedPlaces<'b, 'gcx, 'tcx>>,
|
||||||
pub move_outs: FlowAtLocation<MovingOutStatements<'b, 'gcx, 'tcx>>,
|
pub move_outs: FlowAtLocation<MovingOutStatements<'b, 'gcx, 'tcx>>,
|
||||||
pub ever_inits: FlowAtLocation<EverInitializedLvals<'b, 'gcx, 'tcx>>,
|
pub ever_inits: FlowAtLocation<EverInitializedPlaces<'b, 'gcx, 'tcx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'b, 'gcx, 'tcx> Flows<'b, 'gcx, 'tcx> {
|
impl<'b, 'gcx, 'tcx> Flows<'b, 'gcx, 'tcx> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
borrows: FlowAtLocation<ActiveBorrows<'b, 'gcx, 'tcx>>,
|
borrows: FlowAtLocation<ActiveBorrows<'b, 'gcx, 'tcx>>,
|
||||||
inits: FlowAtLocation<MaybeInitializedLvals<'b, 'gcx, 'tcx>>,
|
inits: FlowAtLocation<MaybeInitializedPlaces<'b, 'gcx, 'tcx>>,
|
||||||
uninits: FlowAtLocation<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>,
|
uninits: FlowAtLocation<MaybeUninitializedPlaces<'b, 'gcx, 'tcx>>,
|
||||||
move_outs: FlowAtLocation<MovingOutStatements<'b, 'gcx, 'tcx>>,
|
move_outs: FlowAtLocation<MovingOutStatements<'b, 'gcx, 'tcx>>,
|
||||||
ever_inits: FlowAtLocation<EverInitializedLvals<'b, 'gcx, 'tcx>>,
|
ever_inits: FlowAtLocation<EverInitializedPlaces<'b, 'gcx, 'tcx>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Flows {
|
Flows {
|
||||||
borrows,
|
borrows,
|
||||||
|
@ -35,8 +35,8 @@ use dataflow::{do_dataflow, DebugFormatted};
|
|||||||
use dataflow::FlowAtLocation;
|
use dataflow::FlowAtLocation;
|
||||||
use dataflow::MoveDataParamEnv;
|
use dataflow::MoveDataParamEnv;
|
||||||
use dataflow::{DataflowAnalysis, DataflowResultsConsumer};
|
use dataflow::{DataflowAnalysis, DataflowResultsConsumer};
|
||||||
use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals};
|
use dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
|
||||||
use dataflow::{EverInitializedLvals, MovingOutStatements};
|
use dataflow::{EverInitializedPlaces, MovingOutStatements};
|
||||||
use dataflow::{BorrowData, Borrows, ReserveOrActivateIndex};
|
use dataflow::{BorrowData, Borrows, ReserveOrActivateIndex};
|
||||||
use dataflow::{ActiveBorrows, Reservations};
|
use dataflow::{ActiveBorrows, Reservations};
|
||||||
use dataflow::indexes::BorrowIndex;
|
use dataflow::indexes::BorrowIndex;
|
||||||
@ -160,7 +160,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
|
|||||||
id,
|
id,
|
||||||
&attributes,
|
&attributes,
|
||||||
&dead_unwinds,
|
&dead_unwinds,
|
||||||
MaybeInitializedLvals::new(tcx, mir, &mdpe),
|
MaybeInitializedPlaces::new(tcx, mir, &mdpe),
|
||||||
|bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]),
|
|bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]),
|
||||||
));
|
));
|
||||||
let flow_uninits = FlowAtLocation::new(do_dataflow(
|
let flow_uninits = FlowAtLocation::new(do_dataflow(
|
||||||
@ -169,7 +169,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
|
|||||||
id,
|
id,
|
||||||
&attributes,
|
&attributes,
|
||||||
&dead_unwinds,
|
&dead_unwinds,
|
||||||
MaybeUninitializedLvals::new(tcx, mir, &mdpe),
|
MaybeUninitializedPlaces::new(tcx, mir, &mdpe),
|
||||||
|bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]),
|
|bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]),
|
||||||
));
|
));
|
||||||
let flow_move_outs = FlowAtLocation::new(do_dataflow(
|
let flow_move_outs = FlowAtLocation::new(do_dataflow(
|
||||||
@ -187,7 +187,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
|
|||||||
id,
|
id,
|
||||||
&attributes,
|
&attributes,
|
||||||
&dead_unwinds,
|
&dead_unwinds,
|
||||||
EverInitializedLvals::new(tcx, mir, &mdpe),
|
EverInitializedPlaces::new(tcx, mir, &mdpe),
|
||||||
|bd, i| DebugFormatted::new(&bd.move_data().inits[i]),
|
|bd, i| DebugFormatted::new(&bd.move_data().inits[i]),
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -607,7 +607,7 @@ enum ArtificialField {
|
|||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
enum ShallowOrDeep {
|
enum ShallowOrDeep {
|
||||||
/// From the RFC: "A *shallow* access means that the immediate
|
/// From the RFC: "A *shallow* access means that the immediate
|
||||||
/// fields reached at LV are accessed, but references or pointers
|
/// fields reached at P are accessed, but references or pointers
|
||||||
/// found within are not dereferenced. Right now, the only access
|
/// found within are not dereferenced. Right now, the only access
|
||||||
/// that is shallow is an assignment like `x = ...;`, which would
|
/// that is shallow is an assignment like `x = ...;`, which would
|
||||||
/// be a *shallow write* of `x`."
|
/// be a *shallow write* of `x`."
|
||||||
|
@ -19,7 +19,7 @@ use std::io;
|
|||||||
use transform::MirSource;
|
use transform::MirSource;
|
||||||
use util::liveness::{LivenessResults, LocalSet};
|
use util::liveness::{LivenessResults, LocalSet};
|
||||||
use dataflow::FlowAtLocation;
|
use dataflow::FlowAtLocation;
|
||||||
use dataflow::MaybeInitializedLvals;
|
use dataflow::MaybeInitializedPlaces;
|
||||||
use dataflow::move_paths::MoveData;
|
use dataflow::move_paths::MoveData;
|
||||||
|
|
||||||
use util as mir_util;
|
use util as mir_util;
|
||||||
@ -71,7 +71,7 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
|
|||||||
universal_regions: UniversalRegions<'tcx>,
|
universal_regions: UniversalRegions<'tcx>,
|
||||||
mir: &Mir<'tcx>,
|
mir: &Mir<'tcx>,
|
||||||
param_env: ty::ParamEnv<'gcx>,
|
param_env: ty::ParamEnv<'gcx>,
|
||||||
flow_inits: &mut FlowAtLocation<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>,
|
flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'cx, 'gcx, 'tcx>>,
|
||||||
move_data: &MoveData<'tcx>,
|
move_data: &MoveData<'tcx>,
|
||||||
) -> (
|
) -> (
|
||||||
RegionInferenceContext<'tcx>,
|
RegionInferenceContext<'tcx>,
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
use dataflow::{FlowAtLocation, FlowsAtLocation};
|
use dataflow::{FlowAtLocation, FlowsAtLocation};
|
||||||
use borrow_check::nll::region_infer::Cause;
|
use borrow_check::nll::region_infer::Cause;
|
||||||
use dataflow::MaybeInitializedLvals;
|
use dataflow::MaybeInitializedPlaces;
|
||||||
use dataflow::move_paths::{HasMoveData, MoveData};
|
use dataflow::move_paths::{HasMoveData, MoveData};
|
||||||
use rustc::mir::{BasicBlock, Location, Mir};
|
use rustc::mir::{BasicBlock, Location, Mir};
|
||||||
use rustc::mir::Local;
|
use rustc::mir::Local;
|
||||||
@ -34,7 +34,7 @@ pub(super) fn generate<'gcx, 'tcx>(
|
|||||||
cx: &mut TypeChecker<'_, 'gcx, 'tcx>,
|
cx: &mut TypeChecker<'_, 'gcx, 'tcx>,
|
||||||
mir: &Mir<'tcx>,
|
mir: &Mir<'tcx>,
|
||||||
liveness: &LivenessResults,
|
liveness: &LivenessResults,
|
||||||
flow_inits: &mut FlowAtLocation<MaybeInitializedLvals<'_, 'gcx, 'tcx>>,
|
flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
|
||||||
move_data: &MoveData<'tcx>,
|
move_data: &MoveData<'tcx>,
|
||||||
) {
|
) {
|
||||||
let tcx = cx.tcx();
|
let tcx = cx.tcx();
|
||||||
@ -63,7 +63,7 @@ where
|
|||||||
tcx: TyCtxt<'typeck, 'gcx, 'tcx>,
|
tcx: TyCtxt<'typeck, 'gcx, 'tcx>,
|
||||||
mir: &'gen Mir<'tcx>,
|
mir: &'gen Mir<'tcx>,
|
||||||
liveness: &'gen LivenessResults,
|
liveness: &'gen LivenessResults,
|
||||||
flow_inits: &'gen mut FlowAtLocation<MaybeInitializedLvals<'flow, 'gcx, 'tcx>>,
|
flow_inits: &'gen mut FlowAtLocation<MaybeInitializedPlaces<'flow, 'gcx, 'tcx>>,
|
||||||
move_data: &'gen MoveData<'tcx>,
|
move_data: &'gen MoveData<'tcx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ use borrow_check::nll::region_infer::Cause;
|
|||||||
use borrow_check::nll::region_infer::ClosureRegionRequirementsExt;
|
use borrow_check::nll::region_infer::ClosureRegionRequirementsExt;
|
||||||
use borrow_check::nll::universal_regions::UniversalRegions;
|
use borrow_check::nll::universal_regions::UniversalRegions;
|
||||||
use dataflow::FlowAtLocation;
|
use dataflow::FlowAtLocation;
|
||||||
use dataflow::MaybeInitializedLvals;
|
use dataflow::MaybeInitializedPlaces;
|
||||||
use dataflow::move_paths::MoveData;
|
use dataflow::move_paths::MoveData;
|
||||||
use rustc::hir::def_id::DefId;
|
use rustc::hir::def_id::DefId;
|
||||||
use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult};
|
use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult};
|
||||||
@ -100,7 +100,7 @@ pub(crate) fn type_check<'gcx, 'tcx>(
|
|||||||
mir_def_id: DefId,
|
mir_def_id: DefId,
|
||||||
universal_regions: &UniversalRegions<'tcx>,
|
universal_regions: &UniversalRegions<'tcx>,
|
||||||
liveness: &LivenessResults,
|
liveness: &LivenessResults,
|
||||||
flow_inits: &mut FlowAtLocation<MaybeInitializedLvals<'_, 'gcx, 'tcx>>,
|
flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
|
||||||
move_data: &MoveData<'tcx>,
|
move_data: &MoveData<'tcx>,
|
||||||
) -> MirTypeckRegionConstraints<'tcx> {
|
) -> MirTypeckRegionConstraints<'tcx> {
|
||||||
let body_id = infcx.tcx.hir.as_local_node_id(mir_def_id).unwrap();
|
let body_id = infcx.tcx.hir.as_local_node_id(mir_def_id).unwrap();
|
||||||
@ -397,7 +397,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
|
|||||||
let base_ty = base.to_ty(tcx);
|
let base_ty = base.to_ty(tcx);
|
||||||
match *pi {
|
match *pi {
|
||||||
ProjectionElem::Deref => {
|
ProjectionElem::Deref => {
|
||||||
let deref_ty = base_ty.builtin_deref(true, ty::LvaluePreference::NoPreference);
|
let deref_ty = base_ty.builtin_deref(true);
|
||||||
PlaceTy::Ty {
|
PlaceTy::Ty {
|
||||||
ty: deref_ty.map(|t| t.ty).unwrap_or_else(|| {
|
ty: deref_ty.map(|t| t.ty).unwrap_or_else(|| {
|
||||||
span_mirbug_and_err!(self, place, "deref of non-pointer {:?}", base_ty)
|
span_mirbug_and_err!(self, place, "deref of non-pointer {:?}", base_ty)
|
||||||
|
@ -575,10 +575,10 @@ impl<'a, 'b, 'tcx> FindPlaceUses<'a, 'b, 'tcx> {
|
|||||||
/// has a reservation at the time).
|
/// has a reservation at the time).
|
||||||
fn is_potential_use(context: PlaceContext) -> bool {
|
fn is_potential_use(context: PlaceContext) -> bool {
|
||||||
match context {
|
match context {
|
||||||
// storage effects on an place do not activate it
|
// storage effects on a place do not activate it
|
||||||
PlaceContext::StorageLive | PlaceContext::StorageDead => false,
|
PlaceContext::StorageLive | PlaceContext::StorageDead => false,
|
||||||
|
|
||||||
// validation effects do not activate an place
|
// validation effects do not activate a place
|
||||||
//
|
//
|
||||||
// FIXME: Should they? Is it just another read? Or can we
|
// FIXME: Should they? Is it just another read? Or can we
|
||||||
// guarantee it won't dereference the stored address? How
|
// guarantee it won't dereference the stored address? How
|
||||||
@ -589,11 +589,11 @@ impl<'a, 'b, 'tcx> FindPlaceUses<'a, 'b, 'tcx> {
|
|||||||
// AsmOutput existed, but it's not necessarily a pure overwrite.
|
// AsmOutput existed, but it's not necessarily a pure overwrite.
|
||||||
// so it's possible this should activate the place.
|
// so it's possible this should activate the place.
|
||||||
PlaceContext::AsmOutput |
|
PlaceContext::AsmOutput |
|
||||||
// pure overwrites of an place do not activate it. (note
|
// pure overwrites of a place do not activate it. (note
|
||||||
// PlaceContext::Call is solely about dest place)
|
// PlaceContext::Call is solely about dest place)
|
||||||
PlaceContext::Store | PlaceContext::Call => false,
|
PlaceContext::Store | PlaceContext::Call => false,
|
||||||
|
|
||||||
// reads of an place *do* activate it
|
// reads of a place *do* activate it
|
||||||
PlaceContext::Move |
|
PlaceContext::Move |
|
||||||
PlaceContext::Copy |
|
PlaceContext::Copy |
|
||||||
PlaceContext::Drop |
|
PlaceContext::Drop |
|
||||||
|
@ -36,7 +36,7 @@ pub use self::storage_liveness::*;
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(super) mod borrows;
|
pub(super) mod borrows;
|
||||||
|
|
||||||
/// `MaybeInitializedLvals` tracks all l-values that might be
|
/// `MaybeInitializedPlaces` tracks all places that might be
|
||||||
/// initialized upon reaching a particular point in the control flow
|
/// initialized upon reaching a particular point in the control flow
|
||||||
/// for a function.
|
/// for a function.
|
||||||
///
|
///
|
||||||
@ -63,35 +63,35 @@ pub(super) mod borrows;
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// To determine whether an l-value *must* be initialized at a
|
/// To determine whether a place *must* be initialized at a
|
||||||
/// particular control-flow point, one can take the set-difference
|
/// particular control-flow point, one can take the set-difference
|
||||||
/// between this data and the data from `MaybeUninitializedLvals` at the
|
/// between this data and the data from `MaybeUninitializedPlaces` at the
|
||||||
/// corresponding control-flow point.
|
/// corresponding control-flow point.
|
||||||
///
|
///
|
||||||
/// Similarly, at a given `drop` statement, the set-intersection
|
/// Similarly, at a given `drop` statement, the set-intersection
|
||||||
/// between this data and `MaybeUninitializedLvals` yields the set of
|
/// between this data and `MaybeUninitializedPlaces` yields the set of
|
||||||
/// l-values that would require a dynamic drop-flag at that statement.
|
/// places that would require a dynamic drop-flag at that statement.
|
||||||
pub struct MaybeInitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
pub struct MaybeInitializedPlaces<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
||||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||||
mir: &'a Mir<'tcx>,
|
mir: &'a Mir<'tcx>,
|
||||||
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
|
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx: 'tcx, 'tcx> MaybeInitializedLvals<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx: 'tcx, 'tcx> MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
|
||||||
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||||
mir: &'a Mir<'tcx>,
|
mir: &'a Mir<'tcx>,
|
||||||
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
|
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
|
||||||
-> Self
|
-> Self
|
||||||
{
|
{
|
||||||
MaybeInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe }
|
MaybeInitializedPlaces { tcx: tcx, mir: mir, mdpe: mdpe }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeInitializedLvals<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
|
||||||
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
|
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `MaybeUninitializedLvals` tracks all l-values that might be
|
/// `MaybeUninitializedPlaces` tracks all places that might be
|
||||||
/// uninitialized upon reaching a particular point in the control flow
|
/// uninitialized upon reaching a particular point in the control flow
|
||||||
/// for a function.
|
/// for a function.
|
||||||
///
|
///
|
||||||
@ -118,42 +118,42 @@ impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeInitializedLvals<'a, 'gcx, 'tcx>
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// To determine whether an l-value *must* be uninitialized at a
|
/// To determine whether a place *must* be uninitialized at a
|
||||||
/// particular control-flow point, one can take the set-difference
|
/// particular control-flow point, one can take the set-difference
|
||||||
/// between this data and the data from `MaybeInitializedLvals` at the
|
/// between this data and the data from `MaybeInitializedPlaces` at the
|
||||||
/// corresponding control-flow point.
|
/// corresponding control-flow point.
|
||||||
///
|
///
|
||||||
/// Similarly, at a given `drop` statement, the set-intersection
|
/// Similarly, at a given `drop` statement, the set-intersection
|
||||||
/// between this data and `MaybeInitializedLvals` yields the set of
|
/// between this data and `MaybeInitializedPlaces` yields the set of
|
||||||
/// l-values that would require a dynamic drop-flag at that statement.
|
/// places that would require a dynamic drop-flag at that statement.
|
||||||
pub struct MaybeUninitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
pub struct MaybeUninitializedPlaces<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
||||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||||
mir: &'a Mir<'tcx>,
|
mir: &'a Mir<'tcx>,
|
||||||
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
|
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
|
||||||
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||||
mir: &'a Mir<'tcx>,
|
mir: &'a Mir<'tcx>,
|
||||||
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
|
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
|
||||||
-> Self
|
-> Self
|
||||||
{
|
{
|
||||||
MaybeUninitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe }
|
MaybeUninitializedPlaces { tcx: tcx, mir: mir, mdpe: mdpe }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
|
||||||
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
|
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `DefinitelyInitializedLvals` tracks all l-values that are definitely
|
/// `DefinitelyInitializedPlaces` tracks all places that are definitely
|
||||||
/// initialized upon reaching a particular point in the control flow
|
/// initialized upon reaching a particular point in the control flow
|
||||||
/// for a function.
|
/// for a function.
|
||||||
///
|
///
|
||||||
/// FIXME: Note that once flow-analysis is complete, this should be
|
/// FIXME: Note that once flow-analysis is complete, this should be
|
||||||
/// the set-complement of MaybeUninitializedLvals; thus we can get rid
|
/// the set-complement of MaybeUninitializedPlaces; thus we can get rid
|
||||||
/// of one or the other of these two. I'm inclined to get rid of
|
/// of one or the other of these two. I'm inclined to get rid of
|
||||||
/// MaybeUninitializedLvals, simply because the sets will tend to be
|
/// MaybeUninitializedPlaces, simply because the sets will tend to be
|
||||||
/// smaller in this analysis and thus easier for humans to process
|
/// smaller in this analysis and thus easier for humans to process
|
||||||
/// when debugging.
|
/// when debugging.
|
||||||
///
|
///
|
||||||
@ -180,43 +180,43 @@ impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeUninitializedLvals<'a, 'gcx, 'tc
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// To determine whether an l-value *may* be uninitialized at a
|
/// To determine whether a place *may* be uninitialized at a
|
||||||
/// particular control-flow point, one can take the set-complement
|
/// particular control-flow point, one can take the set-complement
|
||||||
/// of this data.
|
/// of this data.
|
||||||
///
|
///
|
||||||
/// Similarly, at a given `drop` statement, the set-difference between
|
/// Similarly, at a given `drop` statement, the set-difference between
|
||||||
/// this data and `MaybeInitializedLvals` yields the set of l-values
|
/// this data and `MaybeInitializedPlaces` yields the set of places
|
||||||
/// that would require a dynamic drop-flag at that statement.
|
/// that would require a dynamic drop-flag at that statement.
|
||||||
pub struct DefinitelyInitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
pub struct DefinitelyInitializedPlaces<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
||||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||||
mir: &'a Mir<'tcx>,
|
mir: &'a Mir<'tcx>,
|
||||||
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
|
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx: 'a> DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx: 'a> DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
|
||||||
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||||
mir: &'a Mir<'tcx>,
|
mir: &'a Mir<'tcx>,
|
||||||
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
|
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
|
||||||
-> Self
|
-> Self
|
||||||
{
|
{
|
||||||
DefinitelyInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe }
|
DefinitelyInitializedPlaces { tcx: tcx, mir: mir, mdpe: mdpe }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx: 'a> HasMoveData<'tcx> for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx: 'a> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
|
||||||
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
|
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `MovingOutStatements` tracks the statements that perform moves out
|
/// `MovingOutStatements` tracks the statements that perform moves out
|
||||||
/// of particular l-values. More precisely, it tracks whether the
|
/// of particular places. More precisely, it tracks whether the
|
||||||
/// *effect* of such moves (namely, the uninitialization of the
|
/// *effect* of such moves (namely, the uninitialization of the
|
||||||
/// l-value in question) can reach some point in the control-flow of
|
/// place in question) can reach some point in the control-flow of
|
||||||
/// the function, or if that effect is "killed" by some intervening
|
/// the function, or if that effect is "killed" by some intervening
|
||||||
/// operation reinitializing that l-value.
|
/// operation reinitializing that place.
|
||||||
///
|
///
|
||||||
/// The resulting dataflow is a more enriched version of
|
/// The resulting dataflow is a more enriched version of
|
||||||
/// `MaybeUninitializedLvals`. Both structures on their own only tell
|
/// `MaybeUninitializedPlaces`. Both structures on their own only tell
|
||||||
/// you if an l-value *might* be uninitialized at a given point in the
|
/// you if a place *might* be uninitialized at a given point in the
|
||||||
/// control flow. But `MovingOutStatements` also includes the added
|
/// control flow. But `MovingOutStatements` also includes the added
|
||||||
/// data of *which* particular statement causing the deinitialization
|
/// data of *which* particular statement causing the deinitialization
|
||||||
/// that the borrow checker's error message may need to report.
|
/// that the borrow checker's error message may need to report.
|
||||||
@ -241,7 +241,7 @@ impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MovingOutStatements<'a, 'gcx, 'tcx> {
|
|||||||
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
|
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `EverInitializedLvals` tracks all l-values that might have ever been
|
/// `EverInitializedPlaces` tracks all places that might have ever been
|
||||||
/// initialized upon reaching a particular point in the control flow
|
/// initialized upon reaching a particular point in the control flow
|
||||||
/// for a function, without an intervening `Storage Dead`.
|
/// for a function, without an intervening `Storage Dead`.
|
||||||
///
|
///
|
||||||
@ -270,28 +270,28 @@ impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MovingOutStatements<'a, 'gcx, 'tcx> {
|
|||||||
/// c = S; // {a, b, c, d }
|
/// c = S; // {a, b, c, d }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct EverInitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
pub struct EverInitializedPlaces<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
||||||
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||||
mir: &'a Mir<'tcx>,
|
mir: &'a Mir<'tcx>,
|
||||||
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
|
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx: 'tcx, 'tcx: 'a> EverInitializedLvals<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx: 'tcx, 'tcx: 'a> EverInitializedPlaces<'a, 'gcx, 'tcx> {
|
||||||
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
||||||
mir: &'a Mir<'tcx>,
|
mir: &'a Mir<'tcx>,
|
||||||
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
|
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
|
||||||
-> Self
|
-> Self
|
||||||
{
|
{
|
||||||
EverInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe }
|
EverInitializedPlaces { tcx: tcx, mir: mir, mdpe: mdpe }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for EverInitializedLvals<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'a, 'gcx, 'tcx> {
|
||||||
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
|
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> MaybeInitializedLvals<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
|
||||||
fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
|
fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
|
||||||
state: DropFlagState)
|
state: DropFlagState)
|
||||||
{
|
{
|
||||||
@ -302,7 +302,7 @@ impl<'a, 'gcx, 'tcx> MaybeInitializedLvals<'a, 'gcx, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
|
||||||
fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
|
fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
|
||||||
state: DropFlagState)
|
state: DropFlagState)
|
||||||
{
|
{
|
||||||
@ -313,7 +313,7 @@ impl<'a, 'gcx, 'tcx> MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
|
||||||
fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
|
fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
|
||||||
state: DropFlagState)
|
state: DropFlagState)
|
||||||
{
|
{
|
||||||
@ -324,7 +324,7 @@ impl<'a, 'gcx, 'tcx> DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> BitDenotation for MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
|
||||||
type Idx = MovePathIndex;
|
type Idx = MovePathIndex;
|
||||||
fn name() -> &'static str { "maybe_init" }
|
fn name() -> &'static str { "maybe_init" }
|
||||||
fn bits_per_block(&self) -> usize {
|
fn bits_per_block(&self) -> usize {
|
||||||
@ -375,7 +375,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'gcx, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> BitDenotation for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
|
||||||
type Idx = MovePathIndex;
|
type Idx = MovePathIndex;
|
||||||
fn name() -> &'static str { "maybe_uninit" }
|
fn name() -> &'static str { "maybe_uninit" }
|
||||||
fn bits_per_block(&self) -> usize {
|
fn bits_per_block(&self) -> usize {
|
||||||
@ -430,7 +430,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> BitDenotation for DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
|
||||||
type Idx = MovePathIndex;
|
type Idx = MovePathIndex;
|
||||||
fn name() -> &'static str { "definite_init" }
|
fn name() -> &'static str { "definite_init" }
|
||||||
fn bits_per_block(&self) -> usize {
|
fn bits_per_block(&self) -> usize {
|
||||||
@ -561,7 +561,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for MovingOutStatements<'a, 'gcx, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> BitDenotation for EverInitializedLvals<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> BitDenotation for EverInitializedPlaces<'a, 'gcx, 'tcx> {
|
||||||
type Idx = InitIndex;
|
type Idx = InitIndex;
|
||||||
fn name() -> &'static str { "ever_init" }
|
fn name() -> &'static str { "ever_init" }
|
||||||
fn bits_per_block(&self) -> usize {
|
fn bits_per_block(&self) -> usize {
|
||||||
@ -657,21 +657,21 @@ impl<'a, 'gcx, 'tcx> BitDenotation for EverInitializedLvals<'a, 'gcx, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> BitwiseOperator for MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn join(&self, pred1: usize, pred2: usize) -> usize {
|
fn join(&self, pred1: usize, pred2: usize) -> usize {
|
||||||
pred1 | pred2 // "maybe" means we union effects of both preds
|
pred1 | pred2 // "maybe" means we union effects of both preds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> BitwiseOperator for MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> BitwiseOperator for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn join(&self, pred1: usize, pred2: usize) -> usize {
|
fn join(&self, pred1: usize, pred2: usize) -> usize {
|
||||||
pred1 | pred2 // "maybe" means we union effects of both preds
|
pred1 | pred2 // "maybe" means we union effects of both preds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> BitwiseOperator for DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn join(&self, pred1: usize, pred2: usize) -> usize {
|
fn join(&self, pred1: usize, pred2: usize) -> usize {
|
||||||
pred1 & pred2 // "definitely" means we intersect effects of both preds
|
pred1 & pred2 // "definitely" means we intersect effects of both preds
|
||||||
@ -685,7 +685,7 @@ impl<'a, 'gcx, 'tcx> BitwiseOperator for MovingOutStatements<'a, 'gcx, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> BitwiseOperator for EverInitializedLvals<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> BitwiseOperator for EverInitializedPlaces<'a, 'gcx, 'tcx> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn join(&self, pred1: usize, pred2: usize) -> usize {
|
fn join(&self, pred1: usize, pred2: usize) -> usize {
|
||||||
pred1 | pred2 // inits from both preds are in scope
|
pred1 | pred2 // inits from both preds are in scope
|
||||||
@ -702,21 +702,21 @@ impl<'a, 'gcx, 'tcx> BitwiseOperator for EverInitializedLvals<'a, 'gcx, 'tcx> {
|
|||||||
// propagating, or you start at all-ones and then use Intersect as
|
// propagating, or you start at all-ones and then use Intersect as
|
||||||
// your merge when propagating.
|
// your merge when propagating.
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> InitialFlow for MaybeInitializedLvals<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> InitialFlow for MaybeInitializedPlaces<'a, 'gcx, 'tcx> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bottom_value() -> bool {
|
fn bottom_value() -> bool {
|
||||||
false // bottom = uninitialized
|
false // bottom = uninitialized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> InitialFlow for MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> InitialFlow for MaybeUninitializedPlaces<'a, 'gcx, 'tcx> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bottom_value() -> bool {
|
fn bottom_value() -> bool {
|
||||||
false // bottom = initialized (start_block_effect counters this at outset)
|
false // bottom = initialized (start_block_effect counters this at outset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> InitialFlow for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> InitialFlow for DefinitelyInitializedPlaces<'a, 'gcx, 'tcx> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bottom_value() -> bool {
|
fn bottom_value() -> bool {
|
||||||
true // bottom = initialized (start_block_effect counters this at outset)
|
true // bottom = initialized (start_block_effect counters this at outset)
|
||||||
@ -730,7 +730,7 @@ impl<'a, 'gcx, 'tcx> InitialFlow for MovingOutStatements<'a, 'gcx, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> InitialFlow for EverInitializedLvals<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> InitialFlow for EverInitializedPlaces<'a, 'gcx, 'tcx> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn bottom_value() -> bool {
|
fn bottom_value() -> bool {
|
||||||
false // bottom = no initialized variables by default
|
false // bottom = no initialized variables by default
|
||||||
|
@ -26,9 +26,9 @@ use std::path::PathBuf;
|
|||||||
use std::usize;
|
use std::usize;
|
||||||
|
|
||||||
pub use self::impls::{MaybeStorageLive};
|
pub use self::impls::{MaybeStorageLive};
|
||||||
pub use self::impls::{MaybeInitializedLvals, MaybeUninitializedLvals};
|
pub use self::impls::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
|
||||||
pub use self::impls::{DefinitelyInitializedLvals, MovingOutStatements};
|
pub use self::impls::{DefinitelyInitializedPlaces, MovingOutStatements};
|
||||||
pub use self::impls::EverInitializedLvals;
|
pub use self::impls::EverInitializedPlaces;
|
||||||
pub use self::impls::borrows::{Borrows, BorrowData};
|
pub use self::impls::borrows::{Borrows, BorrowData};
|
||||||
pub(crate) use self::impls::borrows::{ActiveBorrows, Reservations, ReserveOrActivateIndex};
|
pub(crate) use self::impls::borrows::{ActiveBorrows, Reservations, ReserveOrActivateIndex};
|
||||||
pub use self::at_location::{FlowAtLocation, FlowsAtLocation};
|
pub use self::at_location::{FlowAtLocation, FlowsAtLocation};
|
||||||
|
@ -86,7 +86,7 @@ impl MoveOutIndex {
|
|||||||
/// It follows a tree structure.
|
/// It follows a tree structure.
|
||||||
///
|
///
|
||||||
/// Given `struct X { m: M, n: N }` and `x: X`, moves like `drop x.m;`
|
/// Given `struct X { m: M, n: N }` and `x: X`, moves like `drop x.m;`
|
||||||
/// move *out* of the l-value `x.m`.
|
/// move *out* of the place `x.m`.
|
||||||
///
|
///
|
||||||
/// The MovePaths representing `x.m` and `x.n` are siblings (that is,
|
/// The MovePaths representing `x.m` and `x.n` are siblings (that is,
|
||||||
/// one of them will link to the other via the `next_sibling` field,
|
/// one of them will link to the other via the `next_sibling` field,
|
||||||
@ -222,7 +222,7 @@ impl fmt::Debug for Init {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tables mapping from an l-value to its MovePathIndex.
|
/// Tables mapping from a place to its MovePathIndex.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MovePathLookup<'tcx> {
|
pub struct MovePathLookup<'tcx> {
|
||||||
locals: IndexVec<Local, MovePathIndex>,
|
locals: IndexVec<Local, MovePathIndex>,
|
||||||
@ -247,7 +247,7 @@ pub enum LookupResult {
|
|||||||
impl<'tcx> MovePathLookup<'tcx> {
|
impl<'tcx> MovePathLookup<'tcx> {
|
||||||
// Unlike the builder `fn move_path_for` below, this lookup
|
// Unlike the builder `fn move_path_for` below, this lookup
|
||||||
// alternative will *not* create a MovePath on the fly for an
|
// alternative will *not* create a MovePath on the fly for an
|
||||||
// unknown l-value, but will rather return the nearest available
|
// unknown place, but will rather return the nearest available
|
||||||
// parent.
|
// parent.
|
||||||
pub fn find(&self, place: &Place<'tcx>) -> LookupResult {
|
pub fn find(&self, place: &Place<'tcx>) -> LookupResult {
|
||||||
match *place {
|
match *place {
|
||||||
|
@ -9,9 +9,9 @@ use interpret::memory::HasMemory;
|
|||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub enum Place {
|
pub enum Place {
|
||||||
/// An place referring to a value allocated in the `Memory` system.
|
/// A place referring to a value allocated in the `Memory` system.
|
||||||
Ptr {
|
Ptr {
|
||||||
/// An place may have an invalid (integral or undef) pointer,
|
/// A place may have an invalid (integral or undef) pointer,
|
||||||
/// since it might be turned back into a reference
|
/// since it might be turned back into a reference
|
||||||
/// before ever being dereferenced.
|
/// before ever being dereferenced.
|
||||||
ptr: Pointer,
|
ptr: Pointer,
|
||||||
@ -19,7 +19,7 @@ pub enum Place {
|
|||||||
extra: PlaceExtra,
|
extra: PlaceExtra,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// An place referring to a value on the stack. Represented by a stack frame index paired with
|
/// A place referring to a value on the stack. Represented by a stack frame index paired with
|
||||||
/// a Mir local index.
|
/// a Mir local index.
|
||||||
Local { frame: usize, local: mir::Local },
|
Local { frame: usize, local: mir::Local },
|
||||||
}
|
}
|
||||||
@ -33,7 +33,7 @@ pub enum PlaceExtra {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> Place {
|
impl<'tcx> Place {
|
||||||
/// Produces an Place that will error if attempted to be read from
|
/// Produces a Place that will error if attempted to be read from
|
||||||
pub fn undef() -> Self {
|
pub fn undef() -> Self {
|
||||||
Self::from_primval_ptr(PrimVal::Undef.into(), Align::from_bytes(1, 1).unwrap())
|
Self::from_primval_ptr(PrimVal::Undef.into(), Align::from_bytes(1, 1).unwrap())
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex, LookupResult};
|
use dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex, LookupResult};
|
||||||
use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals};
|
use dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
|
||||||
use dataflow::{DataflowResults};
|
use dataflow::{DataflowResults};
|
||||||
use dataflow::{on_all_children_bits, on_all_drop_children_bits};
|
use dataflow::{on_all_children_bits, on_all_drop_children_bits};
|
||||||
use dataflow::{drop_flag_effects_for_location, on_lookup_result_bits};
|
use dataflow::{drop_flag_effects_for_location, on_lookup_result_bits};
|
||||||
@ -60,11 +60,11 @@ impl MirPass for ElaborateDrops {
|
|||||||
let dead_unwinds = find_dead_unwinds(tcx, mir, id, &env);
|
let dead_unwinds = find_dead_unwinds(tcx, mir, id, &env);
|
||||||
let flow_inits =
|
let flow_inits =
|
||||||
do_dataflow(tcx, mir, id, &[], &dead_unwinds,
|
do_dataflow(tcx, mir, id, &[], &dead_unwinds,
|
||||||
MaybeInitializedLvals::new(tcx, mir, &env),
|
MaybeInitializedPlaces::new(tcx, mir, &env),
|
||||||
|bd, p| DebugFormatted::new(&bd.move_data().move_paths[p]));
|
|bd, p| DebugFormatted::new(&bd.move_data().move_paths[p]));
|
||||||
let flow_uninits =
|
let flow_uninits =
|
||||||
do_dataflow(tcx, mir, id, &[], &dead_unwinds,
|
do_dataflow(tcx, mir, id, &[], &dead_unwinds,
|
||||||
MaybeUninitializedLvals::new(tcx, mir, &env),
|
MaybeUninitializedPlaces::new(tcx, mir, &env),
|
||||||
|bd, p| DebugFormatted::new(&bd.move_data().move_paths[p]));
|
|bd, p| DebugFormatted::new(&bd.move_data().move_paths[p]));
|
||||||
|
|
||||||
ElaborateDropsCtxt {
|
ElaborateDropsCtxt {
|
||||||
@ -97,7 +97,7 @@ fn find_dead_unwinds<'a, 'tcx>(
|
|||||||
let mut dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
|
let mut dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
|
||||||
let flow_inits =
|
let flow_inits =
|
||||||
do_dataflow(tcx, mir, id, &[], &dead_unwinds,
|
do_dataflow(tcx, mir, id, &[], &dead_unwinds,
|
||||||
MaybeInitializedLvals::new(tcx, mir, &env),
|
MaybeInitializedPlaces::new(tcx, mir, &env),
|
||||||
|bd, p| DebugFormatted::new(&bd.move_data().move_paths[p]));
|
|bd, p| DebugFormatted::new(&bd.move_data().move_paths[p]));
|
||||||
for (bb, bb_data) in mir.basic_blocks().iter_enumerated() {
|
for (bb, bb_data) in mir.basic_blocks().iter_enumerated() {
|
||||||
let location = match bb_data.terminator().kind {
|
let location = match bb_data.terminator().kind {
|
||||||
@ -300,8 +300,8 @@ struct ElaborateDropsCtxt<'a, 'tcx: 'a> {
|
|||||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||||
mir: &'a Mir<'tcx>,
|
mir: &'a Mir<'tcx>,
|
||||||
env: &'a MoveDataParamEnv<'tcx, 'tcx>,
|
env: &'a MoveDataParamEnv<'tcx, 'tcx>,
|
||||||
flow_inits: DataflowResults<MaybeInitializedLvals<'a, 'tcx, 'tcx>>,
|
flow_inits: DataflowResults<MaybeInitializedPlaces<'a, 'tcx, 'tcx>>,
|
||||||
flow_uninits: DataflowResults<MaybeUninitializedLvals<'a, 'tcx, 'tcx>>,
|
flow_uninits: DataflowResults<MaybeUninitializedPlaces<'a, 'tcx, 'tcx>>,
|
||||||
drop_flags: FxHashMap<MovePathIndex, Local>,
|
drop_flags: FxHashMap<MovePathIndex, Local>,
|
||||||
patch: MirPatch<'tcx>,
|
patch: MirPatch<'tcx>,
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ use dataflow::{do_dataflow, DebugFormatted};
|
|||||||
use dataflow::MoveDataParamEnv;
|
use dataflow::MoveDataParamEnv;
|
||||||
use dataflow::BitDenotation;
|
use dataflow::BitDenotation;
|
||||||
use dataflow::DataflowResults;
|
use dataflow::DataflowResults;
|
||||||
use dataflow::{DefinitelyInitializedLvals, MaybeInitializedLvals, MaybeUninitializedLvals};
|
use dataflow::{DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces};
|
||||||
use dataflow::move_paths::{MovePathIndex, LookupResult};
|
use dataflow::move_paths::{MovePathIndex, LookupResult};
|
||||||
use dataflow::move_paths::{HasMoveData, MoveData};
|
use dataflow::move_paths::{HasMoveData, MoveData};
|
||||||
use dataflow;
|
use dataflow;
|
||||||
@ -50,15 +50,15 @@ impl MirPass for SanityCheck {
|
|||||||
let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
|
let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
|
||||||
let flow_inits =
|
let flow_inits =
|
||||||
do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
|
do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
|
||||||
MaybeInitializedLvals::new(tcx, mir, &mdpe),
|
MaybeInitializedPlaces::new(tcx, mir, &mdpe),
|
||||||
|bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
|
|bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
|
||||||
let flow_uninits =
|
let flow_uninits =
|
||||||
do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
|
do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
|
||||||
MaybeUninitializedLvals::new(tcx, mir, &mdpe),
|
MaybeUninitializedPlaces::new(tcx, mir, &mdpe),
|
||||||
|bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
|
|bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
|
||||||
let flow_def_inits =
|
let flow_def_inits =
|
||||||
do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
|
do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
|
||||||
DefinitelyInitializedLvals::new(tcx, mir, &mdpe),
|
DefinitelyInitializedPlaces::new(tcx, mir, &mdpe),
|
||||||
|bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
|
|bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
|
||||||
|
|
||||||
if has_rustc_mir_with(&attributes, "rustc_peek_maybe_init").is_some() {
|
if has_rustc_mir_with(&attributes, "rustc_peek_maybe_init").is_some() {
|
||||||
|
@ -560,7 +560,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
|
|||||||
/// ptr = cur
|
/// ptr = cur
|
||||||
/// cur = cur.offset(1)
|
/// cur = cur.offset(1)
|
||||||
/// } else {
|
/// } else {
|
||||||
/// ptr = &mut LV[cur]
|
/// ptr = &mut P[cur]
|
||||||
/// cur = cur + 1
|
/// cur = cur + 1
|
||||||
/// }
|
/// }
|
||||||
/// drop(ptr)
|
/// drop(ptr)
|
||||||
@ -731,7 +731,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
|
|||||||
if ptr_based {
|
if ptr_based {
|
||||||
let tmp_ty = tcx.mk_mut_ptr(self.place_ty(self.place));
|
let tmp_ty = tcx.mk_mut_ptr(self.place_ty(self.place));
|
||||||
let tmp = Place::Local(self.new_temp(tmp_ty));
|
let tmp = Place::Local(self.new_temp(tmp_ty));
|
||||||
// tmp = &LV;
|
// tmp = &P;
|
||||||
// cur = tmp as *mut T;
|
// cur = tmp as *mut T;
|
||||||
// end = Offset(cur, len);
|
// end = Offset(cur, len);
|
||||||
drop_block_stmts.push(self.assign(&tmp, Rvalue::Ref(
|
drop_block_stmts.push(self.assign(&tmp, Rvalue::Ref(
|
||||||
|
@ -545,7 +545,7 @@ impl<'a, 'tcx> ArgType<'tcx> {
|
|||||||
self.mode == PassMode::Ignore
|
self.mode == PassMode::Ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the LLVM type for an place of the original Rust type of
|
/// Get the LLVM type for a place of the original Rust type of
|
||||||
/// this argument/return, i.e. the result of `type_of::type_of`.
|
/// this argument/return, i.e. the result of `type_of::type_of`.
|
||||||
pub fn memory_ty(&self, cx: &CodegenCx<'a, 'tcx>) -> Type {
|
pub fn memory_ty(&self, cx: &CodegenCx<'a, 'tcx>) -> Type {
|
||||||
self.layout.llvm_type(cx)
|
self.layout.llvm_type(cx)
|
||||||
@ -674,7 +674,7 @@ impl<'a, 'tcx> FnType<'tcx> {
|
|||||||
_ => bug!("FnType::new_vtable: non-pair self {:?}", self_arg)
|
_ => bug!("FnType::new_vtable: non-pair self {:?}", self_arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
let pointee = self_arg.layout.ty.builtin_deref(true, ty::NoPreference)
|
let pointee = self_arg.layout.ty.builtin_deref(true)
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
bug!("FnType::new_vtable: non-pointer self {:?}", self_arg)
|
bug!("FnType::new_vtable: non-pointer self {:?}", self_arg)
|
||||||
}).ty;
|
}).ty;
|
||||||
|
@ -212,7 +212,7 @@ enum Base {
|
|||||||
Static(ValueRef)
|
Static(ValueRef)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An place as seen from a constant.
|
/// A place as seen from a constant.
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
struct ConstPlace<'tcx> {
|
struct ConstPlace<'tcx> {
|
||||||
base: Base,
|
base: Base,
|
||||||
@ -743,7 +743,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
|
|||||||
operand.llval
|
operand.llval
|
||||||
}
|
}
|
||||||
mir::CastKind::Unsize => {
|
mir::CastKind::Unsize => {
|
||||||
let pointee_ty = operand.ty.builtin_deref(true, ty::NoPreference)
|
let pointee_ty = operand.ty.builtin_deref(true)
|
||||||
.expect("consts: unsizing got non-pointer type").ty;
|
.expect("consts: unsizing got non-pointer type").ty;
|
||||||
let (base, old_info) = if !self.cx.type_is_sized(pointee_ty) {
|
let (base, old_info) = if !self.cx.type_is_sized(pointee_ty) {
|
||||||
// Normally, the source is a thin pointer and we are
|
// Normally, the source is a thin pointer and we are
|
||||||
@ -758,7 +758,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
|
|||||||
(operand.llval, None)
|
(operand.llval, None)
|
||||||
};
|
};
|
||||||
|
|
||||||
let unsized_ty = cast_ty.builtin_deref(true, ty::NoPreference)
|
let unsized_ty = cast_ty.builtin_deref(true)
|
||||||
.expect("consts: unsizing got non-pointer target type").ty;
|
.expect("consts: unsizing got non-pointer target type").ty;
|
||||||
let ptr_ty = self.cx.layout_of(unsized_ty).llvm_type(self.cx).ptr_to();
|
let ptr_ty = self.cx.layout_of(unsized_ty).llvm_type(self.cx).ptr_to();
|
||||||
let base = consts::ptrcast(base, ptr_ty);
|
let base = consts::ptrcast(base, ptr_ty);
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use llvm::ValueRef;
|
use llvm::ValueRef;
|
||||||
use rustc::ty;
|
|
||||||
use rustc::ty::layout::{self, Align, LayoutOf, TyLayout};
|
use rustc::ty::layout::{self, Align, LayoutOf, TyLayout};
|
||||||
use rustc::mir;
|
use rustc::mir;
|
||||||
use rustc_data_structures::indexed_vec::Idx;
|
use rustc_data_structures::indexed_vec::Idx;
|
||||||
@ -100,7 +99,7 @@ impl<'a, 'tcx> OperandRef<'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn deref(self, cx: &CodegenCx<'a, 'tcx>) -> PlaceRef<'tcx> {
|
pub fn deref(self, cx: &CodegenCx<'a, 'tcx>) -> PlaceRef<'tcx> {
|
||||||
let projected_ty = self.layout.ty.builtin_deref(true, ty::NoPreference)
|
let projected_ty = self.layout.ty.builtin_deref(true)
|
||||||
.unwrap_or_else(|| bug!("deref of non-pointer {:?}", self)).ty;
|
.unwrap_or_else(|| bug!("deref of non-pointer {:?}", self)).ty;
|
||||||
let (llptr, llextra) = match self.val {
|
let (llptr, llextra) = match self.val {
|
||||||
OperandValue::Immediate(llptr) => (llptr, ptr::null_mut()),
|
OperandValue::Immediate(llptr) => (llptr, ptr::null_mut()),
|
||||||
|
@ -14,8 +14,8 @@ use rustc::hir::pat_util::EnumerateAndAdjustIterator;
|
|||||||
use rustc::infer;
|
use rustc::infer;
|
||||||
use rustc::infer::type_variable::TypeVariableOrigin;
|
use rustc::infer::type_variable::TypeVariableOrigin;
|
||||||
use rustc::traits::ObligationCauseCode;
|
use rustc::traits::ObligationCauseCode;
|
||||||
use rustc::ty::{self, Ty, TypeFoldable, LvaluePreference};
|
use rustc::ty::{self, Ty, TypeFoldable};
|
||||||
use check::{FnCtxt, Expectation, Diverges};
|
use check::{FnCtxt, Expectation, Diverges, Needs};
|
||||||
use check::coercion::CoerceMany;
|
use check::coercion::CoerceMany;
|
||||||
use util::nodemap::FxHashMap;
|
use util::nodemap::FxHashMap;
|
||||||
|
|
||||||
@ -500,7 +500,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
|
|
||||||
pub fn check_dereferencable(&self, span: Span, expected: Ty<'tcx>, inner: &hir::Pat) -> bool {
|
pub fn check_dereferencable(&self, span: Span, expected: Ty<'tcx>, inner: &hir::Pat) -> bool {
|
||||||
if let PatKind::Binding(..) = inner.node {
|
if let PatKind::Binding(..) = inner.node {
|
||||||
if let Some(mt) = self.shallow_resolve(expected).builtin_deref(true, ty::NoPreference) {
|
if let Some(mt) = self.shallow_resolve(expected).builtin_deref(true) {
|
||||||
if let ty::TyDynamic(..) = mt.ty.sty {
|
if let ty::TyDynamic(..) = mt.ty.sty {
|
||||||
// This is "x = SomeTrait" being reduced from
|
// This is "x = SomeTrait" being reduced from
|
||||||
// "let &x = &SomeTrait" or "let box x = Box<SomeTrait>", an error.
|
// "let &x = &SomeTrait" or "let box x = Box<SomeTrait>", an error.
|
||||||
@ -584,7 +584,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
});
|
});
|
||||||
let discrim_ty;
|
let discrim_ty;
|
||||||
if let Some(m) = contains_ref_bindings {
|
if let Some(m) = contains_ref_bindings {
|
||||||
discrim_ty = self.check_expr_with_lvalue_pref(discrim, LvaluePreference::from_mutbl(m));
|
discrim_ty = self.check_expr_with_needs(discrim, Needs::maybe_mut_place(m));
|
||||||
} else {
|
} else {
|
||||||
// ...but otherwise we want to use any supertype of the
|
// ...but otherwise we want to use any supertype of the
|
||||||
// discriminant. This is sort of a workaround, see note (*) in
|
// discriminant. This is sort of a workaround, see note (*) in
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
use astconv::AstConv;
|
use astconv::AstConv;
|
||||||
|
|
||||||
use super::{FnCtxt, LvalueOp};
|
use super::{FnCtxt, PlaceOp, Needs};
|
||||||
use super::method::MethodCallee;
|
use super::method::MethodCallee;
|
||||||
|
|
||||||
use rustc::infer::InferOk;
|
use rustc::infer::InferOk;
|
||||||
@ -18,7 +18,6 @@ use rustc::session::DiagnosticMessageId;
|
|||||||
use rustc::traits;
|
use rustc::traits;
|
||||||
use rustc::ty::{self, Ty, TraitRef};
|
use rustc::ty::{self, Ty, TraitRef};
|
||||||
use rustc::ty::{ToPredicate, TypeFoldable};
|
use rustc::ty::{ToPredicate, TypeFoldable};
|
||||||
use rustc::ty::{LvaluePreference, NoPreference};
|
|
||||||
use rustc::ty::adjustment::{Adjustment, Adjust, OverloadedDeref};
|
use rustc::ty::adjustment::{Adjustment, Adjust, OverloadedDeref};
|
||||||
|
|
||||||
use syntax_pos::Span;
|
use syntax_pos::Span;
|
||||||
@ -85,7 +84,7 @@ impl<'a, 'gcx, 'tcx> Iterator for Autoderef<'a, 'gcx, 'tcx> {
|
|||||||
|
|
||||||
// Otherwise, deref if type is derefable:
|
// Otherwise, deref if type is derefable:
|
||||||
let (kind, new_ty) =
|
let (kind, new_ty) =
|
||||||
if let Some(mt) = self.cur_ty.builtin_deref(self.include_raw_pointers, NoPreference) {
|
if let Some(mt) = self.cur_ty.builtin_deref(self.include_raw_pointers) {
|
||||||
(AutoderefKind::Builtin, mt.ty)
|
(AutoderefKind::Builtin, mt.ty)
|
||||||
} else {
|
} else {
|
||||||
let ty = self.overloaded_deref_ty(self.cur_ty)?;
|
let ty = self.overloaded_deref_ty(self.cur_ty)?;
|
||||||
@ -163,19 +162,19 @@ impl<'a, 'gcx, 'tcx> Autoderef<'a, 'gcx, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the adjustment steps.
|
/// Returns the adjustment steps.
|
||||||
pub fn adjust_steps(&self, pref: LvaluePreference)
|
pub fn adjust_steps(&self, needs: Needs)
|
||||||
-> Vec<Adjustment<'tcx>> {
|
-> Vec<Adjustment<'tcx>> {
|
||||||
self.fcx.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(pref))
|
self.fcx.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(needs))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn adjust_steps_as_infer_ok(&self, pref: LvaluePreference)
|
pub fn adjust_steps_as_infer_ok(&self, needs: Needs)
|
||||||
-> InferOk<'tcx, Vec<Adjustment<'tcx>>> {
|
-> InferOk<'tcx, Vec<Adjustment<'tcx>>> {
|
||||||
let mut obligations = vec![];
|
let mut obligations = vec![];
|
||||||
let targets = self.steps.iter().skip(1).map(|&(ty, _)| ty)
|
let targets = self.steps.iter().skip(1).map(|&(ty, _)| ty)
|
||||||
.chain(iter::once(self.cur_ty));
|
.chain(iter::once(self.cur_ty));
|
||||||
let steps: Vec<_> = self.steps.iter().map(|&(source, kind)| {
|
let steps: Vec<_> = self.steps.iter().map(|&(source, kind)| {
|
||||||
if let AutoderefKind::Overloaded = kind {
|
if let AutoderefKind::Overloaded = kind {
|
||||||
self.fcx.try_overloaded_deref(self.span, source, pref)
|
self.fcx.try_overloaded_deref(self.span, source, needs)
|
||||||
.and_then(|InferOk { value: method, obligations: o }| {
|
.and_then(|InferOk { value: method, obligations: o }| {
|
||||||
obligations.extend(o);
|
obligations.extend(o);
|
||||||
if let ty::TyRef(region, mt) = method.sig.output().sty {
|
if let ty::TyRef(region, mt) = method.sig.output().sty {
|
||||||
@ -238,8 +237,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
pub fn try_overloaded_deref(&self,
|
pub fn try_overloaded_deref(&self,
|
||||||
span: Span,
|
span: Span,
|
||||||
base_ty: Ty<'tcx>,
|
base_ty: Ty<'tcx>,
|
||||||
pref: LvaluePreference)
|
needs: Needs)
|
||||||
-> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
|
-> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
|
||||||
self.try_overloaded_lvalue_op(span, base_ty, &[], pref, LvalueOp::Deref)
|
self.try_overloaded_place_op(span, base_ty, &[], needs, PlaceOp::Deref)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,14 +8,14 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use super::{Expectation, FnCtxt, TupleArgumentsFlag};
|
use super::{Expectation, FnCtxt, Needs, TupleArgumentsFlag};
|
||||||
use super::autoderef::Autoderef;
|
use super::autoderef::Autoderef;
|
||||||
use super::method::MethodCallee;
|
use super::method::MethodCallee;
|
||||||
|
|
||||||
use hir::def::Def;
|
use hir::def::Def;
|
||||||
use hir::def_id::{DefId, LOCAL_CRATE};
|
use hir::def_id::{DefId, LOCAL_CRATE};
|
||||||
use rustc::{infer, traits};
|
use rustc::{infer, traits};
|
||||||
use rustc::ty::{self, TyCtxt, TypeFoldable, LvaluePreference, Ty};
|
use rustc::ty::{self, TyCtxt, TypeFoldable, Ty};
|
||||||
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow};
|
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow};
|
||||||
use syntax::abi;
|
use syntax::abi;
|
||||||
use syntax::symbol::Symbol;
|
use syntax::symbol::Symbol;
|
||||||
@ -96,7 +96,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
// If the callee is a bare function or a closure, then we're all set.
|
// If the callee is a bare function or a closure, then we're all set.
|
||||||
match adjusted_ty.sty {
|
match adjusted_ty.sty {
|
||||||
ty::TyFnDef(..) | ty::TyFnPtr(_) => {
|
ty::TyFnDef(..) | ty::TyFnPtr(_) => {
|
||||||
let adjustments = autoderef.adjust_steps(LvaluePreference::NoPreference);
|
let adjustments = autoderef.adjust_steps(Needs::None);
|
||||||
self.apply_adjustments(callee_expr, adjustments);
|
self.apply_adjustments(callee_expr, adjustments);
|
||||||
return Some(CallStep::Builtin(adjusted_ty));
|
return Some(CallStep::Builtin(adjusted_ty));
|
||||||
}
|
}
|
||||||
@ -113,7 +113,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
infer::FnCall,
|
infer::FnCall,
|
||||||
&closure_ty)
|
&closure_ty)
|
||||||
.0;
|
.0;
|
||||||
let adjustments = autoderef.adjust_steps(LvaluePreference::NoPreference);
|
let adjustments = autoderef.adjust_steps(Needs::None);
|
||||||
self.record_deferred_call_resolution(def_id, DeferredCallResolution {
|
self.record_deferred_call_resolution(def_id, DeferredCallResolution {
|
||||||
call_expr,
|
call_expr,
|
||||||
callee_expr,
|
callee_expr,
|
||||||
@ -143,7 +143,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.try_overloaded_call_traits(call_expr, adjusted_ty).map(|(autoref, method)| {
|
self.try_overloaded_call_traits(call_expr, adjusted_ty).map(|(autoref, method)| {
|
||||||
let mut adjustments = autoderef.adjust_steps(LvaluePreference::NoPreference);
|
let mut adjustments = autoderef.adjust_steps(Needs::None);
|
||||||
adjustments.extend(autoref);
|
adjustments.extend(autoref);
|
||||||
self.apply_adjustments(callee_expr, adjustments);
|
self.apply_adjustments(callee_expr, adjustments);
|
||||||
CallStep::Overloaded(method)
|
CallStep::Overloaded(method)
|
||||||
|
@ -60,7 +60,7 @@
|
|||||||
//! sort of a minor point so I've opted to leave it for later---after all
|
//! sort of a minor point so I've opted to leave it for later---after all
|
||||||
//! we may want to adjust precisely when coercions occur.
|
//! we may want to adjust precisely when coercions occur.
|
||||||
|
|
||||||
use check::{Diverges, FnCtxt};
|
use check::{Diverges, FnCtxt, Needs};
|
||||||
|
|
||||||
use rustc::hir;
|
use rustc::hir;
|
||||||
use rustc::hir::def_id::DefId;
|
use rustc::hir::def_id::DefId;
|
||||||
@ -69,8 +69,7 @@ use rustc::infer::type_variable::TypeVariableOrigin;
|
|||||||
use rustc::lint;
|
use rustc::lint;
|
||||||
use rustc::traits::{self, ObligationCause, ObligationCauseCode};
|
use rustc::traits::{self, ObligationCause, ObligationCauseCode};
|
||||||
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow};
|
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow};
|
||||||
use rustc::ty::{self, LvaluePreference, TypeAndMut,
|
use rustc::ty::{self, TypeAndMut, Ty, ClosureSubsts};
|
||||||
Ty, ClosureSubsts};
|
|
||||||
use rustc::ty::fold::TypeFoldable;
|
use rustc::ty::fold::TypeFoldable;
|
||||||
use rustc::ty::error::TypeError;
|
use rustc::ty::error::TypeError;
|
||||||
use rustc::ty::relate::RelateResult;
|
use rustc::ty::relate::RelateResult;
|
||||||
@ -410,9 +409,9 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
|
|||||||
return success(vec![], ty, obligations);
|
return success(vec![], ty, obligations);
|
||||||
}
|
}
|
||||||
|
|
||||||
let pref = LvaluePreference::from_mutbl(mt_b.mutbl);
|
let needs = Needs::maybe_mut_place(mt_b.mutbl);
|
||||||
let InferOk { value: mut adjustments, obligations: o }
|
let InferOk { value: mut adjustments, obligations: o }
|
||||||
= autoderef.adjust_steps_as_infer_ok(pref);
|
= autoderef.adjust_steps_as_infer_ok(needs);
|
||||||
obligations.extend(o);
|
obligations.extend(o);
|
||||||
obligations.extend(autoderef.into_obligations());
|
obligations.extend(autoderef.into_obligations());
|
||||||
|
|
||||||
|
@ -11,11 +11,11 @@
|
|||||||
use super::{probe, MethodCallee};
|
use super::{probe, MethodCallee};
|
||||||
|
|
||||||
use astconv::AstConv;
|
use astconv::AstConv;
|
||||||
use check::{FnCtxt, LvalueOp, callee};
|
use check::{FnCtxt, PlaceOp, callee, Needs};
|
||||||
use hir::def_id::DefId;
|
use hir::def_id::DefId;
|
||||||
use rustc::ty::subst::Substs;
|
use rustc::ty::subst::Substs;
|
||||||
use rustc::traits;
|
use rustc::traits;
|
||||||
use rustc::ty::{self, LvaluePreference, NoPreference, PreferMutLvalue, Ty};
|
use rustc::ty::{self, Ty};
|
||||||
use rustc::ty::subst::Subst;
|
use rustc::ty::subst::Subst;
|
||||||
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, OverloadedDeref};
|
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, OverloadedDeref};
|
||||||
use rustc::ty::fold::TypeFoldable;
|
use rustc::ty::fold::TypeFoldable;
|
||||||
@ -136,7 +136,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(hir::MutMutable) = pick.autoref {
|
if let Some(hir::MutMutable) = pick.autoref {
|
||||||
self.convert_lvalue_derefs_to_mutable();
|
self.convert_place_derefs_to_mutable();
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfirmResult { callee, illegal_sized_bound }
|
ConfirmResult { callee, illegal_sized_bound }
|
||||||
@ -155,7 +155,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
|||||||
let (_, n) = autoderef.nth(pick.autoderefs).unwrap();
|
let (_, n) = autoderef.nth(pick.autoderefs).unwrap();
|
||||||
assert_eq!(n, pick.autoderefs);
|
assert_eq!(n, pick.autoderefs);
|
||||||
|
|
||||||
let mut adjustments = autoderef.adjust_steps(LvaluePreference::NoPreference);
|
let mut adjustments = autoderef.adjust_steps(Needs::None);
|
||||||
|
|
||||||
let mut target = autoderef.unambiguous_final_ty();
|
let mut target = autoderef.unambiguous_final_ty();
|
||||||
|
|
||||||
@ -416,7 +416,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
|||||||
/// When we select a method with a mutable autoref, we have to go convert any
|
/// When we select a method with a mutable autoref, we have to go convert any
|
||||||
/// auto-derefs, indices, etc from `Deref` and `Index` into `DerefMut` and `IndexMut`
|
/// auto-derefs, indices, etc from `Deref` and `Index` into `DerefMut` and `IndexMut`
|
||||||
/// respectively.
|
/// respectively.
|
||||||
fn convert_lvalue_derefs_to_mutable(&self) {
|
fn convert_place_derefs_to_mutable(&self) {
|
||||||
// Gather up expressions we want to munge.
|
// Gather up expressions we want to munge.
|
||||||
let mut exprs = Vec::new();
|
let mut exprs = Vec::new();
|
||||||
exprs.push(self.self_expr);
|
exprs.push(self.self_expr);
|
||||||
@ -431,14 +431,14 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("convert_lvalue_derefs_to_mutable: exprs={:?}", exprs);
|
debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);
|
||||||
|
|
||||||
// Fix up autoderefs and derefs.
|
// Fix up autoderefs and derefs.
|
||||||
for (i, &expr) in exprs.iter().rev().enumerate() {
|
for (i, &expr) in exprs.iter().rev().enumerate() {
|
||||||
debug!("convert_lvalue_derefs_to_mutable: i={} expr={:?}", i, expr);
|
debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr);
|
||||||
|
|
||||||
// Fix up the autoderefs. Autorefs can only occur immediately preceding
|
// Fix up the autoderefs. Autorefs can only occur immediately preceding
|
||||||
// overloaded lvalue ops, and will be fixed by them in order to get
|
// overloaded place ops, and will be fixed by them in order to get
|
||||||
// the correct region.
|
// the correct region.
|
||||||
let mut source = self.node_ty(expr.hir_id);
|
let mut source = self.node_ty(expr.hir_id);
|
||||||
// Do not mutate adjustments in place, but rather take them,
|
// Do not mutate adjustments in place, but rather take them,
|
||||||
@ -449,10 +449,10 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
|||||||
.adjustments_mut()
|
.adjustments_mut()
|
||||||
.remove(expr.hir_id);
|
.remove(expr.hir_id);
|
||||||
if let Some(mut adjustments) = previous_adjustments {
|
if let Some(mut adjustments) = previous_adjustments {
|
||||||
let pref = LvaluePreference::PreferMutLvalue;
|
let needs = Needs::MutPlace;
|
||||||
for adjustment in &mut adjustments {
|
for adjustment in &mut adjustments {
|
||||||
if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind {
|
if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind {
|
||||||
if let Some(ok) = self.try_overloaded_deref(expr.span, source, pref) {
|
if let Some(ok) = self.try_overloaded_deref(expr.span, source, needs) {
|
||||||
let method = self.register_infer_ok_obligations(ok);
|
let method = self.register_infer_ok_obligations(ok);
|
||||||
if let ty::TyRef(region, mt) = method.sig.output().sty {
|
if let ty::TyRef(region, mt) = method.sig.output().sty {
|
||||||
*deref = OverloadedDeref {
|
*deref = OverloadedDeref {
|
||||||
@ -470,28 +470,28 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
|||||||
match expr.node {
|
match expr.node {
|
||||||
hir::ExprIndex(ref base_expr, ref index_expr) => {
|
hir::ExprIndex(ref base_expr, ref index_expr) => {
|
||||||
let index_expr_ty = self.node_ty(index_expr.hir_id);
|
let index_expr_ty = self.node_ty(index_expr.hir_id);
|
||||||
self.convert_lvalue_op_to_mutable(
|
self.convert_place_op_to_mutable(
|
||||||
LvalueOp::Index, expr, base_expr, &[index_expr_ty]);
|
PlaceOp::Index, expr, base_expr, &[index_expr_ty]);
|
||||||
}
|
}
|
||||||
hir::ExprUnary(hir::UnDeref, ref base_expr) => {
|
hir::ExprUnary(hir::UnDeref, ref base_expr) => {
|
||||||
self.convert_lvalue_op_to_mutable(
|
self.convert_place_op_to_mutable(
|
||||||
LvalueOp::Deref, expr, base_expr, &[]);
|
PlaceOp::Deref, expr, base_expr, &[]);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_lvalue_op_to_mutable(&self,
|
fn convert_place_op_to_mutable(&self,
|
||||||
op: LvalueOp,
|
op: PlaceOp,
|
||||||
expr: &hir::Expr,
|
expr: &hir::Expr,
|
||||||
base_expr: &hir::Expr,
|
base_expr: &hir::Expr,
|
||||||
arg_tys: &[Ty<'tcx>])
|
arg_tys: &[Ty<'tcx>])
|
||||||
{
|
{
|
||||||
debug!("convert_lvalue_op_to_mutable({:?}, {:?}, {:?}, {:?})",
|
debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})",
|
||||||
op, expr, base_expr, arg_tys);
|
op, expr, base_expr, arg_tys);
|
||||||
if !self.tables.borrow().is_method_call(expr) {
|
if !self.tables.borrow().is_method_call(expr) {
|
||||||
debug!("convert_lvalue_op_to_mutable - builtin, nothing to do");
|
debug!("convert_place_op_to_mutable - builtin, nothing to do");
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -499,24 +499,24 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
|||||||
.map_or_else(|| self.node_ty(expr.hir_id), |adj| adj.target);
|
.map_or_else(|| self.node_ty(expr.hir_id), |adj| adj.target);
|
||||||
let base_ty = self.resolve_type_vars_if_possible(&base_ty);
|
let base_ty = self.resolve_type_vars_if_possible(&base_ty);
|
||||||
|
|
||||||
// Need to deref because overloaded lvalue ops take self by-reference.
|
// Need to deref because overloaded place ops take self by-reference.
|
||||||
let base_ty = base_ty.builtin_deref(false, NoPreference)
|
let base_ty = base_ty.builtin_deref(false)
|
||||||
.expect("lvalue op takes something that is not a ref")
|
.expect("place op takes something that is not a ref")
|
||||||
.ty;
|
.ty;
|
||||||
|
|
||||||
let method = self.try_overloaded_lvalue_op(
|
let method = self.try_overloaded_place_op(
|
||||||
expr.span, base_ty, arg_tys, PreferMutLvalue, op);
|
expr.span, base_ty, arg_tys, Needs::MutPlace, op);
|
||||||
let method = match method {
|
let method = match method {
|
||||||
Some(ok) => self.register_infer_ok_obligations(ok),
|
Some(ok) => self.register_infer_ok_obligations(ok),
|
||||||
None => return self.tcx.sess.delay_span_bug(expr.span, "re-trying op failed")
|
None => return self.tcx.sess.delay_span_bug(expr.span, "re-trying op failed")
|
||||||
};
|
};
|
||||||
debug!("convert_lvalue_op_to_mutable: method={:?}", method);
|
debug!("convert_place_op_to_mutable: method={:?}", method);
|
||||||
self.write_method_call(expr.hir_id, method);
|
self.write_method_call(expr.hir_id, method);
|
||||||
|
|
||||||
let (region, mutbl) = if let ty::TyRef(r, mt) = method.sig.inputs()[0].sty {
|
let (region, mutbl) = if let ty::TyRef(r, mt) = method.sig.inputs()[0].sty {
|
||||||
(r, mt.mutbl)
|
(r, mt.mutbl)
|
||||||
} else {
|
} else {
|
||||||
span_bug!(expr.span, "input to lvalue op is not a ref?");
|
span_bug!(expr.span, "input to place op is not a ref?");
|
||||||
};
|
};
|
||||||
|
|
||||||
// Convert the autoref in the base expr to mutable with the correct
|
// Convert the autoref in the base expr to mutable with the correct
|
||||||
@ -529,7 +529,7 @@ impl<'a, 'gcx, 'tcx> ConfirmContext<'a, 'gcx, 'tcx> {
|
|||||||
let mut source = base_expr_ty;
|
let mut source = base_expr_ty;
|
||||||
for adjustment in &mut adjustments[..] {
|
for adjustment in &mut adjustments[..] {
|
||||||
if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind {
|
if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind {
|
||||||
debug!("convert_lvalue_op_to_mutable: converting autoref {:?}", adjustment);
|
debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment);
|
||||||
adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl));
|
adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl));
|
||||||
adjustment.target = self.tcx.mk_ref(region, ty::TypeAndMut {
|
adjustment.target = self.tcx.mk_ref(region, ty::TypeAndMut {
|
||||||
ty: source,
|
ty: source,
|
||||||
|
@ -95,7 +95,6 @@ use rustc::infer::type_variable::{TypeVariableOrigin};
|
|||||||
use rustc::middle::region;
|
use rustc::middle::region;
|
||||||
use rustc::ty::subst::{Kind, Subst, Substs};
|
use rustc::ty::subst::{Kind, Subst, Substs};
|
||||||
use rustc::traits::{self, FulfillmentContext, ObligationCause, ObligationCauseCode};
|
use rustc::traits::{self, FulfillmentContext, ObligationCause, ObligationCauseCode};
|
||||||
use rustc::ty::{ParamTy, LvaluePreference, NoPreference, PreferMutLvalue};
|
|
||||||
use rustc::ty::{self, Ty, TyCtxt, Visibility, ToPredicate};
|
use rustc::ty::{self, Ty, TyCtxt, Visibility, ToPredicate};
|
||||||
use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
|
use rustc::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
|
||||||
use rustc::ty::fold::TypeFoldable;
|
use rustc::ty::fold::TypeFoldable;
|
||||||
@ -368,6 +367,21 @@ impl<'a, 'gcx, 'tcx> Expectation<'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Needs {
|
||||||
|
MutPlace,
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Needs {
|
||||||
|
fn maybe_mut_place(m: hir::Mutability) -> Self {
|
||||||
|
match m {
|
||||||
|
hir::MutMutable => Needs::MutPlace,
|
||||||
|
hir::MutImmutable => Needs::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct UnsafetyState {
|
pub struct UnsafetyState {
|
||||||
pub def: ast::NodeId,
|
pub def: ast::NodeId,
|
||||||
@ -410,7 +424,7 @@ impl UnsafetyState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum LvalueOp {
|
pub enum PlaceOp {
|
||||||
Deref,
|
Deref,
|
||||||
Index
|
Index
|
||||||
}
|
}
|
||||||
@ -543,7 +557,7 @@ pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
|
|||||||
/// foo();}` or `{return; 22}`, where we would warn on the
|
/// foo();}` or `{return; 22}`, where we would warn on the
|
||||||
/// `foo()` or `22`.
|
/// `foo()` or `22`.
|
||||||
///
|
///
|
||||||
/// - To permit assignment into a local variable or other lvalue
|
/// - To permit assignment into a local variable or other place
|
||||||
/// (including the "return slot") of type `!`. This is allowed
|
/// (including the "return slot") of type `!`. This is allowed
|
||||||
/// if **either** the type of value being assigned is `!`, which
|
/// if **either** the type of value being assigned is `!`, which
|
||||||
/// means the current code is dead, **or** the expression's
|
/// means the current code is dead, **or** the expression's
|
||||||
@ -2207,11 +2221,65 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// For the overloaded lvalue expressions (`*x`, `x[3]`), the trait
|
fn is_place_expr(&self, expr: &hir::Expr) -> bool {
|
||||||
|
match expr.node {
|
||||||
|
hir::ExprPath(hir::QPath::Resolved(_, ref path)) => {
|
||||||
|
match path.def {
|
||||||
|
Def::Local(..) | Def::Upvar(..) | Def::Static(..) | Def::Err => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::ExprType(ref e, _) => {
|
||||||
|
self.is_place_expr(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::ExprUnary(hir::UnDeref, _) |
|
||||||
|
hir::ExprField(..) |
|
||||||
|
hir::ExprTupField(..) |
|
||||||
|
hir::ExprIndex(..) => {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Partially qualified paths in expressions can only legally
|
||||||
|
// refer to associated items which are always rvalues.
|
||||||
|
hir::ExprPath(hir::QPath::TypeRelative(..)) |
|
||||||
|
|
||||||
|
hir::ExprCall(..) |
|
||||||
|
hir::ExprMethodCall(..) |
|
||||||
|
hir::ExprStruct(..) |
|
||||||
|
hir::ExprTup(..) |
|
||||||
|
hir::ExprIf(..) |
|
||||||
|
hir::ExprMatch(..) |
|
||||||
|
hir::ExprClosure(..) |
|
||||||
|
hir::ExprBlock(..) |
|
||||||
|
hir::ExprRepeat(..) |
|
||||||
|
hir::ExprArray(..) |
|
||||||
|
hir::ExprBreak(..) |
|
||||||
|
hir::ExprAgain(..) |
|
||||||
|
hir::ExprRet(..) |
|
||||||
|
hir::ExprWhile(..) |
|
||||||
|
hir::ExprLoop(..) |
|
||||||
|
hir::ExprAssign(..) |
|
||||||
|
hir::ExprInlineAsm(..) |
|
||||||
|
hir::ExprAssignOp(..) |
|
||||||
|
hir::ExprLit(_) |
|
||||||
|
hir::ExprUnary(..) |
|
||||||
|
hir::ExprBox(..) |
|
||||||
|
hir::ExprAddrOf(..) |
|
||||||
|
hir::ExprBinary(..) |
|
||||||
|
hir::ExprYield(..) |
|
||||||
|
hir::ExprCast(..) => {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// For the overloaded place expressions (`*x`, `x[3]`), the trait
|
||||||
/// returns a type of `&T`, but the actual type we assign to the
|
/// returns a type of `&T`, but the actual type we assign to the
|
||||||
/// *expression* is `T`. So this function just peels off the return
|
/// *expression* is `T`. So this function just peels off the return
|
||||||
/// type by one layer to yield `T`.
|
/// type by one layer to yield `T`.
|
||||||
fn make_overloaded_lvalue_return_type(&self,
|
fn make_overloaded_place_return_type(&self,
|
||||||
method: MethodCallee<'tcx>)
|
method: MethodCallee<'tcx>)
|
||||||
-> ty::TypeAndMut<'tcx>
|
-> ty::TypeAndMut<'tcx>
|
||||||
{
|
{
|
||||||
@ -2219,7 +2287,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
let ret_ty = method.sig.output();
|
let ret_ty = method.sig.output();
|
||||||
|
|
||||||
// method returns &T, but the type as visible to user is T, so deref
|
// method returns &T, but the type as visible to user is T, so deref
|
||||||
ret_ty.builtin_deref(true, NoPreference).unwrap()
|
ret_ty.builtin_deref(true).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lookup_indexing(&self,
|
fn lookup_indexing(&self,
|
||||||
@ -2227,7 +2295,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
base_expr: &'gcx hir::Expr,
|
base_expr: &'gcx hir::Expr,
|
||||||
base_ty: Ty<'tcx>,
|
base_ty: Ty<'tcx>,
|
||||||
idx_ty: Ty<'tcx>,
|
idx_ty: Ty<'tcx>,
|
||||||
lvalue_pref: LvaluePreference)
|
needs: Needs)
|
||||||
-> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)>
|
-> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)>
|
||||||
{
|
{
|
||||||
// FIXME(#18741) -- this is almost but not quite the same as the
|
// FIXME(#18741) -- this is almost but not quite the same as the
|
||||||
@ -2237,7 +2305,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
let mut autoderef = self.autoderef(base_expr.span, base_ty);
|
let mut autoderef = self.autoderef(base_expr.span, base_ty);
|
||||||
let mut result = None;
|
let mut result = None;
|
||||||
while result.is_none() && autoderef.next().is_some() {
|
while result.is_none() && autoderef.next().is_some() {
|
||||||
result = self.try_index_step(expr, base_expr, &autoderef, lvalue_pref, idx_ty);
|
result = self.try_index_step(expr, base_expr, &autoderef, needs, idx_ty);
|
||||||
}
|
}
|
||||||
autoderef.finalize();
|
autoderef.finalize();
|
||||||
result
|
result
|
||||||
@ -2252,7 +2320,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
expr: &hir::Expr,
|
expr: &hir::Expr,
|
||||||
base_expr: &hir::Expr,
|
base_expr: &hir::Expr,
|
||||||
autoderef: &Autoderef<'a, 'gcx, 'tcx>,
|
autoderef: &Autoderef<'a, 'gcx, 'tcx>,
|
||||||
lvalue_pref: LvaluePreference,
|
needs: Needs,
|
||||||
index_ty: Ty<'tcx>)
|
index_ty: Ty<'tcx>)
|
||||||
-> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)>
|
-> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)>
|
||||||
{
|
{
|
||||||
@ -2279,14 +2347,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
// type from the method signature.
|
// type from the method signature.
|
||||||
// If some lookup succeeded, install method in table
|
// If some lookup succeeded, install method in table
|
||||||
let input_ty = self.next_ty_var(TypeVariableOrigin::AutoDeref(base_expr.span));
|
let input_ty = self.next_ty_var(TypeVariableOrigin::AutoDeref(base_expr.span));
|
||||||
let method = self.try_overloaded_lvalue_op(
|
let method = self.try_overloaded_place_op(
|
||||||
expr.span, self_ty, &[input_ty], lvalue_pref, LvalueOp::Index);
|
expr.span, self_ty, &[input_ty], needs, PlaceOp::Index);
|
||||||
|
|
||||||
let result = method.map(|ok| {
|
let result = method.map(|ok| {
|
||||||
debug!("try_index_step: success, using overloaded indexing");
|
debug!("try_index_step: success, using overloaded indexing");
|
||||||
let method = self.register_infer_ok_obligations(ok);
|
let method = self.register_infer_ok_obligations(ok);
|
||||||
|
|
||||||
let mut adjustments = autoderef.adjust_steps(lvalue_pref);
|
let mut adjustments = autoderef.adjust_steps(needs);
|
||||||
if let ty::TyRef(region, mt) = method.sig.inputs()[0].sty {
|
if let ty::TyRef(region, mt) = method.sig.inputs()[0].sty {
|
||||||
adjustments.push(Adjustment {
|
adjustments.push(Adjustment {
|
||||||
kind: Adjust::Borrow(AutoBorrow::Ref(region, mt.mutbl)),
|
kind: Adjust::Borrow(AutoBorrow::Ref(region, mt.mutbl)),
|
||||||
@ -2305,7 +2373,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
self.apply_adjustments(base_expr, adjustments);
|
self.apply_adjustments(base_expr, adjustments);
|
||||||
|
|
||||||
self.write_method_call(expr.hir_id, method);
|
self.write_method_call(expr.hir_id, method);
|
||||||
(input_ty, self.make_overloaded_lvalue_return_type(method).ty)
|
(input_ty, self.make_overloaded_place_return_type(method).ty)
|
||||||
});
|
});
|
||||||
if result.is_some() {
|
if result.is_some() {
|
||||||
return result;
|
return result;
|
||||||
@ -2315,45 +2383,45 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_lvalue_op(&self, op: LvalueOp, is_mut: bool) -> (Option<DefId>, Symbol) {
|
fn resolve_place_op(&self, op: PlaceOp, is_mut: bool) -> (Option<DefId>, Symbol) {
|
||||||
let (tr, name) = match (op, is_mut) {
|
let (tr, name) = match (op, is_mut) {
|
||||||
(LvalueOp::Deref, false) =>
|
(PlaceOp::Deref, false) =>
|
||||||
(self.tcx.lang_items().deref_trait(), "deref"),
|
(self.tcx.lang_items().deref_trait(), "deref"),
|
||||||
(LvalueOp::Deref, true) =>
|
(PlaceOp::Deref, true) =>
|
||||||
(self.tcx.lang_items().deref_mut_trait(), "deref_mut"),
|
(self.tcx.lang_items().deref_mut_trait(), "deref_mut"),
|
||||||
(LvalueOp::Index, false) =>
|
(PlaceOp::Index, false) =>
|
||||||
(self.tcx.lang_items().index_trait(), "index"),
|
(self.tcx.lang_items().index_trait(), "index"),
|
||||||
(LvalueOp::Index, true) =>
|
(PlaceOp::Index, true) =>
|
||||||
(self.tcx.lang_items().index_mut_trait(), "index_mut"),
|
(self.tcx.lang_items().index_mut_trait(), "index_mut"),
|
||||||
};
|
};
|
||||||
(tr, Symbol::intern(name))
|
(tr, Symbol::intern(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_overloaded_lvalue_op(&self,
|
fn try_overloaded_place_op(&self,
|
||||||
span: Span,
|
span: Span,
|
||||||
base_ty: Ty<'tcx>,
|
base_ty: Ty<'tcx>,
|
||||||
arg_tys: &[Ty<'tcx>],
|
arg_tys: &[Ty<'tcx>],
|
||||||
lvalue_pref: LvaluePreference,
|
needs: Needs,
|
||||||
op: LvalueOp)
|
op: PlaceOp)
|
||||||
-> Option<InferOk<'tcx, MethodCallee<'tcx>>>
|
-> Option<InferOk<'tcx, MethodCallee<'tcx>>>
|
||||||
{
|
{
|
||||||
debug!("try_overloaded_lvalue_op({:?},{:?},{:?},{:?})",
|
debug!("try_overloaded_place_op({:?},{:?},{:?},{:?})",
|
||||||
span,
|
span,
|
||||||
base_ty,
|
base_ty,
|
||||||
lvalue_pref,
|
needs,
|
||||||
op);
|
op);
|
||||||
|
|
||||||
// Try Mut first, if preferred.
|
// Try Mut first, if needed.
|
||||||
let (mut_tr, mut_op) = self.resolve_lvalue_op(op, true);
|
let (mut_tr, mut_op) = self.resolve_place_op(op, true);
|
||||||
let method = match (lvalue_pref, mut_tr) {
|
let method = match (needs, mut_tr) {
|
||||||
(PreferMutLvalue, Some(trait_did)) => {
|
(Needs::MutPlace, Some(trait_did)) => {
|
||||||
self.lookup_method_in_trait(span, mut_op, trait_did, base_ty, Some(arg_tys))
|
self.lookup_method_in_trait(span, mut_op, trait_did, base_ty, Some(arg_tys))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Otherwise, fall back to the immutable version.
|
// Otherwise, fall back to the immutable version.
|
||||||
let (imm_tr, imm_op) = self.resolve_lvalue_op(op, false);
|
let (imm_tr, imm_op) = self.resolve_place_op(op, false);
|
||||||
let method = match (method, imm_tr) {
|
let method = match (method, imm_tr) {
|
||||||
(None, Some(trait_did)) => {
|
(None, Some(trait_did)) => {
|
||||||
self.lookup_method_in_trait(span, imm_op, trait_did, base_ty, Some(arg_tys))
|
self.lookup_method_in_trait(span, imm_op, trait_did, base_ty, Some(arg_tys))
|
||||||
@ -2738,18 +2806,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
fn check_expr_coercable_to_type(&self,
|
fn check_expr_coercable_to_type(&self,
|
||||||
expr: &'gcx hir::Expr,
|
expr: &'gcx hir::Expr,
|
||||||
expected: Ty<'tcx>) -> Ty<'tcx> {
|
expected: Ty<'tcx>) -> Ty<'tcx> {
|
||||||
self.check_expr_coercable_to_type_with_lvalue_pref(expr, expected, NoPreference)
|
self.check_expr_coercable_to_type_with_needs(expr, expected, Needs::None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_expr_coercable_to_type_with_lvalue_pref(&self,
|
fn check_expr_coercable_to_type_with_needs(&self,
|
||||||
expr: &'gcx hir::Expr,
|
expr: &'gcx hir::Expr,
|
||||||
expected: Ty<'tcx>,
|
expected: Ty<'tcx>,
|
||||||
lvalue_pref: LvaluePreference)
|
needs: Needs)
|
||||||
-> Ty<'tcx> {
|
-> Ty<'tcx> {
|
||||||
let ty = self.check_expr_with_expectation_and_lvalue_pref(
|
let ty = self.check_expr_with_expectation_and_needs(
|
||||||
expr,
|
expr,
|
||||||
ExpectHasType(expected),
|
ExpectHasType(expected),
|
||||||
lvalue_pref);
|
needs);
|
||||||
self.demand_coerce(expr, ty, expected)
|
self.demand_coerce(expr, ty, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2761,16 +2829,15 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
fn check_expr_with_expectation(&self,
|
fn check_expr_with_expectation(&self,
|
||||||
expr: &'gcx hir::Expr,
|
expr: &'gcx hir::Expr,
|
||||||
expected: Expectation<'tcx>) -> Ty<'tcx> {
|
expected: Expectation<'tcx>) -> Ty<'tcx> {
|
||||||
self.check_expr_with_expectation_and_lvalue_pref(expr, expected, NoPreference)
|
self.check_expr_with_expectation_and_needs(expr, expected, Needs::None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_expr(&self, expr: &'gcx hir::Expr) -> Ty<'tcx> {
|
fn check_expr(&self, expr: &'gcx hir::Expr) -> Ty<'tcx> {
|
||||||
self.check_expr_with_expectation(expr, NoExpectation)
|
self.check_expr_with_expectation(expr, NoExpectation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_expr_with_lvalue_pref(&self, expr: &'gcx hir::Expr,
|
fn check_expr_with_needs(&self, expr: &'gcx hir::Expr, needs: Needs) -> Ty<'tcx> {
|
||||||
lvalue_pref: LvaluePreference) -> Ty<'tcx> {
|
self.check_expr_with_expectation_and_needs(expr, NoExpectation, needs)
|
||||||
self.check_expr_with_expectation_and_lvalue_pref(expr, NoExpectation, lvalue_pref)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine the `self` type, using fresh variables for all variables
|
// determine the `self` type, using fresh variables for all variables
|
||||||
@ -2853,9 +2920,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
span: Span,
|
span: Span,
|
||||||
args: &'gcx [hir::Expr],
|
args: &'gcx [hir::Expr],
|
||||||
expected: Expectation<'tcx>,
|
expected: Expectation<'tcx>,
|
||||||
lvalue_pref: LvaluePreference) -> Ty<'tcx> {
|
needs: Needs) -> Ty<'tcx> {
|
||||||
let rcvr = &args[0];
|
let rcvr = &args[0];
|
||||||
let rcvr_t = self.check_expr_with_lvalue_pref(&rcvr, lvalue_pref);
|
let rcvr_t = self.check_expr_with_needs(&rcvr, needs);
|
||||||
// no need to check for bot/err -- callee does that
|
// no need to check for bot/err -- callee does that
|
||||||
let rcvr_t = self.structurally_resolved_type(expr.span, rcvr_t);
|
let rcvr_t = self.structurally_resolved_type(expr.span, rcvr_t);
|
||||||
|
|
||||||
@ -2965,10 +3032,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
// Check field access expressions
|
// Check field access expressions
|
||||||
fn check_field(&self,
|
fn check_field(&self,
|
||||||
expr: &'gcx hir::Expr,
|
expr: &'gcx hir::Expr,
|
||||||
lvalue_pref: LvaluePreference,
|
needs: Needs,
|
||||||
base: &'gcx hir::Expr,
|
base: &'gcx hir::Expr,
|
||||||
field: &Spanned<ast::Name>) -> Ty<'tcx> {
|
field: &Spanned<ast::Name>) -> Ty<'tcx> {
|
||||||
let expr_t = self.check_expr_with_lvalue_pref(base, lvalue_pref);
|
let expr_t = self.check_expr_with_needs(base, needs);
|
||||||
let expr_t = self.structurally_resolved_type(expr.span,
|
let expr_t = self.structurally_resolved_type(expr.span,
|
||||||
expr_t);
|
expr_t);
|
||||||
let mut private_candidate = None;
|
let mut private_candidate = None;
|
||||||
@ -2983,7 +3050,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
if let Some(field) = fields.iter().find(|f| f.name.to_ident() == ident) {
|
if let Some(field) = fields.iter().find(|f| f.name.to_ident() == ident) {
|
||||||
let field_ty = self.field_ty(expr.span, field, substs);
|
let field_ty = self.field_ty(expr.span, field, substs);
|
||||||
if field.vis.is_accessible_from(def_scope, self.tcx) {
|
if field.vis.is_accessible_from(def_scope, self.tcx) {
|
||||||
let adjustments = autoderef.adjust_steps(lvalue_pref);
|
let adjustments = autoderef.adjust_steps(needs);
|
||||||
self.apply_adjustments(base, adjustments);
|
self.apply_adjustments(base, adjustments);
|
||||||
autoderef.finalize();
|
autoderef.finalize();
|
||||||
|
|
||||||
@ -3102,10 +3169,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
// Check tuple index expressions
|
// Check tuple index expressions
|
||||||
fn check_tup_field(&self,
|
fn check_tup_field(&self,
|
||||||
expr: &'gcx hir::Expr,
|
expr: &'gcx hir::Expr,
|
||||||
lvalue_pref: LvaluePreference,
|
needs: Needs,
|
||||||
base: &'gcx hir::Expr,
|
base: &'gcx hir::Expr,
|
||||||
idx: codemap::Spanned<usize>) -> Ty<'tcx> {
|
idx: codemap::Spanned<usize>) -> Ty<'tcx> {
|
||||||
let expr_t = self.check_expr_with_lvalue_pref(base, lvalue_pref);
|
let expr_t = self.check_expr_with_needs(base, needs);
|
||||||
let expr_t = self.structurally_resolved_type(expr.span,
|
let expr_t = self.structurally_resolved_type(expr.span,
|
||||||
expr_t);
|
expr_t);
|
||||||
let mut private_candidate = None;
|
let mut private_candidate = None;
|
||||||
@ -3146,7 +3213,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(field_ty) = field {
|
if let Some(field_ty) = field {
|
||||||
let adjustments = autoderef.adjust_steps(lvalue_pref);
|
let adjustments = autoderef.adjust_steps(needs);
|
||||||
self.apply_adjustments(base, adjustments);
|
self.apply_adjustments(base, adjustments);
|
||||||
autoderef.finalize();
|
autoderef.finalize();
|
||||||
return field_ty;
|
return field_ty;
|
||||||
@ -3476,10 +3543,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
/// Note that inspecting a type's structure *directly* may expose the fact
|
/// Note that inspecting a type's structure *directly* may expose the fact
|
||||||
/// that there are actually multiple representations for `TyError`, so avoid
|
/// that there are actually multiple representations for `TyError`, so avoid
|
||||||
/// that when err needs to be handled differently.
|
/// that when err needs to be handled differently.
|
||||||
fn check_expr_with_expectation_and_lvalue_pref(&self,
|
fn check_expr_with_expectation_and_needs(&self,
|
||||||
expr: &'gcx hir::Expr,
|
expr: &'gcx hir::Expr,
|
||||||
expected: Expectation<'tcx>,
|
expected: Expectation<'tcx>,
|
||||||
lvalue_pref: LvaluePreference) -> Ty<'tcx> {
|
needs: Needs) -> Ty<'tcx> {
|
||||||
debug!(">> typechecking: expr={:?} expected={:?}",
|
debug!(">> typechecking: expr={:?} expected={:?}",
|
||||||
expr, expected);
|
expr, expected);
|
||||||
|
|
||||||
@ -3492,7 +3559,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
self.diverges.set(Diverges::Maybe);
|
self.diverges.set(Diverges::Maybe);
|
||||||
self.has_errors.set(false);
|
self.has_errors.set(false);
|
||||||
|
|
||||||
let ty = self.check_expr_kind(expr, expected, lvalue_pref);
|
let ty = self.check_expr_kind(expr, expected, needs);
|
||||||
|
|
||||||
// Warn for non-block expressions with diverging children.
|
// Warn for non-block expressions with diverging children.
|
||||||
match expr.node {
|
match expr.node {
|
||||||
@ -3526,7 +3593,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
fn check_expr_kind(&self,
|
fn check_expr_kind(&self,
|
||||||
expr: &'gcx hir::Expr,
|
expr: &'gcx hir::Expr,
|
||||||
expected: Expectation<'tcx>,
|
expected: Expectation<'tcx>,
|
||||||
lvalue_pref: LvaluePreference) -> Ty<'tcx> {
|
needs: Needs) -> Ty<'tcx> {
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
let id = expr.id;
|
let id = expr.id;
|
||||||
match expr.node {
|
match expr.node {
|
||||||
@ -3560,22 +3627,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
NoExpectation
|
NoExpectation
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let lvalue_pref = match unop {
|
let needs = match unop {
|
||||||
hir::UnDeref => lvalue_pref,
|
hir::UnDeref => needs,
|
||||||
_ => NoPreference
|
_ => Needs::None
|
||||||
};
|
};
|
||||||
let mut oprnd_t = self.check_expr_with_expectation_and_lvalue_pref(&oprnd,
|
let mut oprnd_t = self.check_expr_with_expectation_and_needs(&oprnd,
|
||||||
expected_inner,
|
expected_inner,
|
||||||
lvalue_pref);
|
needs);
|
||||||
|
|
||||||
if !oprnd_t.references_error() {
|
if !oprnd_t.references_error() {
|
||||||
oprnd_t = self.structurally_resolved_type(expr.span, oprnd_t);
|
oprnd_t = self.structurally_resolved_type(expr.span, oprnd_t);
|
||||||
match unop {
|
match unop {
|
||||||
hir::UnDeref => {
|
hir::UnDeref => {
|
||||||
if let Some(mt) = oprnd_t.builtin_deref(true, NoPreference) {
|
if let Some(mt) = oprnd_t.builtin_deref(true) {
|
||||||
oprnd_t = mt.ty;
|
oprnd_t = mt.ty;
|
||||||
} else if let Some(ok) = self.try_overloaded_deref(
|
} else if let Some(ok) = self.try_overloaded_deref(
|
||||||
expr.span, oprnd_t, lvalue_pref) {
|
expr.span, oprnd_t, needs) {
|
||||||
let method = self.register_infer_ok_obligations(ok);
|
let method = self.register_infer_ok_obligations(ok);
|
||||||
if let ty::TyRef(region, mt) = method.sig.inputs()[0].sty {
|
if let ty::TyRef(region, mt) = method.sig.inputs()[0].sty {
|
||||||
self.apply_adjustments(oprnd, vec![Adjustment {
|
self.apply_adjustments(oprnd, vec![Adjustment {
|
||||||
@ -3583,7 +3650,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
target: method.sig.inputs()[0]
|
target: method.sig.inputs()[0]
|
||||||
}]);
|
}]);
|
||||||
}
|
}
|
||||||
oprnd_t = self.make_overloaded_lvalue_return_type(method).ty;
|
oprnd_t = self.make_overloaded_place_return_type(method).ty;
|
||||||
self.write_method_call(expr.hir_id, method);
|
self.write_method_call(expr.hir_id, method);
|
||||||
} else {
|
} else {
|
||||||
type_error_struct!(tcx.sess, expr.span, oprnd_t, E0614,
|
type_error_struct!(tcx.sess, expr.span, oprnd_t, E0614,
|
||||||
@ -3614,8 +3681,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
let hint = expected.only_has_type(self).map_or(NoExpectation, |ty| {
|
let hint = expected.only_has_type(self).map_or(NoExpectation, |ty| {
|
||||||
match ty.sty {
|
match ty.sty {
|
||||||
ty::TyRef(_, ref mt) | ty::TyRawPtr(ref mt) => {
|
ty::TyRef(_, ref mt) | ty::TyRawPtr(ref mt) => {
|
||||||
if self.tcx.expr_is_lval(&oprnd) {
|
if self.is_place_expr(&oprnd) {
|
||||||
// Lvalues may legitimately have unsized types.
|
// Places may legitimately have unsized types.
|
||||||
// For example, dereferences of a fat pointer and
|
// For example, dereferences of a fat pointer and
|
||||||
// the last field of a struct can be unsized.
|
// the last field of a struct can be unsized.
|
||||||
ExpectHasType(mt.ty)
|
ExpectHasType(mt.ty)
|
||||||
@ -3626,8 +3693,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
_ => NoExpectation
|
_ => NoExpectation
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let lvalue_pref = LvaluePreference::from_mutbl(mutbl);
|
let needs = Needs::maybe_mut_place(mutbl);
|
||||||
let ty = self.check_expr_with_expectation_and_lvalue_pref(&oprnd, hint, lvalue_pref);
|
let ty = self.check_expr_with_expectation_and_needs(&oprnd, hint, needs);
|
||||||
|
|
||||||
let tm = ty::TypeAndMut { ty: ty, mutbl: mutbl };
|
let tm = ty::TypeAndMut { ty: ty, mutbl: mutbl };
|
||||||
if tm.ty.references_error() {
|
if tm.ty.references_error() {
|
||||||
@ -3771,7 +3838,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
tcx.types.never
|
tcx.types.never
|
||||||
}
|
}
|
||||||
hir::ExprAssign(ref lhs, ref rhs) => {
|
hir::ExprAssign(ref lhs, ref rhs) => {
|
||||||
let lhs_ty = self.check_expr_with_lvalue_pref(&lhs, PreferMutLvalue);
|
let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace);
|
||||||
|
|
||||||
let rhs_ty = self.check_expr_coercable_to_type(&rhs, lhs_ty);
|
let rhs_ty = self.check_expr_coercable_to_type(&rhs, lhs_ty);
|
||||||
|
|
||||||
@ -3783,7 +3850,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
_ => {
|
_ => {
|
||||||
// Only check this if not in an `if` condition, as the
|
// Only check this if not in an `if` condition, as the
|
||||||
// mistyped comparison help is more appropriate.
|
// mistyped comparison help is more appropriate.
|
||||||
if !self.tcx.expr_is_lval(&lhs) {
|
if !self.is_place_expr(&lhs) {
|
||||||
struct_span_err!(self.tcx.sess, expr.span, E0070,
|
struct_span_err!(self.tcx.sess, expr.span, E0070,
|
||||||
"invalid left-hand side expression")
|
"invalid left-hand side expression")
|
||||||
.span_label(expr.span, "left-hand of expression not valid")
|
.span_label(expr.span, "left-hand of expression not valid")
|
||||||
@ -3872,7 +3939,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
self.check_call(expr, &callee, args, expected)
|
self.check_call(expr, &callee, args, expected)
|
||||||
}
|
}
|
||||||
hir::ExprMethodCall(ref segment, span, ref args) => {
|
hir::ExprMethodCall(ref segment, span, ref args) => {
|
||||||
self.check_method_call(expr, segment, span, args, expected, lvalue_pref)
|
self.check_method_call(expr, segment, span, args, expected, needs)
|
||||||
}
|
}
|
||||||
hir::ExprCast(ref e, ref t) => {
|
hir::ExprCast(ref e, ref t) => {
|
||||||
// Find the type of `e`. Supply hints based on the type we are casting to,
|
// Find the type of `e`. Supply hints based on the type we are casting to,
|
||||||
@ -4015,13 +4082,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
self.check_expr_struct(expr, expected, qpath, fields, base_expr)
|
self.check_expr_struct(expr, expected, qpath, fields, base_expr)
|
||||||
}
|
}
|
||||||
hir::ExprField(ref base, ref field) => {
|
hir::ExprField(ref base, ref field) => {
|
||||||
self.check_field(expr, lvalue_pref, &base, field)
|
self.check_field(expr, needs, &base, field)
|
||||||
}
|
}
|
||||||
hir::ExprTupField(ref base, idx) => {
|
hir::ExprTupField(ref base, idx) => {
|
||||||
self.check_tup_field(expr, lvalue_pref, &base, idx)
|
self.check_tup_field(expr, needs, &base, idx)
|
||||||
}
|
}
|
||||||
hir::ExprIndex(ref base, ref idx) => {
|
hir::ExprIndex(ref base, ref idx) => {
|
||||||
let base_t = self.check_expr_with_lvalue_pref(&base, lvalue_pref);
|
let base_t = self.check_expr_with_needs(&base, needs);
|
||||||
let idx_t = self.check_expr(&idx);
|
let idx_t = self.check_expr(&idx);
|
||||||
|
|
||||||
if base_t.references_error() {
|
if base_t.references_error() {
|
||||||
@ -4030,7 +4097,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
idx_t
|
idx_t
|
||||||
} else {
|
} else {
|
||||||
let base_t = self.structurally_resolved_type(expr.span, base_t);
|
let base_t = self.structurally_resolved_type(expr.span, base_t);
|
||||||
match self.lookup_indexing(expr, base, base_t, idx_t, lvalue_pref) {
|
match self.lookup_indexing(expr, base, base_t, idx_t, needs) {
|
||||||
Some((index_ty, element_ty)) => {
|
Some((index_ty, element_ty)) => {
|
||||||
self.demand_coerce(idx, idx_t, index_ty);
|
self.demand_coerce(idx, idx_t, index_ty);
|
||||||
element_ty
|
element_ty
|
||||||
@ -4178,9 +4245,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
// ref mut, for soundness (issue #23116). In particular, in
|
// ref mut, for soundness (issue #23116). In particular, in
|
||||||
// the latter case, we need to be clear that the type of the
|
// the latter case, we need to be clear that the type of the
|
||||||
// referent for the reference that results is *equal to* the
|
// referent for the reference that results is *equal to* the
|
||||||
// type of the lvalue it is referencing, and not some
|
// type of the place it is referencing, and not some
|
||||||
// supertype thereof.
|
// supertype thereof.
|
||||||
let init_ty = self.check_expr_with_lvalue_pref(init, LvaluePreference::from_mutbl(m));
|
let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m));
|
||||||
self.demand_eqtype(init.span, local_ty, init_ty);
|
self.demand_eqtype(init.span, local_ty, init_ty);
|
||||||
init_ty
|
init_ty
|
||||||
} else {
|
} else {
|
||||||
@ -5023,7 +5090,7 @@ pub fn check_bounds_are_used<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
|||||||
let lifetime_count = generics.lifetimes().count();
|
let lifetime_count = generics.lifetimes().count();
|
||||||
|
|
||||||
for leaf_ty in ty.walk() {
|
for leaf_ty in ty.walk() {
|
||||||
if let ty::TyParam(ParamTy {idx, ..}) = leaf_ty.sty {
|
if let ty::TyParam(ty::ParamTy {idx, ..}) = leaf_ty.sty {
|
||||||
debug!("Found use of ty param num {}", idx);
|
debug!("Found use of ty param num {}", idx);
|
||||||
tps_used[idx as usize - lifetime_count] = true;
|
tps_used[idx as usize - lifetime_count] = true;
|
||||||
} else if let ty::TyError = leaf_ty.sty {
|
} else if let ty::TyError = leaf_ty.sty {
|
||||||
|
@ -10,9 +10,9 @@
|
|||||||
|
|
||||||
//! Code related to processing overloaded binary and unary operators.
|
//! Code related to processing overloaded binary and unary operators.
|
||||||
|
|
||||||
use super::FnCtxt;
|
use super::{FnCtxt, Needs};
|
||||||
use super::method::MethodCallee;
|
use super::method::MethodCallee;
|
||||||
use rustc::ty::{self, Ty, TypeFoldable, NoPreference, PreferMutLvalue, TypeVariants};
|
use rustc::ty::{self, Ty, TypeFoldable, TypeVariants};
|
||||||
use rustc::ty::TypeVariants::{TyStr, TyRef};
|
use rustc::ty::TypeVariants::{TyStr, TyRef};
|
||||||
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow};
|
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow};
|
||||||
use rustc::infer::type_variable::TypeVariableOrigin;
|
use rustc::infer::type_variable::TypeVariableOrigin;
|
||||||
@ -40,10 +40,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
return_ty
|
return_ty
|
||||||
};
|
};
|
||||||
|
|
||||||
let tcx = self.tcx;
|
if !self.is_place_expr(lhs_expr) {
|
||||||
if !tcx.expr_is_lval(lhs_expr) {
|
|
||||||
struct_span_err!(
|
struct_span_err!(
|
||||||
tcx.sess, lhs_expr.span,
|
self.tcx.sess, lhs_expr.span,
|
||||||
E0067, "invalid left-hand side expression")
|
E0067, "invalid left-hand side expression")
|
||||||
.span_label(
|
.span_label(
|
||||||
lhs_expr.span,
|
lhs_expr.span,
|
||||||
@ -166,18 +165,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
|||||||
op,
|
op,
|
||||||
is_assign);
|
is_assign);
|
||||||
|
|
||||||
let lhs_pref = match is_assign {
|
let lhs_needs = match is_assign {
|
||||||
IsAssign::Yes => PreferMutLvalue,
|
IsAssign::Yes => Needs::MutPlace,
|
||||||
IsAssign::No => NoPreference
|
IsAssign::No => Needs::None
|
||||||
};
|
};
|
||||||
// Find a suitable supertype of the LHS expression's type, by coercing to
|
// Find a suitable supertype of the LHS expression's type, by coercing to
|
||||||
// a type variable, to pass as the `Self` to the trait, avoiding invariant
|
// a type variable, to pass as the `Self` to the trait, avoiding invariant
|
||||||
// trait matching creating lifetime constraints that are too strict.
|
// trait matching creating lifetime constraints that are too strict.
|
||||||
// E.g. adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result
|
// E.g. adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result
|
||||||
// in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`.
|
// in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`.
|
||||||
let lhs_ty = self.check_expr_coercable_to_type_with_lvalue_pref(lhs_expr,
|
let lhs_ty = self.check_expr_coercable_to_type_with_needs(lhs_expr,
|
||||||
self.next_ty_var(TypeVariableOrigin::MiscVariable(lhs_expr.span)),
|
self.next_ty_var(TypeVariableOrigin::MiscVariable(lhs_expr.span)),
|
||||||
lhs_pref);
|
lhs_needs);
|
||||||
let lhs_ty = self.resolve_type_vars_with_obligations(lhs_ty);
|
let lhs_ty = self.resolve_type_vars_with_obligations(lhs_ty);
|
||||||
|
|
||||||
// NB: As we have not yet type-checked the RHS, we don't have the
|
// NB: As we have not yet type-checked the RHS, we don't have the
|
||||||
|
@ -732,8 +732,8 @@ and [RFC 809] for more details.
|
|||||||
"##,
|
"##,
|
||||||
|
|
||||||
E0067: r##"
|
E0067: r##"
|
||||||
The left-hand side of a compound assignment expression must be an lvalue
|
The left-hand side of a compound assignment expression must be a place
|
||||||
expression. An lvalue expression represents a memory location and includes
|
expression. A place expression represents a memory location and includes
|
||||||
item paths (ie, namespaced variables), dereferences, indexing expressions,
|
item paths (ie, namespaced variables), dereferences, indexing expressions,
|
||||||
and field references.
|
and field references.
|
||||||
|
|
||||||
@ -742,7 +742,7 @@ Let's start with some erroneous code examples:
|
|||||||
```compile_fail,E0067
|
```compile_fail,E0067
|
||||||
use std::collections::LinkedList;
|
use std::collections::LinkedList;
|
||||||
|
|
||||||
// Bad: assignment to non-lvalue expression
|
// Bad: assignment to non-place expression
|
||||||
LinkedList::new() += 1;
|
LinkedList::new() += 1;
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
@ -783,14 +783,14 @@ function's return type and the value being returned.
|
|||||||
"##,
|
"##,
|
||||||
|
|
||||||
E0070: r##"
|
E0070: r##"
|
||||||
The left-hand side of an assignment operator must be an lvalue expression. An
|
The left-hand side of an assignment operator must be a place expression. An
|
||||||
lvalue expression represents a memory location and can be a variable (with
|
place expression represents a memory location and can be a variable (with
|
||||||
optional namespacing), a dereference, an indexing expression or a field
|
optional namespacing), a dereference, an indexing expression or a field
|
||||||
reference.
|
reference.
|
||||||
|
|
||||||
More details can be found in the [Expressions] section of the Reference.
|
More details can be found in the [Expressions] section of the Reference.
|
||||||
|
|
||||||
[Expressions]: https://doc.rust-lang.org/reference/expressions.html#lvalues-rvalues-and-temporaries
|
[Expressions]: https://doc.rust-lang.org/reference/expressions.html#places-rvalues-and-temporaries
|
||||||
|
|
||||||
Now, we can go further. Here are some erroneous code examples:
|
Now, we can go further. Here are some erroneous code examples:
|
||||||
|
|
||||||
@ -806,7 +806,7 @@ fn some_other_func() {}
|
|||||||
|
|
||||||
fn some_function() {
|
fn some_function() {
|
||||||
SOME_CONST = 14; // error : a constant value cannot be changed!
|
SOME_CONST = 14; // error : a constant value cannot be changed!
|
||||||
1 = 3; // error : 1 isn't a valid lvalue!
|
1 = 3; // error : 1 isn't a valid place!
|
||||||
some_other_func() = 4; // error : we can't assign value to a function!
|
some_other_func() = 4; // error : we can't assign value to a function!
|
||||||
SomeStruct.x = 12; // error : SomeStruct a structure name but it is used
|
SomeStruct.x = 12; // error : SomeStruct a structure name but it is used
|
||||||
// like a variable!
|
// like a variable!
|
||||||
|
@ -1439,7 +1439,7 @@ impl<'a> MethodDef<'a> {
|
|||||||
&catch_all_substructure);
|
&catch_all_substructure);
|
||||||
|
|
||||||
// Final wrinkle: the self_args are expressions that deref
|
// Final wrinkle: the self_args are expressions that deref
|
||||||
// down to desired l-values, but we cannot actually deref
|
// down to desired places, but we cannot actually deref
|
||||||
// them when they are fed as r-values into a tuple
|
// them when they are fed as r-values into a tuple
|
||||||
// expression; here add a layer of borrowing, turning
|
// expression; here add a layer of borrowing, turning
|
||||||
// `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
|
// `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
|
||||||
@ -1516,7 +1516,7 @@ impl<'a> MethodDef<'a> {
|
|||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Final wrinkle: the self_args are expressions that deref
|
// Final wrinkle: the self_args are expressions that deref
|
||||||
// down to desired l-values, but we cannot actually deref
|
// down to desired places, but we cannot actually deref
|
||||||
// them when they are fed as r-values into a tuple
|
// them when they are fed as r-values into a tuple
|
||||||
// expression; here add a layer of borrowing, turning
|
// expression; here add a layer of borrowing, turning
|
||||||
// `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
|
// `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`.
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
// check that we link regions in mutable lvalue ops correctly - issue #41774
|
// check that we link regions in mutable place ops correctly - issue #41774
|
||||||
|
|
||||||
struct Data(i32);
|
struct Data(i32);
|
||||||
|
|
||||||
|
@ -404,9 +404,9 @@ pub fn value_cast(a: u32) -> i32 {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Change l-value in assignment ------------------------------------------------
|
// Change place in assignment --------------------------------------------------
|
||||||
#[cfg(cfail1)]
|
#[cfg(cfail1)]
|
||||||
pub fn lvalue() -> i32 {
|
pub fn place() -> i32 {
|
||||||
let mut x = 10;
|
let mut x = 10;
|
||||||
let mut y = 11;
|
let mut y = 11;
|
||||||
x = 9;
|
x = 9;
|
||||||
@ -416,7 +416,7 @@ pub fn lvalue() -> i32 {
|
|||||||
#[cfg(not(cfail1))]
|
#[cfg(not(cfail1))]
|
||||||
#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")]
|
#[rustc_clean(except="HirBody,MirOptimized,MirValidated", cfg="cfail2")]
|
||||||
#[rustc_clean(cfg="cfail3")]
|
#[rustc_clean(cfg="cfail3")]
|
||||||
pub fn lvalue() -> i32 {
|
pub fn place() -> i32 {
|
||||||
let mut x = 10;
|
let mut x = 10;
|
||||||
let mut y = 11;
|
let mut y = 11;
|
||||||
y = 9;
|
y = 9;
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
// Test that we don't ICE when translating a generic impl method from
|
// Test that we don't ICE when translating a generic impl method from
|
||||||
// an extern crate that contains a match expression on a local
|
// an extern crate that contains a match expression on a local
|
||||||
// variable lvalue where one of the match case bodies contains an
|
// variable place where one of the match case bodies contains an
|
||||||
// expression that autoderefs through an overloaded generic deref
|
// expression that autoderefs through an overloaded generic deref
|
||||||
// impl.
|
// impl.
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
// This used to generate invalid IR in that even if we took the
|
// This used to generate invalid IR in that even if we took the
|
||||||
// `false` branch we'd still try to free the Box from the other
|
// `false` branch we'd still try to free the Box from the other
|
||||||
// arm. This was due to treating `*Box::new(9)` as an rvalue datum
|
// arm. This was due to treating `*Box::new(9)` as an rvalue datum
|
||||||
// instead of as an lvalue.
|
// instead of as a place.
|
||||||
|
|
||||||
fn test(foo: bool) -> u8 {
|
fn test(foo: bool) -> u8 {
|
||||||
match foo {
|
match foo {
|
||||||
|
@ -8,8 +8,8 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
// Test that an `&mut self` method, when invoked on an lvalue whose
|
// Test that an `&mut self` method, when invoked on a place whose
|
||||||
// type is `&mut [u8]`, passes in a pointer to the lvalue and not a
|
// type is `&mut [u8]`, passes in a pointer to the place and not a
|
||||||
// temporary. Issue #19147.
|
// temporary. Issue #19147.
|
||||||
|
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
@ -41,7 +41,7 @@ fn main() {
|
|||||||
// all borrows are extended - nothing has been dropped yet
|
// all borrows are extended - nothing has been dropped yet
|
||||||
assert_eq!(get(), vec![]);
|
assert_eq!(get(), vec![]);
|
||||||
}
|
}
|
||||||
// in a let-statement, extended lvalues are dropped
|
// in a let-statement, extended places are dropped
|
||||||
// *after* the let result (tho they have the same scope
|
// *after* the let result (tho they have the same scope
|
||||||
// as far as scope-based borrowck goes).
|
// as far as scope-based borrowck goes).
|
||||||
assert_eq!(get(), vec![0, 2, 3, 1]);
|
assert_eq!(get(), vec![0, 2, 3, 1]);
|
||||||
|
@ -40,6 +40,6 @@ fn main() {
|
|||||||
assert_eq!(b, 1: u16);
|
assert_eq!(b, 1: u16);
|
||||||
|
|
||||||
let mut v = Vec::new();
|
let mut v = Vec::new();
|
||||||
v: Vec<u8> = vec![1, 2, 3]; // Lvalue type ascription
|
v: Vec<u8> = vec![1, 2, 3]; // Place expression type ascription
|
||||||
assert_eq!(v, [1u8, 2, 3]);
|
assert_eq!(v, [1u8, 2, 3]);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
macro_rules! not_an_lvalue {
|
macro_rules! not_a_place {
|
||||||
($thing:expr) => {
|
($thing:expr) => {
|
||||||
$thing = 42;
|
$thing = 42;
|
||||||
//~^ ERROR invalid left-hand side expression
|
//~^ ERROR invalid left-hand side expression
|
||||||
@ -16,5 +16,5 @@ macro_rules! not_an_lvalue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
not_an_lvalue!(99);
|
not_a_place!(99);
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,8 @@ error[E0070]: invalid left-hand side expression
|
|||||||
13 | $thing = 42;
|
13 | $thing = 42;
|
||||||
| ^^^^^^^^^^^ left-hand of expression not valid
|
| ^^^^^^^^^^^ left-hand of expression not valid
|
||||||
...
|
...
|
||||||
19 | not_an_lvalue!(99);
|
19 | not_a_place!(99);
|
||||||
| ------------------- in this macro invocation
|
| ----------------- in this macro invocation
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user