Skip to content

Commit

Permalink
Auto merge of #46142 - eddyb:even-mirer-2, r=nikomatsakis
Browse files Browse the repository at this point in the history
MIR: split Operand::Consume into Copy and Move.

By encoding the choice of leaving the source untouched (`Copy`) and invalidating it (`Move`) in MIR, we can express moves of copyable values and have MIR borrow-checking enforce them, *including* ownership transfer of stack locals in calls  (when the ABI passes by indirection).

Optimizations could turn a "last-use" `Copy` into a `Move`, and the MIR borrow-checker, at least within the confines of safe code, could even do this when the underlying lvalue was borrowed.
(However, that last part would be the first time lifetime inference affects code generation, AFAIK).

Furthermore, as `Move`s invalidate borrows as well, for any local that is initialized only once, we can ignore borrows that happened before a `Move` and safely reuse/replace its memory storage.
This will allow us to perform NRVO in the presence of short-lived borrows, unlike LLVM (currently), and even compute optimal `StorageLive...StorageDead` ranges instead of discarding them.
  • Loading branch information
bors committed Nov 28, 2017
2 parents 3e9a7f7 + 919ed40 commit 7745a7a
Show file tree
Hide file tree
Showing 72 changed files with 438 additions and 445 deletions.
5 changes: 4 additions & 1 deletion src/librustc/ich/impls_mir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,10 @@ impl<'gcx> HashStable<StableHashingContext<'gcx>> for mir::Operand<'gcx> {
mem::discriminant(self).hash_stable(hcx, hasher);

match *self {
mir::Operand::Consume(ref lvalue) => {
mir::Operand::Copy(ref lvalue) => {
lvalue.hash_stable(hcx, hasher);
}
mir::Operand::Move(ref lvalue) => {
lvalue.hash_stable(hcx, hasher);
}
mir::Operand::Constant(ref constant) => {
Expand Down
21 changes: 17 additions & 4 deletions src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1283,7 +1283,17 @@ pub struct VisibilityScopeData {
/// being nested in one another.
#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)]
pub enum Operand<'tcx> {
Consume(Lvalue<'tcx>),
/// Copy: The value must be available for use afterwards.
///
/// This implies that the type of the lvalue must be `Copy`; this is true
/// by construction during build, but also checked by the MIR type checker.
Copy(Lvalue<'tcx>),
/// Move: The value (including old borrows of it) will not be used again.
///
/// Safe for values of all types (modulo future developments towards `?Move`).
/// Correct usage patterns are enforced by the borrow checker for safe code.
/// `Copy` may be converted to `Move` to enable "last-use" optimizations.
Move(Lvalue<'tcx>),
Constant(Box<Constant<'tcx>>),
}

Expand All @@ -1292,7 +1302,8 @@ impl<'tcx> Debug for Operand<'tcx> {
use self::Operand::*;
match *self {
Constant(ref a) => write!(fmt, "{:?}", a),
Consume(ref lv) => write!(fmt, "{:?}", lv),
Copy(ref lv) => write!(fmt, "{:?}", lv),
Move(ref lv) => write!(fmt, "move {:?}", lv),
}
}
}
Expand Down Expand Up @@ -2089,14 +2100,16 @@ impl<'tcx> TypeFoldable<'tcx> for Rvalue<'tcx> {
impl<'tcx> TypeFoldable<'tcx> for Operand<'tcx> {
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
match *self {
Operand::Consume(ref lval) => Operand::Consume(lval.fold_with(folder)),
Operand::Copy(ref lval) => Operand::Copy(lval.fold_with(folder)),
Operand::Move(ref lval) => Operand::Move(lval.fold_with(folder)),
Operand::Constant(ref c) => Operand::Constant(c.fold_with(folder)),
}
}

fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
match *self {
Operand::Consume(ref lval) => lval.visit_with(visitor),
Operand::Copy(ref lval) |
Operand::Move(ref lval) => lval.visit_with(visitor),
Operand::Constant(ref c) => c.visit_with(visitor)
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/librustc/mir/tcx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,8 @@ impl<'tcx> Operand<'tcx> {
where D: HasLocalDecls<'tcx>
{
match self {
&Operand::Consume(ref l) => l.ty(local_decls, tcx).to_ty(tcx),
&Operand::Copy(ref l) |
&Operand::Move(ref l) => l.ty(local_decls, tcx).to_ty(tcx),
&Operand::Constant(ref c) => c.ty,
}
}
Expand Down
18 changes: 12 additions & 6 deletions src/librustc/mir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -611,8 +611,11 @@ macro_rules! make_mir_visitor {
operand: & $($mutability)* Operand<'tcx>,
location: Location) {
match *operand {
Operand::Consume(ref $($mutability)* lvalue) => {
self.visit_lvalue(lvalue, LvalueContext::Consume, location);
Operand::Copy(ref $($mutability)* lvalue) => {
self.visit_lvalue(lvalue, LvalueContext::Copy, location);
}
Operand::Move(ref $($mutability)* lvalue) => {
self.visit_lvalue(lvalue, LvalueContext::Move, location);
}
Operand::Constant(ref $($mutability)* constant) => {
self.visit_constant(constant, location);
Expand Down Expand Up @@ -679,7 +682,7 @@ macro_rules! make_mir_visitor {
self.visit_ty(ty, TyContext::Location(location));
}
ProjectionElem::Index(ref $($mutability)* local) => {
self.visit_local(local, LvalueContext::Consume, location);
self.visit_local(local, LvalueContext::Copy, location);
}
ProjectionElem::ConstantIndex { offset: _,
min_length: _,
Expand Down Expand Up @@ -860,7 +863,8 @@ pub enum LvalueContext<'tcx> {
Projection(Mutability),

// Consumed as part of an operand
Consume,
Copy,
Move,

// Starting and ending a storage live range
StorageLive,
Expand Down Expand Up @@ -913,7 +917,8 @@ impl<'tcx> LvalueContext<'tcx> {
LvalueContext::Inspect |
LvalueContext::Borrow { kind: BorrowKind::Shared, .. } |
LvalueContext::Borrow { kind: BorrowKind::Unique, .. } |
LvalueContext::Projection(Mutability::Not) | LvalueContext::Consume |
LvalueContext::Projection(Mutability::Not) |
LvalueContext::Copy | LvalueContext::Move |
LvalueContext::StorageLive | LvalueContext::StorageDead |
LvalueContext::Validate => false,
}
Expand All @@ -924,7 +929,8 @@ impl<'tcx> LvalueContext<'tcx> {
match *self {
LvalueContext::Inspect | LvalueContext::Borrow { kind: BorrowKind::Shared, .. } |
LvalueContext::Borrow { kind: BorrowKind::Unique, .. } |
LvalueContext::Projection(Mutability::Not) | LvalueContext::Consume => true,
LvalueContext::Projection(Mutability::Not) |
LvalueContext::Copy | LvalueContext::Move => true,
LvalueContext::Borrow { kind: BorrowKind::Mut, .. } | LvalueContext::Store |
LvalueContext::Call | LvalueContext::Projection(Mutability::Mut) |
LvalueContext::Drop | LvalueContext::StorageLive | LvalueContext::StorageDead |
Expand Down
Loading

0 comments on commit 7745a7a

Please sign in to comment.