From 046675d73569ab6a62f4b8b656ff5f2034107946 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 13 Jul 2017 14:05:45 -0700 Subject: [PATCH] Also release locks on ReleaseValidation and EndRegion --- src/librustc_mir/interpret/lvalue.rs | 73 ++++++++++++++++++++-------- src/librustc_mir/interpret/step.rs | 19 +++----- 2 files changed, 61 insertions(+), 31 deletions(-) diff --git a/src/librustc_mir/interpret/lvalue.rs b/src/librustc_mir/interpret/lvalue.rs index 9f344d19557..c0976e9ded9 100644 --- a/src/librustc_mir/interpret/lvalue.rs +++ b/src/librustc_mir/interpret/lvalue.rs @@ -1,5 +1,5 @@ use rustc::hir::Mutability as TyMutability; -use rustc::mir; +use rustc::mir::{self, ValidationOp}; use rustc::ty::layout::{Size, Align}; use rustc::ty::{self, Ty}; use rustc::middle::region::CodeExtent; @@ -469,6 +469,21 @@ pub(super) fn lvalue_ty(&self, lvalue: &mir::Lvalue<'tcx>) -> Ty<'tcx> { } // Validity checks +#[derive(Copy, Clone, Debug)] +pub struct ValidationCtx { + op: ValidationOp, + region: Option, + mutbl: TyMutability, +} + +impl ValidationCtx { + pub fn new(op: ValidationOp) -> Self { + ValidationCtx { + op, region: None, mutbl: TyMutability::MutMutable, + } + } +} + impl<'a, 'tcx> EvalContext<'a, 'tcx> { fn validate_variant( &mut self, @@ -476,38 +491,44 @@ fn validate_variant( ty: Ty<'tcx>, variant: &ty::VariantDef, subst: &ty::subst::Substs<'tcx>, - outer_mutbl: TyMutability + vctx: ValidationCtx, ) -> EvalResult<'tcx> { // TODO: Take visibility/privacy into account. for (idx, field) in variant.fields.iter().enumerate() { let field_ty = field.ty(self.tcx, subst); let field_lvalue = self.lvalue_field(lvalue, idx, ty, field_ty)?; - self.acquire_valid(field_lvalue, field_ty, outer_mutbl)?; + self.validate(field_lvalue, field_ty, vctx)?; } Ok(()) } - fn validate_ptr(&mut self, val: Value, region: Option, pointee_ty: Ty<'tcx>, mutbl: TyMutability) -> EvalResult<'tcx> { + fn validate_ptr(&mut self, val: Value, pointee_ty: Ty<'tcx>, vctx: ValidationCtx) -> EvalResult<'tcx> { use self::TyMutability::*; // Acquire lock let (len, _) = self.size_and_align_of_dst(pointee_ty, val)?; let ptr = val.into_ptr(&mut self.memory)?.to_ptr()?; - let access = match mutbl { MutMutable => AccessKind::Write, MutImmutable => AccessKind::Read }; - self.memory.acquire_lock(ptr, len, region, access)?; + let access = match vctx.mutbl { MutMutable => AccessKind::Write, MutImmutable => AccessKind::Read }; + match vctx.op { + ValidationOp::Acquire => self.memory.acquire_lock(ptr, len, vctx.region, access)?, + ValidationOp::Release => self.memory.release_lock_until(ptr, len, None)?, + ValidationOp::Suspend(region) => self.memory.release_lock_until(ptr, len, Some(region))?, + } // Recurse let pointee_lvalue = self.val_to_lvalue(val, pointee_ty)?; - self.acquire_valid(pointee_lvalue, pointee_ty, mutbl) + self.validate(pointee_lvalue, pointee_ty, vctx) } - pub(super) fn acquire_valid(&mut self, lvalue: Lvalue<'tcx>, ty: Ty<'tcx>, outer_mutbl: TyMutability) -> EvalResult<'tcx> { + /// Validate the lvalue at the given type. If `release` is true, just do a release of all write locks + pub(super) fn validate(&mut self, lvalue: Lvalue<'tcx>, ty: Ty<'tcx>, mut vctx: ValidationCtx) -> EvalResult<'tcx> + { use rustc::ty::TypeVariants::*; use rustc::ty::RegionKind::*; use rustc::ty::AdtKind; use self::TyMutability::*; - trace!("Validating {:?} at type {}, outer mutability {:?}", lvalue, ty, outer_mutbl); + trace!("Validating {:?} at type {}, context {:?}", lvalue, ty, vctx); match ty.sty { TyChar | TyInt(_) | TyUint(_) | TyRawPtr(_) => { // TODO: Make sure these are not undef. @@ -520,17 +541,29 @@ pub(super) fn acquire_valid(&mut self, lvalue: Lvalue<'tcx>, ty: Ty<'tcx>, outer } TyRef(region, ty::TypeAndMut { ty: pointee_ty, mutbl }) => { let val = self.read_lvalue(lvalue)?; - let combined_mutbl = match outer_mutbl { MutMutable => mutbl, MutImmutable => MutImmutable }; - let extent = match *region { - ReScope(extent) => Some(extent), - _ => None, - }; - self.validate_ptr(val, extent, pointee_ty, combined_mutbl) + // Sharing restricts our context + if mutbl == MutImmutable { + // Actually, in case of releasing-validation, this means we are done. + if vctx.op != ValidationOp::Acquire { + return Ok(()); + } + vctx.mutbl = MutImmutable; + } + // Inner lifetimes *outlive* outer ones, so only if we have no lifetime restriction yet, + // we record the region of this borrow to the context. + if vctx.region == None { + match *region { + ReScope(ce) => vctx.region = Some(ce), + // It is possible for us to encode erased lifetimes here because the lifetimes in + // this functions' Subst will be erased. + _ => {}, + } + } + self.validate_ptr(val, pointee_ty, vctx) } TyAdt(adt, _) if adt.is_box() => { let val = self.read_lvalue(lvalue)?; - // TODO: The region can't always be None. It must take outer borrows into account. - self.validate_ptr(val, None, ty.boxed_ty(), outer_mutbl) + self.validate_ptr(val, ty.boxed_ty(), vctx) } TySlice(elem_ty) => { let len = match lvalue { @@ -539,7 +572,7 @@ pub(super) fn acquire_valid(&mut self, lvalue: Lvalue<'tcx>, ty: Ty<'tcx>, outer }; for i in 0..len { let inner_lvalue = self.lvalue_index(lvalue, ty, i)?; - self.acquire_valid(inner_lvalue, elem_ty, outer_mutbl)?; + self.validate(inner_lvalue, elem_ty, vctx)?; } Ok(()) } @@ -568,14 +601,14 @@ pub(super) fn acquire_valid(&mut self, lvalue: Lvalue<'tcx>, ty: Ty<'tcx>, outer let lvalue = self.eval_lvalue_projection(lvalue, ty, &mir::ProjectionElem::Downcast(adt, variant_idx))?; // Recursively validate the fields - self.validate_variant(lvalue, ty, variant, subst, outer_mutbl) + self.validate_variant(lvalue, ty, variant, subst, vctx) } else { // No fields, nothing left to check. Downcasting may fail, e.g. in case of a CEnum. Ok(()) } } AdtKind::Struct => { - self.validate_variant(lvalue, ty, adt.struct_variant(), subst, outer_mutbl) + self.validate_variant(lvalue, ty, adt.struct_variant(), subst, vctx) } AdtKind::Union => { // No guarantees are provided for union types. diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index 9f3fae569eb..f148a096128 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -5,14 +5,14 @@ use rustc::hir::def_id::DefId; use rustc::hir; use rustc::mir::visit::{Visitor, LvalueContext}; -use rustc::mir::{self, ValidationOp}; +use rustc::mir; use rustc::traits::Reveal; use rustc::ty::layout::Layout; use rustc::ty::{subst, self}; use error::{EvalResult, EvalError}; use eval_context::{EvalContext, StackPopCleanup}; -use lvalue::{Global, GlobalId, Lvalue}; +use lvalue::{Global, GlobalId, Lvalue, ValidationCtx}; use value::{Value, PrimVal}; use syntax::codemap::Span; use syntax::ast::Mutability; @@ -132,19 +132,16 @@ fn statement(&mut self, stmt: &mir::Statement<'tcx>) -> EvalResult<'tcx> { // Validity checks. Validate(ref op, ref lvalues) => { - match *op { - ValidationOp::Acquire => { - for operand in lvalues { - let lvalue = self.eval_lvalue(&operand.lval)?; - self.acquire_valid(lvalue, operand.ty, hir::MutMutable)?; - } - } - _ => { /* not yet implemented */ } + for operand in lvalues { + let lvalue = self.eval_lvalue(&operand.lval)?; + self.validate(lvalue, operand.ty, ValidationCtx::new(*op))?; } } // Just a borrowck thing - EndRegion(..) => {} + EndRegion(ce) => { + self.memory.locks_lifetime_ended(Some(ce)); + } // Defined to do nothing. These are added by optimization passes, to avoid changing the // size of MIR constantly. -- 2.44.0