2017-07-11 14:10:38 -07:00
|
|
|
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
//! This pass adds validation calls (AcquireValid, ReleaseValid) where appropriate.
|
|
|
|
//! It has to be run really early, before transformations like inlining, because
|
|
|
|
//! introducing these calls *adds* UB -- so, conceptually, this pass is actually part
|
|
|
|
//! of MIR building, and only after this pass we think of the program has having the
|
|
|
|
//! normal MIR semantics.
|
|
|
|
|
2017-07-21 12:43:09 -07:00
|
|
|
use rustc::ty::{self, TyCtxt, RegionKind};
|
|
|
|
use rustc::hir;
|
2017-07-11 14:10:38 -07:00
|
|
|
use rustc::mir::*;
|
|
|
|
use rustc::mir::transform::{MirPass, MirSource};
|
2017-07-21 12:43:09 -07:00
|
|
|
use rustc::middle::region::CodeExtent;
|
2017-07-11 14:10:38 -07:00
|
|
|
|
|
|
|
pub struct AddValidation;
|
|
|
|
|
2017-07-21 12:43:09 -07:00
|
|
|
/// 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)
|
2017-07-20 15:27:03 -07:00
|
|
|
where D: HasLocalDecls<'tcx>
|
|
|
|
{
|
|
|
|
use rustc::mir::Lvalue::*;
|
|
|
|
|
|
|
|
match *lval {
|
2017-07-21 12:43:09 -07:00
|
|
|
Local { .. } => (None, hir::MutMutable),
|
|
|
|
Static(_) => (None, hir::MutImmutable),
|
2017-07-20 15:27:03 -07:00
|
|
|
Projection(ref proj) => {
|
|
|
|
match proj.elem {
|
|
|
|
ProjectionElem::Deref => {
|
2017-07-21 12:43:09 -07:00
|
|
|
// Computing the inside the recursion makes this quadratic.
|
|
|
|
// We don't expect deep paths though.
|
2017-07-20 15:27:03 -07:00
|
|
|
let ty = proj.base.ty(local_decls, tcx).to_ty(tcx);
|
2017-07-21 12:43:09 -07:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
|
2017-07-20 15:27:03 -07:00
|
|
|
}
|
2017-07-21 12:43:09 -07:00
|
|
|
_ => lval_context(&proj.base, local_decls, tcx),
|
2017-07-20 15:27:03 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-11 14:10:38 -07:00
|
|
|
impl MirPass for AddValidation {
|
|
|
|
fn run_pass<'a, 'tcx>(&self,
|
2017-07-11 16:31:30 -07:00
|
|
|
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
2017-07-11 14:10:38 -07:00
|
|
|
_: MirSource,
|
|
|
|
mir: &mut Mir<'tcx>) {
|
2017-07-21 23:18:34 -07:00
|
|
|
if !tcx.sess.opts.debugging_opts.mir_emit_validate {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-07-22 01:04:16 -07:00
|
|
|
let local_decls = mir.local_decls.clone(); // FIXME: Find a way to get rid of this clone.
|
2017-07-21 12:43:09 -07:00
|
|
|
|
|
|
|
/// Convert an lvalue to a validation operand.
|
2017-07-21 14:49:01 -07:00
|
|
|
let lval_to_operand = |lval: Lvalue<'tcx>| -> ValidationOperand<'tcx, Lvalue<'tcx>> {
|
2017-07-21 12:43:09 -07:00
|
|
|
let (re, mutbl) = lval_context(&lval, &local_decls, tcx);
|
|
|
|
let ty = lval.ty(&local_decls, tcx).to_ty(tcx);
|
|
|
|
ValidationOperand { lval, ty, re, mutbl }
|
|
|
|
};
|
|
|
|
|
2017-07-11 16:31:30 -07:00
|
|
|
// PART 1
|
|
|
|
// Add an AcquireValid at the beginning of the start block.
|
2017-07-11 14:10:38 -07:00
|
|
|
if mir.arg_count > 0 {
|
|
|
|
let acquire_stmt = Statement {
|
|
|
|
source_info: SourceInfo {
|
|
|
|
scope: ARGUMENT_VISIBILITY_SCOPE,
|
2017-07-22 01:04:16 -07:00
|
|
|
span: mir.span, // FIXME: Consider using just the span covering the function
|
2017-07-21 12:43:09 -07:00
|
|
|
// argument declaration.
|
2017-07-11 14:10:38 -07:00
|
|
|
},
|
|
|
|
kind: StatementKind::Validate(ValidationOp::Acquire,
|
|
|
|
// Skip return value, go over all the arguments
|
|
|
|
mir.local_decls.iter_enumerated().skip(1).take(mir.arg_count)
|
2017-07-21 12:43:09 -07:00
|
|
|
.map(|(local, _)| lval_to_operand(Lvalue::Local(local))).collect()
|
2017-07-11 16:31:30 -07:00
|
|
|
)
|
2017-07-11 14:10:38 -07:00
|
|
|
};
|
|
|
|
mir.basic_blocks_mut()[START_BLOCK].statements.insert(0, acquire_stmt);
|
|
|
|
}
|
2017-07-11 16:31:30 -07:00
|
|
|
|
|
|
|
// PART 2
|
2017-07-21 12:43:09 -07:00
|
|
|
// 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();
|
2017-07-11 16:31:30 -07:00
|
|
|
for block_data in mir.basic_blocks_mut() {
|
|
|
|
match block_data.terminator {
|
2017-07-21 12:43:09 -07:00
|
|
|
Some(Terminator { kind: TerminatorKind::Call { ref args, ref destination, .. },
|
|
|
|
source_info }) => {
|
2017-07-11 16:31:30 -07:00
|
|
|
// Before the call: Release all arguments
|
|
|
|
let release_stmt = Statement {
|
|
|
|
source_info,
|
|
|
|
kind: StatementKind::Validate(ValidationOp::Release,
|
|
|
|
args.iter().filter_map(|op| {
|
|
|
|
match op {
|
2017-07-21 12:43:09 -07:00
|
|
|
&Operand::Consume(ref lval) =>
|
|
|
|
Some(lval_to_operand(lval.clone())),
|
2017-07-11 16:31:30 -07:00
|
|
|
&Operand::Constant(..) => { None },
|
|
|
|
}
|
|
|
|
}).collect())
|
|
|
|
};
|
|
|
|
block_data.statements.push(release_stmt);
|
|
|
|
// Remember the return destination for later
|
|
|
|
if let &Some(ref destination) = destination {
|
|
|
|
returns.push((source_info, destination.0.clone(), destination.1));
|
|
|
|
}
|
|
|
|
}
|
2017-07-21 12:43:09 -07:00
|
|
|
Some(Terminator { kind: TerminatorKind::Drop { location: ref lval, .. },
|
|
|
|
source_info }) |
|
|
|
|
Some(Terminator { kind: TerminatorKind::DropAndReplace { location: ref lval, .. },
|
|
|
|
source_info }) => {
|
2017-07-13 22:10:14 -07:00
|
|
|
// Before the call: Release all arguments
|
|
|
|
let release_stmt = Statement {
|
|
|
|
source_info,
|
2017-07-21 12:43:09 -07:00
|
|
|
kind: StatementKind::Validate(ValidationOp::Release,
|
|
|
|
vec![lval_to_operand(lval.clone())]),
|
2017-07-13 22:10:14 -07:00
|
|
|
};
|
|
|
|
block_data.statements.push(release_stmt);
|
|
|
|
// drop doesn't return anything, so we need no acquire.
|
|
|
|
}
|
2017-07-11 16:31:30 -07:00
|
|
|
_ => {
|
|
|
|
// Not a block ending in a Call -> ignore.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Now we go over the returns we collected to acquire the return values.
|
|
|
|
for (source_info, dest_lval, dest_block) in returns {
|
|
|
|
let acquire_stmt = Statement {
|
|
|
|
source_info,
|
2017-07-21 12:43:09 -07:00
|
|
|
kind: StatementKind::Validate(ValidationOp::Acquire,
|
|
|
|
vec![lval_to_operand(dest_lval)]),
|
2017-07-11 16:31:30 -07:00
|
|
|
};
|
|
|
|
mir.basic_blocks_mut()[dest_block].statements.insert(0, acquire_stmt);
|
|
|
|
}
|
|
|
|
|
|
|
|
// PART 3
|
2017-07-21 12:43:09 -07:00
|
|
|
// 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.
|
2017-07-11 16:31:30 -07:00
|
|
|
for block_data in mir.basic_blocks_mut() {
|
2017-07-21 12:43:09 -07:00
|
|
|
// We want to insert statements around Ref commands as we iterate. To this end, we
|
|
|
|
// iterate backwards using indices.
|
2017-07-11 16:31:30 -07:00
|
|
|
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)) => {
|
|
|
|
(dest_lval.clone(), re, src_lval.clone())
|
|
|
|
},
|
|
|
|
_ => continue,
|
|
|
|
};
|
|
|
|
// So this is a ref, and we got all the data we wanted.
|
2017-07-24 19:19:39 -07:00
|
|
|
// Do an acquire of the result -- but only what it points to, so add a Deref
|
|
|
|
// projection.
|
|
|
|
let dest_lval = Projection { base: dest_lval, elem: ProjectionElem::Deref };
|
|
|
|
let dest_lval = Lvalue::Projection(Box::new(dest_lval));
|
2017-07-11 16:31:30 -07:00
|
|
|
let acquire_stmt = Statement {
|
|
|
|
source_info: block_data.statements[i].source_info,
|
2017-07-21 12:43:09 -07:00
|
|
|
kind: StatementKind::Validate(ValidationOp::Acquire,
|
|
|
|
vec![lval_to_operand(dest_lval)]),
|
2017-07-11 16:31:30 -07:00
|
|
|
};
|
|
|
|
block_data.statements.insert(i+1, acquire_stmt);
|
|
|
|
|
2017-07-21 12:43:09 -07:00
|
|
|
// 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);
|
2017-07-11 16:31:30 -07:00
|
|
|
}
|
|
|
|
}
|
2017-07-11 14:10:38 -07:00
|
|
|
}
|
|
|
|
}
|