use ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
use util::ppaux;
use rustc_back::slice;
-use hir::InlineAsm;
+use hir::{self, InlineAsm};
use std::ascii;
use std::borrow::{Cow};
use std::cell::Ref;
},
/// Assert the given lvalues to be valid inhabitants of their type.
- Validate(ValidationOp, Vec<(Ty<'tcx>, Lvalue<'tcx>)>),
+ Validate(ValidationOp, Vec<ValidationOperand<'tcx>>),
/// Mark one terminating point of an extent (i.e. static region).
/// (The starting point(s) arise implicitly from borrows.)
}
}
+#[derive(Clone, RustcEncodable, RustcDecodable)]
+pub struct ValidationOperand<'tcx> {
+ pub lval: Lvalue<'tcx>,
+ pub ty: Ty<'tcx>,
+ pub re: Option<CodeExtent>,
+ pub mutbl: hir::Mutability,
+}
+
+impl<'tcx> Debug for ValidationOperand<'tcx> {
+ fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
+ write!(fmt, "{:?}@{:?}", self.lval, self.ty)?;
+ if let Some(ce) = self.re {
+ // (reuse lifetime rendering policy from ppaux.)
+ write!(fmt, "/{}", ty::ReScope(ce))?;
+ }
+ if let hir::MutImmutable = self.mutbl {
+ write!(fmt, " (imm)")?;
+ }
+ Ok(())
+ }
+}
+
impl<'tcx> Debug for Statement<'tcx> {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
use self::StatementKind::*;
}
}
+impl<'tcx> TypeFoldable<'tcx> for ValidationOperand<'tcx> {
+ fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
+ ValidationOperand {
+ lval: self.lval.fold_with(folder),
+ ty: self.ty.fold_with(folder),
+ re: self.re,
+ mutbl: self.mutbl,
+ }
+ }
+
+ fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> bool {
+ self.lval.visit_with(visitor) || self.ty.visit_with(visitor)
+ }
+}
+
impl<'tcx> TypeFoldable<'tcx> for Statement<'tcx> {
fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) -> Self {
use mir::StatementKind::*;
Validate(ref op, ref lvals) =>
Validate(op.clone(),
- lvals.iter().map(|ty_and_lval| ty_and_lval.fold_with(folder)).collect()),
+ lvals.iter().map(|operand| operand.fold_with(folder)).collect()),
Nop => Nop,
};
//! of MIR building, and only after this pass we think of the program has having the
//! normal MIR semantics.
-use rustc::ty::{TyCtxt, RegionKind};
+use rustc::ty::{self, TyCtxt, RegionKind};
+use rustc::hir;
use rustc::mir::*;
use rustc::mir::transform::{MirPass, MirSource};
+use rustc::middle::region::CodeExtent;
pub struct AddValidation;
-
-fn is_lvalue_shared<'a, 'tcx, D>(lval: &Lvalue<'tcx>, local_decls: &D, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> bool
+/// Determine the "context" of the lval: Mutability and region.
+fn lval_context<'a, 'tcx, D>(
+ lval: &Lvalue<'tcx>,
+ local_decls: &D,
+ tcx: TyCtxt<'a, 'tcx, 'tcx>
+) -> (Option<CodeExtent>, hir::Mutability)
where D: HasLocalDecls<'tcx>
{
use rustc::mir::Lvalue::*;
match *lval {
- Local { .. } => false,
- Static(_) => true,
+ Local { .. } => (None, hir::MutMutable),
+ Static(_) => (None, hir::MutImmutable),
Projection(ref proj) => {
- // If the base is shared, things stay shared
- if is_lvalue_shared(&proj.base, local_decls, tcx) {
- return true;
- }
- // A Deref projection may make things shared
match proj.elem {
ProjectionElem::Deref => {
- // Computing the inside the recursion makes this quadratic. We don't expect deep paths though.
+ // Computing the inside the recursion makes this quadratic.
+ // We don't expect deep paths though.
let ty = proj.base.ty(local_decls, tcx).to_ty(tcx);
- !ty.is_mutable_pointer()
+ // A Deref projection may restrict the context, this depends on the type
+ // being deref'd.
+ let context = match ty.sty {
+ ty::TyRef(re, tam) => {
+ let re = match re {
+ &RegionKind::ReScope(ce) => Some(ce),
+ &RegionKind::ReErased =>
+ bug!("AddValidation pass must be run before erasing lifetimes"),
+ _ => None
+ };
+ (re, tam.mutbl)
+ }
+ ty::TyRawPtr(_) =>
+ // There is no guarantee behind even a mutable raw pointer,
+ // no write locks are acquired there, so we also don't want to
+ // release any.
+ (None, hir::MutImmutable),
+ ty::TyAdt(adt, _) if adt.is_box() => (None, hir::MutMutable),
+ _ => bug!("Deref on a non-pointer type {:?}", ty),
+ };
+ // "Intersect" this restriction with proj.base.
+ if let (Some(_), hir::MutImmutable) = context {
+ // This is already as restricted as it gets, no need to even recurse
+ context
+ } else {
+ let base_context = lval_context(&proj.base, local_decls, tcx);
+ // The region of the outermost Deref is always most restrictive.
+ let re = context.0.or(base_context.0);
+ let mutbl = context.1.and(base_context.1);
+ (re, mutbl)
+ }
+
}
- _ => false,
+ _ => lval_context(&proj.base, local_decls, tcx),
}
}
}
tcx: TyCtxt<'a, 'tcx, 'tcx>,
_: MirSource,
mir: &mut Mir<'tcx>) {
+ let local_decls = mir.local_decls.clone(); // TODO: Find a way to get rid of this clone.
+
+ /// Convert an lvalue to a validation operand.
+ let lval_to_operand = |lval: Lvalue<'tcx>| -> ValidationOperand<'tcx> {
+ let (re, mutbl) = lval_context(&lval, &local_decls, tcx);
+ let ty = lval.ty(&local_decls, tcx).to_ty(tcx);
+ ValidationOperand { lval, ty, re, mutbl }
+ };
+
// PART 1
// Add an AcquireValid at the beginning of the start block.
if mir.arg_count > 0 {
let acquire_stmt = Statement {
source_info: SourceInfo {
scope: ARGUMENT_VISIBILITY_SCOPE,
- span: mir.span, // TODO: Consider using just the span covering the function argument declaration
+ span: mir.span, // TODO: Consider using just the span covering the function
+ // argument declaration.
},
kind: StatementKind::Validate(ValidationOp::Acquire,
// Skip return value, go over all the arguments
mir.local_decls.iter_enumerated().skip(1).take(mir.arg_count)
- .map(|(local, local_decl)| (local_decl.ty, Lvalue::Local(local))).collect()
+ .map(|(local, _)| lval_to_operand(Lvalue::Local(local))).collect()
)
};
mir.basic_blocks_mut()[START_BLOCK].statements.insert(0, acquire_stmt);
}
// PART 2
- // Add ReleaseValid/AcquireValid around function call terminators. We don't use a visitor because
- // we need to access the block that a Call jumps to.
- let mut returns : Vec<(SourceInfo, Lvalue<'tcx>, BasicBlock)> = Vec::new(); // Here we collect the destinations.
- let local_decls = mir.local_decls.clone(); // TODO: Find a way to get rid of this clone.
+ // Add ReleaseValid/AcquireValid around function call terminators. We don't use a visitor
+ // because we need to access the block that a Call jumps to.
+ let mut returns : Vec<(SourceInfo, Lvalue<'tcx>, BasicBlock)> = Vec::new();
for block_data in mir.basic_blocks_mut() {
match block_data.terminator {
- Some(Terminator { kind: TerminatorKind::Call { ref args, ref destination, .. }, source_info }) => {
+ Some(Terminator { kind: TerminatorKind::Call { ref args, ref destination, .. },
+ source_info }) => {
// Before the call: Release all arguments
let release_stmt = Statement {
source_info,
kind: StatementKind::Validate(ValidationOp::Release,
args.iter().filter_map(|op| {
match op {
- &Operand::Consume(ref lval) => {
- let ty = lval.ty(&local_decls, tcx).to_ty(tcx);
- Some((ty, lval.clone()))
- },
+ &Operand::Consume(ref lval) =>
+ Some(lval_to_operand(lval.clone())),
&Operand::Constant(..) => { None },
}
}).collect())
returns.push((source_info, destination.0.clone(), destination.1));
}
}
- Some(Terminator { kind: TerminatorKind::Drop { location: ref lval, .. }, source_info }) |
- Some(Terminator { kind: TerminatorKind::DropAndReplace { location: ref lval, .. }, source_info }) => {
+ Some(Terminator { kind: TerminatorKind::Drop { location: ref lval, .. },
+ source_info }) |
+ Some(Terminator { kind: TerminatorKind::DropAndReplace { location: ref lval, .. },
+ source_info }) => {
// Before the call: Release all arguments
- let ty = lval.ty(&local_decls, tcx).to_ty(tcx);
let release_stmt = Statement {
source_info,
- kind: StatementKind::Validate(ValidationOp::Release, vec![(ty, lval.clone())])
+ kind: StatementKind::Validate(ValidationOp::Release,
+ vec![lval_to_operand(lval.clone())]),
};
block_data.statements.push(release_stmt);
// drop doesn't return anything, so we need no acquire.
}
// Now we go over the returns we collected to acquire the return values.
for (source_info, dest_lval, dest_block) in returns {
- let ty = dest_lval.ty(&local_decls, tcx).to_ty(tcx);
let acquire_stmt = Statement {
source_info,
- kind: StatementKind::Validate(ValidationOp::Acquire, vec![(ty, dest_lval)])
+ kind: StatementKind::Validate(ValidationOp::Acquire,
+ vec![lval_to_operand(dest_lval)]),
};
mir.basic_blocks_mut()[dest_block].statements.insert(0, acquire_stmt);
}
// PART 3
- // Add ReleaseValid/AcquireValid around Ref. Again an iterator does not seem very suited as
- // we need to add new statements before and after each Ref.
+ // Add ReleaseValid/AcquireValid around Ref. Again an iterator does not seem very suited
+ // as we need to add new statements before and after each Ref.
for block_data in mir.basic_blocks_mut() {
- // We want to insert statements around Ref commands as we iterate. To this end, we iterate backwards
- // using indices.
+ // We want to insert statements around Ref commands as we iterate. To this end, we
+ // iterate backwards using indices.
for i in (0..block_data.statements.len()).rev() {
let (dest_lval, re, src_lval) = match block_data.statements[i].kind {
StatementKind::Assign(ref dest_lval, Rvalue::Ref(re, _, ref src_lval)) => {
_ => continue,
};
// So this is a ref, and we got all the data we wanted.
- let dest_ty = dest_lval.ty(&local_decls, tcx).to_ty(tcx);
let acquire_stmt = Statement {
source_info: block_data.statements[i].source_info,
- kind: StatementKind::Validate(ValidationOp::Acquire, vec![(dest_ty, dest_lval)]),
+ kind: StatementKind::Validate(ValidationOp::Acquire,
+ vec![lval_to_operand(dest_lval)]),
};
block_data.statements.insert(i+1, acquire_stmt);
- // The source is released until the region of the borrow ends -- but not if it is shared.
- if !is_lvalue_shared(&src_lval, &local_decls, tcx) {
- let src_ty = src_lval.ty(&local_decls, tcx).to_ty(tcx);
- let op = match re {
- &RegionKind::ReScope(ce) => ValidationOp::Suspend(ce),
- &RegionKind::ReErased => bug!("AddValidation pass must be run before erasing lifetimes"),
- _ => ValidationOp::Release,
- };
- let release_stmt = Statement {
- source_info: block_data.statements[i].source_info,
- kind: StatementKind::Validate(op, vec![(src_ty, src_lval)]),
- };
- block_data.statements.insert(i, release_stmt);
- }
+ // The source is released until the region of the borrow ends.
+ let op = match re {
+ &RegionKind::ReScope(ce) => ValidationOp::Suspend(ce),
+ &RegionKind::ReErased =>
+ bug!("AddValidation pass must be run before erasing lifetimes"),
+ _ => ValidationOp::Release,
+ };
+ let release_stmt = Statement {
+ source_info: block_data.statements[i].source_info,
+ kind: StatementKind::Validate(op, vec![lval_to_operand(src_lval)]),
+ };
+ block_data.statements.insert(i, release_stmt);
}
}
}