Auto merge of #65608 - matthewjasper:mir-eval-order, r=pnkfelix

Fix MIR lowering evaluation order and soundness bug

* Fixes a soundness issue with built-in index operations
* Ensures correct evaluation order of assignment expressions where the RHS is a FRU or is a use of a local of reference type.
* Removes an unnecessary symbol to string conversion

closes #65909
closes #65910
This commit is contained in:
bors 2019-11-12 18:02:54 +00:00
commit 4f03f4a989
16 changed files with 687 additions and 223 deletions

View file

@ -1665,6 +1665,15 @@ pub enum FakeReadCause {
/// Therefore, we insert a "fake read" here to ensure that we get
/// appropriate errors.
ForLet,
/// If we have an index expression like
///
/// (*x)[1][{ x = y; 4}]
///
/// then the first bounds check is invalidated when we evaluate the second
/// index expression. Thus we create a fake borrow of `x` across the second
/// indexer, which will cause a borrow check error.
ForIndex,
}
#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
@ -1764,9 +1773,8 @@ impl_stable_hash_for!(struct Static<'tcx> {
def_id
});
#[derive(
Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable, HashStable,
)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(RustcEncodable, RustcDecodable, HashStable)]
pub enum ProjectionElem<V, T> {
Deref,
Field(Field, T),

View file

@ -2,9 +2,9 @@ use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::hir::{AsyncGeneratorKind, GeneratorKind};
use rustc::mir::{
self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory, Local,
LocalDecl, LocalKind, Location, Operand, Place, PlaceBase, PlaceRef, ProjectionElem, Rvalue,
Statement, StatementKind, TerminatorKind, VarBindingForm,
self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
FakeReadCause, Local, LocalDecl, LocalKind, Location, Operand, Place, PlaceBase, PlaceRef,
ProjectionElem, Rvalue, Statement, StatementKind, TerminatorKind, VarBindingForm,
};
use rustc::ty::{self, Ty};
use rustc_data_structures::fx::FxHashSet;
@ -380,42 +380,38 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let first_borrow_desc;
let mut err = match (
gen_borrow_kind,
"immutable",
"mutable",
issued_borrow.kind,
"immutable",
"mutable",
) {
(BorrowKind::Shared, lft, _, BorrowKind::Mut { .. }, _, rgt) => {
(BorrowKind::Shared, BorrowKind::Mut { .. }) => {
first_borrow_desc = "mutable ";
self.cannot_reborrow_already_borrowed(
span,
&desc_place,
&msg_place,
lft,
"immutable",
issued_span,
"it",
rgt,
"mutable",
&msg_borrow,
None,
)
}
(BorrowKind::Mut { .. }, _, lft, BorrowKind::Shared, rgt, _) => {
(BorrowKind::Mut { .. }, BorrowKind::Shared) => {
first_borrow_desc = "immutable ";
self.cannot_reborrow_already_borrowed(
span,
&desc_place,
&msg_place,
lft,
"mutable",
issued_span,
"it",
rgt,
"immutable",
&msg_borrow,
None,
)
}
(BorrowKind::Mut { .. }, _, _, BorrowKind::Mut { .. }, _, _) => {
(BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
first_borrow_desc = "first ";
self.cannot_mutably_borrow_multiply(
span,
@ -427,7 +423,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
)
}
(BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => {
(BorrowKind::Unique, BorrowKind::Unique) => {
first_borrow_desc = "first ";
self.cannot_uniquely_borrow_by_two_closures(
span,
@ -437,25 +433,45 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
)
}
(BorrowKind::Mut { .. }, _, _, BorrowKind::Shallow, _, _)
| (BorrowKind::Unique, _, _, BorrowKind::Shallow, _, _) => {
let mut err = self.cannot_mutate_in_match_guard(
span,
issued_span,
&desc_place,
"mutably borrow",
);
borrow_spans.var_span_label(
&mut err,
format!(
"borrow occurs due to use of `{}`{}", desc_place, borrow_spans.describe()
),
);
(BorrowKind::Mut { .. }, BorrowKind::Shallow)
| (BorrowKind::Unique, BorrowKind::Shallow) => {
if let Some(immutable_section_description) = self.classify_immutable_section(
&issued_borrow.assigned_place,
) {
let mut err = self.cannot_mutate_in_immutable_section(
span,
issued_span,
&desc_place,
immutable_section_description,
"mutably borrow",
);
borrow_spans.var_span_label(
&mut err,
format!(
"borrow occurs due to use of `{}`{}",
desc_place,
borrow_spans.describe(),
),
);
return err;
return err;
} else {
first_borrow_desc = "immutable ";
self.cannot_reborrow_already_borrowed(
span,
&desc_place,
&msg_place,
"mutable",
issued_span,
"it",
"immutable",
&msg_borrow,
None,
)
}
}
(BorrowKind::Unique, _, _, _, _, _) => {
(BorrowKind::Unique, _) => {
first_borrow_desc = "first ";
self.cannot_uniquely_borrow_by_one_closure(
span,
@ -469,14 +485,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
)
},
(BorrowKind::Shared, lft, _, BorrowKind::Unique, _, _) => {
(BorrowKind::Shared, BorrowKind::Unique) => {
first_borrow_desc = "first ";
self.cannot_reborrow_already_uniquely_borrowed(
span,
container_name,
&desc_place,
"",
lft,
"immutable",
issued_span,
"",
None,
@ -484,14 +500,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
)
}
(BorrowKind::Mut { .. }, _, lft, BorrowKind::Unique, _, _) => {
(BorrowKind::Mut { .. }, BorrowKind::Unique) => {
first_borrow_desc = "first ";
self.cannot_reborrow_already_uniquely_borrowed(
span,
container_name,
&desc_place,
"",
lft,
"mutable",
issued_span,
"",
None,
@ -499,12 +515,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
)
}
(BorrowKind::Shared, _, _, BorrowKind::Shared, _, _)
| (BorrowKind::Shared, _, _, BorrowKind::Shallow, _, _)
| (BorrowKind::Shallow, _, _, BorrowKind::Mut { .. }, _, _)
| (BorrowKind::Shallow, _, _, BorrowKind::Unique, _, _)
| (BorrowKind::Shallow, _, _, BorrowKind::Shared, _, _)
| (BorrowKind::Shallow, _, _, BorrowKind::Shallow, _, _) => unreachable!(),
(BorrowKind::Shared, BorrowKind::Shared)
| (BorrowKind::Shared, BorrowKind::Shallow)
| (BorrowKind::Shallow, BorrowKind::Mut { .. })
| (BorrowKind::Shallow, BorrowKind::Unique)
| (BorrowKind::Shallow, BorrowKind::Shared)
| (BorrowKind::Shallow, BorrowKind::Shallow) => unreachable!(),
};
if issued_spans == borrow_spans {
@ -1429,20 +1445,23 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
let loan_span = loan_spans.args_or_use();
if loan.kind == BorrowKind::Shallow {
let mut err = self.cannot_mutate_in_match_guard(
span,
loan_span,
&self.describe_place(place.as_ref()).unwrap_or_else(|| "_".to_owned()),
"assign",
);
loan_spans.var_span_label(
&mut err,
format!("borrow occurs due to use{}", loan_spans.describe()),
);
if let Some(section) = self.classify_immutable_section(&loan.assigned_place) {
let mut err = self.cannot_mutate_in_immutable_section(
span,
loan_span,
&self.describe_place(place.as_ref()).unwrap_or_else(|| "_".to_owned()),
section,
"assign",
);
loan_spans.var_span_label(
&mut err,
format!("borrow occurs due to use{}", loan_spans.describe()),
);
err.buffer(&mut self.errors_buffer);
err.buffer(&mut self.errors_buffer);
return;
return;
}
}
let mut err = self.cannot_assign_to_borrowed(
@ -1593,6 +1612,35 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
}
}
/// Describe the reason for the fake borrow that was assigned to `place`.
fn classify_immutable_section(&self, place: &Place<'tcx>) -> Option<&'static str> {
use rustc::mir::visit::Visitor;
struct FakeReadCauseFinder<'a, 'tcx> {
place: &'a Place<'tcx>,
cause: Option<FakeReadCause>,
}
impl<'tcx> Visitor<'tcx> for FakeReadCauseFinder<'_, 'tcx> {
fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
match statement {
Statement {
kind: StatementKind::FakeRead(cause, box ref place),
..
} if *place == *self.place => {
self.cause = Some(*cause);
}
_ => (),
}
}
}
let mut visitor = FakeReadCauseFinder { place, cause: None };
visitor.visit_body(&self.body);
match visitor.cause {
Some(FakeReadCause::ForMatchGuard) => Some("match guard"),
Some(FakeReadCause::ForIndex) => Some("indexing expression"),
_ => None,
}
}
/// Annotate argument and return type of function and closure with (synthesized) lifetime for
/// borrow of local value that does not live long enough.
fn annotate_argument_and_return_for_borrow(

View file

@ -4,9 +4,11 @@ use crate::build::expr::category::Category;
use crate::build::ForGuard::{OutsideGuard, RefWithinGuard};
use crate::build::{BlockAnd, BlockAndExtension, Builder};
use crate::hair::*;
use rustc::middle::region;
use rustc::mir::interpret::{PanicInfo::BoundsCheck};
use rustc::mir::*;
use rustc::ty::{CanonicalUserTypeAnnotation, Ty, TyCtxt, Variance};
use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, Variance};
use syntax_pos::Span;
use rustc_index::vec::Idx;
@ -68,6 +70,17 @@ impl From<PlaceBase<'tcx>> for PlaceBuilder<'tcx> {
impl<'a, 'tcx> Builder<'a, 'tcx> {
/// Compile `expr`, yielding a place that we can move from etc.
///
/// WARNING: Any user code might:
/// * Invalidate any slice bounds checks performed.
/// * Change the address that this `Place` refers to.
/// * Modify the memory that this place refers to.
/// * Invalidate the memory that this place refers to, this will be caught
/// by borrow checking.
///
/// Extra care is needed if any user code is allowed to run between calling
/// this method and using it, as is the case for `match` and index
/// expressions.
pub fn as_place<M>(&mut self, mut block: BasicBlock, expr: M) -> BlockAnd<Place<'tcx>>
where
M: Mirror<'tcx, Output = Expr<'tcx>>,
@ -83,7 +96,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
M: Mirror<'tcx, Output = Expr<'tcx>>,
{
let expr = self.hir.mirror(expr);
self.expr_as_place(block, expr, Mutability::Mut)
self.expr_as_place(block, expr, Mutability::Mut, None)
}
/// Compile `expr`, yielding a place that we can move from etc.
@ -114,7 +127,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
M: Mirror<'tcx, Output = Expr<'tcx>>,
{
let expr = self.hir.mirror(expr);
self.expr_as_place(block, expr, Mutability::Not)
self.expr_as_place(block, expr, Mutability::Not, None)
}
fn expr_as_place(
@ -122,6 +135,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
mut block: BasicBlock,
expr: Expr<'tcx>,
mutability: Mutability,
fake_borrow_temps: Option<&mut Vec<Local>>,
) -> BlockAnd<PlaceBuilder<'tcx>> {
debug!(
"expr_as_place(block={:?}, expr={:?}, mutability={:?})",
@ -137,63 +151,40 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
lint_level,
value,
} => this.in_scope((region_scope, source_info), lint_level, |this| {
if mutability == Mutability::Not {
this.as_read_only_place_builder(block, value)
} else {
this.as_place_builder(block, value)
}
let value = this.hir.mirror(value);
this.expr_as_place(block, value, mutability, fake_borrow_temps)
}),
ExprKind::Field { lhs, name } => {
let place_builder = unpack!(block = this.as_place_builder(block, lhs));
let lhs = this.hir.mirror(lhs);
let place_builder = unpack!(block = this.expr_as_place(
block,
lhs,
mutability,
fake_borrow_temps,
));
block.and(place_builder.field(name, expr.ty))
}
ExprKind::Deref { arg } => {
let place_builder = unpack!(block = this.as_place_builder(block, arg));
let arg = this.hir.mirror(arg);
let place_builder = unpack!(block = this.expr_as_place(
block,
arg,
mutability,
fake_borrow_temps,
));
block.and(place_builder.deref())
}
ExprKind::Index { lhs, index } => {
let (usize_ty, bool_ty) = (this.hir.usize_ty(), this.hir.bool_ty());
let place_builder = unpack!(block = this.as_place_builder(block, lhs));
// Making this a *fresh* temporary also means we do not have to worry about
// the index changing later: Nothing will ever change this temporary.
// The "retagging" transformation (for Stacked Borrows) relies on this.
let idx = unpack!(block = this.as_temp(
this.lower_index_expression(
block,
expr.temp_lifetime,
lhs,
index,
Mutability::Not,
));
let slice = place_builder.clone().into_place(this.hir.tcx());
// bounds check:
let (len, lt) = (
this.temp(usize_ty.clone(), expr_span),
this.temp(bool_ty, expr_span),
);
this.cfg.push_assign(
block,
source_info, // len = len(slice)
&len,
Rvalue::Len(slice),
);
this.cfg.push_assign(
block,
source_info, // lt = idx < len
&lt,
Rvalue::BinaryOp(
BinOp::Lt,
Operand::Copy(Place::from(idx)),
Operand::Copy(len.clone()),
),
);
let msg = BoundsCheck {
len: Operand::Move(len),
index: Operand::Copy(Place::from(idx)),
};
let success = this.assert(block, Operand::Move(lt), true, msg, expr_span);
success.and(place_builder.index(idx))
mutability,
fake_borrow_temps,
expr.temp_lifetime,
expr_span,
source_info,
)
}
ExprKind::SelfRef => block.and(PlaceBuilder::from(Local::new(1))),
ExprKind::VarRef { id } => {
@ -215,7 +206,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
)),
ExprKind::PlaceTypeAscription { source, user_ty } => {
let place_builder = unpack!(block = this.as_place_builder(block, source));
let source = this.hir.mirror(source);
let place_builder = unpack!(block = this.expr_as_place(
block,
source,
mutability,
fake_borrow_temps,
));
if let Some(user_ty) = user_ty {
let annotation_index = this.canonical_user_type_annotations.push(
CanonicalUserTypeAnnotation {
@ -309,4 +306,208 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}
}
/// Lower an index expression
///
/// This has two complications;
///
/// * We need to do a bounds check.
/// * We need to ensure that the bounds check can't be invalidated using an
/// expression like `x[1][{x = y; 2}]`. We use fake borrows here to ensure
/// that this is the case.
fn lower_index_expression(
&mut self,
mut block: BasicBlock,
base: ExprRef<'tcx>,
index: ExprRef<'tcx>,
mutability: Mutability,
fake_borrow_temps: Option<&mut Vec<Local>>,
temp_lifetime: Option<region::Scope>,
expr_span: Span,
source_info: SourceInfo
) -> BlockAnd<PlaceBuilder<'tcx>> {
let lhs = self.hir.mirror(base);
let base_fake_borrow_temps = &mut Vec::new();
let is_outermost_index = fake_borrow_temps.is_none();
let fake_borrow_temps = fake_borrow_temps.unwrap_or(base_fake_borrow_temps);
let base_place = unpack!(block = self.expr_as_place(
block,
lhs,
mutability,
Some(fake_borrow_temps),
));
// Making this a *fresh* temporary means we do not have to worry about
// the index changing later: Nothing will ever change this temporary.
// The "retagging" transformation (for Stacked Borrows) relies on this.
let idx = unpack!(block = self.as_temp(
block,
temp_lifetime,
index,
Mutability::Not,
));
block = self.bounds_check(
block,
base_place.clone().into_place(self.hir.tcx()),
idx,
expr_span,
source_info,
);
if is_outermost_index {
self.read_fake_borrows(block, fake_borrow_temps, source_info)
} else {
self.add_fake_borrows_of_base(
&base_place,
block,
fake_borrow_temps,
expr_span,
source_info,
);
}
block.and(base_place.index(idx))
}
fn bounds_check(
&mut self,
block: BasicBlock,
slice: Place<'tcx>,
index: Local,
expr_span: Span,
source_info: SourceInfo,
) -> BasicBlock {
let usize_ty = self.hir.usize_ty();
let bool_ty = self.hir.bool_ty();
// bounds check:
let len = self.temp(usize_ty, expr_span);
let lt = self.temp(bool_ty, expr_span);
// len = len(slice)
self.cfg.push_assign(
block,
source_info,
&len,
Rvalue::Len(slice),
);
// lt = idx < len
self.cfg.push_assign(
block,
source_info,
&lt,
Rvalue::BinaryOp(
BinOp::Lt,
Operand::Copy(Place::from(index)),
Operand::Copy(len.clone()),
),
);
let msg = BoundsCheck {
len: Operand::Move(len),
index: Operand::Copy(Place::from(index)),
};
// assert!(lt, "...")
self.assert(block, Operand::Move(lt), true, msg, expr_span)
}
fn add_fake_borrows_of_base(
&mut self,
base_place: &PlaceBuilder<'tcx>,
block: BasicBlock,
fake_borrow_temps: &mut Vec<Local>,
expr_span: Span,
source_info: SourceInfo,
) {
let tcx = self.hir.tcx();
let place_ty = Place::ty_from(
&base_place.base,
&base_place.projection,
&self.local_decls,
tcx,
);
if let ty::Slice(_) = place_ty.ty.kind {
// We need to create fake borrows to ensure that the bounds
// check that we just did stays valid. Since we can't assign to
// unsized values, we only need to ensure that none of the
// pointers in the base place are modified.
for (idx, elem) in base_place.projection.iter().enumerate().rev() {
match elem {
ProjectionElem::Deref => {
let fake_borrow_deref_ty = Place::ty_from(
&base_place.base,
&base_place.projection[..idx],
&self.local_decls,
tcx,
).ty;
let fake_borrow_ty = tcx.mk_imm_ref(
tcx.lifetimes.re_erased,
fake_borrow_deref_ty,
);
let fake_borrow_temp = self.local_decls.push(
LocalDecl::new_temp(fake_borrow_ty, expr_span)
);
let projection = tcx.intern_place_elems(&base_place.projection[..idx]);
self.cfg.push_assign(
block,
source_info,
&fake_borrow_temp.into(),
Rvalue::Ref(
tcx.lifetimes.re_erased,
BorrowKind::Shallow,
Place {
base: base_place.base.clone(),
projection,
}
),
);
fake_borrow_temps.push(fake_borrow_temp);
}
ProjectionElem::Index(_) => {
let index_ty = Place::ty_from(
&base_place.base,
&base_place.projection[..idx],
&self.local_decls,
tcx,
);
match index_ty.ty.kind {
// The previous index expression has already
// done any index expressions needed here.
ty::Slice(_) => break,
ty::Array(..) => (),
_ => bug!("unexpected index base"),
}
}
ProjectionElem::Field(..)
| ProjectionElem::Downcast(..)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } => (),
}
}
}
}
fn read_fake_borrows(
&mut self,
block: BasicBlock,
fake_borrow_temps: &mut Vec<Local>,
source_info: SourceInfo,
) {
// All indexes have been evaluated now, read all of the
// fake borrows so that they are live across those index
// expressions.
for temp in fake_borrow_temps {
self.cfg.push(
block,
Statement {
source_info,
kind: StatementKind::FakeRead(
FakeReadCause::ForIndex,
Box::new(Place::from(*temp)),
)
}
);
}
}
}

View file

@ -1,6 +1,5 @@
//! See docs in `build/expr/mod.rs`.
use rustc_data_structures::fx::FxHashMap;
use rustc_index::vec::Idx;
use crate::build::expr::category::{Category, RvalueFunc};
@ -9,11 +8,16 @@ use crate::hair::*;
use rustc::middle::region;
use rustc::mir::interpret::PanicInfo;
use rustc::mir::*;
use rustc::ty::{self, CanonicalUserTypeAnnotation, Ty, UpvarSubsts};
use rustc::ty::{self, Ty, UpvarSubsts};
use syntax_pos::Span;
impl<'a, 'tcx> Builder<'a, 'tcx> {
/// See comment on `as_local_operand`
/// Returns an rvalue suitable for use until the end of the current
/// scope expression.
///
/// The operand returned from this function will *not be valid* after
/// an ExprKind::Scope is passed, so please do *not* return it from
/// functions to avoid bad miscompiles.
pub fn as_local_rvalue<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<Rvalue<'tcx>>
where
M: Mirror<'tcx, Output = Expr<'tcx>>,
@ -23,7 +27,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
/// Compile `expr`, yielding an rvalue.
pub fn as_rvalue<M>(
fn as_rvalue<M>(
&mut self,
block: BasicBlock,
scope: Option<region::Scope>,
@ -66,16 +70,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let value_operand = unpack!(block = this.as_operand(block, scope, value));
block.and(Rvalue::Repeat(value_operand, count))
}
ExprKind::Borrow {
borrow_kind,
arg,
} => {
let arg_place = match borrow_kind {
BorrowKind::Shared => unpack!(block = this.as_read_only_place(block, arg)),
_ => unpack!(block = this.as_place(block, arg)),
};
block.and(Rvalue::Ref(this.hir.tcx().lifetimes.re_erased, borrow_kind, arg_place))
}
ExprKind::Binary { op, lhs, rhs } => {
let lhs = unpack!(block = this.as_operand(block, scope, lhs));
let rhs = unpack!(block = this.as_operand(block, scope, rhs));
@ -256,77 +250,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
};
block.and(Rvalue::Aggregate(result, operands))
}
ExprKind::Adt {
adt_def,
variant_index,
substs,
user_ty,
fields,
base,
} => {
// see (*) above
let is_union = adt_def.is_union();
let active_field_index = if is_union {
Some(fields[0].name.index())
} else {
None
};
// first process the set of fields that were provided
// (evaluating them in order given by user)
let fields_map: FxHashMap<_, _> = fields
.into_iter()
.map(|f| {
(
f.name,
unpack!(block = this.as_operand(block, scope, f.expr)),
)
}).collect();
let field_names = this.hir.all_fields(adt_def, variant_index);
let fields = if let Some(FruInfo { base, field_types }) = base {
let base = unpack!(block = this.as_place(block, base));
// MIR does not natively support FRU, so for each
// base-supplied field, generate an operand that
// reads it from the base.
field_names
.into_iter()
.zip(field_types.into_iter())
.map(|(n, ty)| match fields_map.get(&n) {
Some(v) => v.clone(),
None => this.consume_by_copy_or_move(this.hir.tcx().mk_place_field(
base.clone(),
n,
ty,
)),
})
.collect()
} else {
field_names
.iter()
.filter_map(|n| fields_map.get(n).cloned())
.collect()
};
let inferred_ty = expr.ty;
let user_ty = user_ty.map(|ty| {
this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
span: source_info.span,
user_ty: ty,
inferred_ty,
})
});
let adt = box AggregateKind::Adt(
adt_def,
variant_index,
substs,
user_ty,
active_field_index,
);
block.and(Rvalue::Aggregate(adt, fields))
}
ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
block = unpack!(this.stmt_expr(block, expr, None));
block.and(this.unit_rvalue())
@ -351,6 +274,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::Match { .. }
| ExprKind::NeverToAny { .. }
| ExprKind::Use { .. }
| ExprKind::Borrow { .. }
| ExprKind::Adt { .. }
| ExprKind::Loop { .. }
| ExprKind::LogicalOp { .. }
| ExprKind::Call { .. }

View file

@ -48,11 +48,12 @@ impl Category {
| ExprKind::Match { .. }
| ExprKind::NeverToAny { .. }
| ExprKind::Use { .. }
| ExprKind::Adt { .. }
| ExprKind::Borrow { .. }
| ExprKind::Call { .. } => Some(Category::Rvalue(RvalueFunc::Into)),
ExprKind::Array { .. }
| ExprKind::Tuple { .. }
| ExprKind::Adt { .. }
| ExprKind::Closure { .. }
| ExprKind::Unary { .. }
| ExprKind::Binary { .. }
@ -60,7 +61,6 @@ impl Category {
| ExprKind::Cast { .. }
| ExprKind::Pointer { .. }
| ExprKind::Repeat { .. }
| ExprKind::Borrow { .. }
| ExprKind::Assign { .. }
| ExprKind::AssignOp { .. }
| ExprKind::Yield { .. }

View file

@ -4,7 +4,9 @@ use crate::build::expr::category::{Category, RvalueFunc};
use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
use crate::hair::*;
use rustc::mir::*;
use rustc::ty;
use rustc::ty::{self, CanonicalUserTypeAnnotation};
use rustc_data_structures::fx::FxHashMap;
use syntax_pos::symbol::sym;
use rustc_target::spec::abi::Abi;
@ -200,16 +202,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ty::FnDef(def_id, _) => {
let f = ty.fn_sig(this.hir.tcx());
if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic {
Some(this.hir.tcx().item_name(def_id).as_str())
Some(this.hir.tcx().item_name(def_id))
} else {
None
}
}
_ => None,
};
let intrinsic = intrinsic.as_ref().map(|s| &s[..]);
let fun = unpack!(block = this.as_local_operand(block, fun));
if intrinsic == Some("move_val_init") {
if let Some(sym::move_val_init) = intrinsic {
// `move_val_init` has "magic" semantics - the second argument is
// always evaluated "directly" into the first one.
@ -271,6 +272,102 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ExprKind::Use { source } => {
this.into(destination, block, source)
}
ExprKind::Borrow { arg, borrow_kind } => {
// We don't do this in `as_rvalue` because we use `as_place`
// for borrow expressions, so we cannot create an `RValue` that
// remains valid across user code. `as_rvalue` is usually called
// by this method anyway, so this shouldn't cause too many
// unnecessary temporaries.
let arg_place = match borrow_kind {
BorrowKind::Shared => unpack!(block = this.as_read_only_place(block, arg)),
_ => unpack!(block = this.as_place(block, arg)),
};
let borrow = Rvalue::Ref(
this.hir.tcx().lifetimes.re_erased,
borrow_kind,
arg_place,
);
this.cfg.push_assign(block, source_info, destination, borrow);
block.unit()
}
ExprKind::Adt {
adt_def,
variant_index,
substs,
user_ty,
fields,
base,
} => {
// See the notes for `ExprKind::Array` in `as_rvalue` and for
// `ExprKind::Borrow` above.
let is_union = adt_def.is_union();
let active_field_index = if is_union {
Some(fields[0].name.index())
} else {
None
};
let scope = this.local_scope();
// first process the set of fields that were provided
// (evaluating them in order given by user)
let fields_map: FxHashMap<_, _> = fields
.into_iter()
.map(|f| {
(
f.name,
unpack!(block = this.as_operand(block, scope, f.expr)),
)
}).collect();
let field_names = this.hir.all_fields(adt_def, variant_index);
let fields = if let Some(FruInfo { base, field_types }) = base {
let base = unpack!(block = this.as_place(block, base));
// MIR does not natively support FRU, so for each
// base-supplied field, generate an operand that
// reads it from the base.
field_names
.into_iter()
.zip(field_types.into_iter())
.map(|(n, ty)| match fields_map.get(&n) {
Some(v) => v.clone(),
None => this.consume_by_copy_or_move(
this.hir.tcx().mk_place_field(base.clone(), n, ty),
),
}).collect()
} else {
field_names
.iter()
.filter_map(|n| fields_map.get(n).cloned())
.collect()
};
let inferred_ty = expr.ty;
let user_ty = user_ty.map(|ty| {
this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
span: source_info.span,
user_ty: ty,
inferred_ty,
})
});
let adt = box AggregateKind::Adt(
adt_def,
variant_index,
substs,
user_ty,
active_field_index,
);
this.cfg.push_assign(
block,
source_info,
destination,
Rvalue::Aggregate(adt, fields)
);
block.unit()
}
// These cases don't actually need a destination
ExprKind::Assign { .. }
@ -325,10 +422,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::Cast { .. }
| ExprKind::Pointer { .. }
| ExprKind::Repeat { .. }
| ExprKind::Borrow { .. }
| ExprKind::Array { .. }
| ExprKind::Tuple { .. }
| ExprKind::Adt { .. }
| ExprKind::Closure { .. }
| ExprKind::Literal { .. }
| ExprKind::Yield { .. } => {

View file

@ -395,23 +395,25 @@ impl<'cx, 'tcx> crate::borrow_check::MirBorrowckCtxt<'cx, 'tcx> {
)
}
crate fn cannot_mutate_in_match_guard(
crate fn cannot_mutate_in_immutable_section(
&self,
mutate_span: Span,
match_span: Span,
match_place: &str,
immutable_span: Span,
immutable_place: &str,
immutable_section: &str,
action: &str,
) -> DiagnosticBuilder<'cx> {
let mut err = struct_span_err!(
self,
mutate_span,
E0510,
"cannot {} `{}` in match guard",
"cannot {} `{}` in {}",
action,
match_place,
immutable_place,
immutable_section,
);
err.span_label(mutate_span, format!("cannot {}", action));
err.span_label(match_span, String::from("value is immutable in match guard"));
err.span_label(immutable_span, format!("value is immutable in {}", immutable_section));
err
}

View file

@ -432,6 +432,7 @@ symbols! {
module,
module_path,
more_struct_aliases,
move_val_init,
movbe_target_feature,
must_use,
naked,

View file

@ -36,11 +36,11 @@ fn main() {
// END RUST SOURCE
// START rustc.XXX.mir_map.0.mir
// let mut _0: &'static Foo;
// let mut _1: &'static Foo;
// let _1: &'static Foo;
// let _2: Foo;
// let mut _3: &'static [(u32, u32)];
// let mut _4: &'static [(u32, u32); 42];
// let mut _5: &'static [(u32, u32); 42];
// let _5: &'static [(u32, u32); 42];
// let _6: [(u32, u32); 42];
// let mut _7: (u32, u32);
// let mut _8: (u32, u32);

View file

@ -34,12 +34,12 @@ fn main() {
// let _1: &str;
// let mut _2: Test1;
// let mut _3: isize;
// let mut _4: &str;
// let mut _5: &str;
// let _4: &str;
// let _5: &str;
// let _6: &str;
// let mut _7: Test2;
// let mut _8: isize;
// let mut _9: &str;
// let _9: &str;
// bb0: {
// StorageLive(_1);
// StorageLive(_2);
@ -103,12 +103,12 @@ fn main() {
// let _1: &str;
// let mut _2: Test1;
// let mut _3: isize;
// let mut _4: &str;
// let mut _5: &str;
// let _4: &str;
// let _5: &str;
// let _6: &str;
// let mut _7: Test2;
// let mut _8: isize;
// let mut _9: &str;
// let _9: &str;
// bb0: {
// StorageLive(_1);
// StorageLive(_2);
@ -172,12 +172,12 @@ fn main() {
// let _1: &str;
// let mut _2: Test1;
// let mut _3: isize;
// let mut _4: &str;
// let mut _5: &str;
// let _4: &str;
// let _5: &str;
// let _6: &str;
// let mut _7: Test2;
// let mut _8: isize;
// let mut _9: &str;
// let _9: &str;
// bb0: {
// StorageLive(_1);
// StorageLive(_2);

View file

@ -1,8 +1,8 @@
error[E0381]: use of possibly-uninitialized variable: `origin`
--> $DIR/borrowck-init-in-fru.rs:9:5
--> $DIR/borrowck-init-in-fru.rs:9:14
|
LL | origin = Point { x: 10, ..origin };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of possibly-uninitialized `origin.y`
| ^^^^^^^^^^^^^^^^^^^^^^^^^ use of possibly-uninitialized `origin.y`
error: aborting due to previous error

View file

@ -0,0 +1,82 @@
// Test that we error if a slice is modified after it has been bounds checked
// and before we actually index it.
fn modify_before_assert_slice_slice(x: &[&[i32]]) -> i32 {
let mut x = x;
let z: &[i32] = &[1, 2, 3];
let y: &[&[i32]] = &[z];
x[{ x = y; 0 }][2] // OK we haven't checked any bounds before we index `x`.
}
fn modify_before_assert_array_slice(x: &[&[i32]; 3]) -> i32 {
let mut x = x;
let z: &[i32] = &[1, 2, 3];
let y: &[&[i32]; 3] = &[z, z, z];
x[{ x = y; 0 }][2] // OK we haven't checked any bounds before we index `x`.
}
fn modify_before_assert_slice_array(x: &[&[i32; 3]]) -> i32 {
let mut x = x;
let z: &[i32; 3] = &[1, 2, 3];
let y: &[&[i32; 3]] = &[z];
x[{ x = y; 0 }][2] // OK we haven't checked any bounds before we index `x`.
}
fn modify_before_assert_array_array(x: &[&[i32; 3]; 3]) -> i32 {
let mut x = x;
let z: &[i32; 3] = &[1, 2, 3];
let y: &[&[i32; 3]; 3] = &[z, z, z];
x[{ x = y; 0 }][2] // OK we haven't checked any bounds before we index `x`.
}
fn modify_after_assert_slice_slice(x: &[&[i32]]) -> i32 {
let mut x = x;
let z: &[i32] = &[1, 2, 3];
let y: &[&[i32]] = &[&z];
x[1][{ x = y; 2}] //~ ERROR cannot assign `x` in indexing expression
}
fn modify_after_assert_array_slice(x: &[&[i32]; 1]) -> i32 {
let mut x = x;
let z: &[i32] = &[1, 2, 3];
let y: &[&[i32]; 1] = &[&z];
x[0][{ x = y; 2}] // OK cannot invalidate a fixed-size array bounds check
}
fn modify_after_assert_slice_array(x: &[&[i32; 3]]) -> i32 {
let mut x = x;
let z: &[i32; 3] = &[1, 2, 3];
let y: &[&[i32; 3]] = &[&z];
x[1][{ x = y; 2}] //~ ERROR cannot assign `x` in indexing expression
}
fn modify_after_assert_array_array(x: &[&[i32; 3]; 1]) -> i32 {
let mut x = x;
let z: &[i32; 3] = &[1, 2, 3];
let y: &[&[i32; 3]; 1] = &[&z];
x[0][{ x = y; 2}] // OK cannot invalidate a fixed-size array bounds check
}
fn modify_after_assert_slice_slice_array(x: &[&[[i32; 1]]]) -> i32 {
let mut x = x;
let z: &[[i32; 1]] = &[[1], [2], [3]];
let y: &[&[[i32; 1]]] = &[&z];
x[1][{ x = y; 2}][0] //~ ERROR cannot assign `x` in indexing expression
}
fn modify_after_assert_slice_slice_slice(x: &[&[&[i32]]]) -> i32 {
let mut x = x;
let z: &[&[i32]] = &[&[1], &[2], &[3]];
let y: &[&[&[i32]]] = &[z];
x[1][{ x = y; 2}][0] //~ ERROR cannot assign `x` in indexing expression
}
fn main() {
println!("{}", modify_after_assert_slice_array(&[&[4, 5, 6], &[9, 10, 11]]));
println!("{}", modify_after_assert_slice_slice(&[&[4, 5, 6], &[9, 10, 11]]));
println!("{}", modify_after_assert_slice_slice_array(&[&[[4], [5], [6]], &[[9], [10], [11]]]));
println!("{}", modify_after_assert_slice_slice_slice(
&[&[&[4], &[5], &[6]], &[&[9], &[10], &[11]]]),
);
}

View file

@ -0,0 +1,35 @@
error[E0510]: cannot assign `x` in indexing expression
--> $DIR/slice-index-bounds-check-invalidation.rs:36:12
|
LL | x[1][{ x = y; 2}]
| ---- ^^^^^ cannot assign
| |
| value is immutable in indexing expression
error[E0510]: cannot assign `x` in indexing expression
--> $DIR/slice-index-bounds-check-invalidation.rs:50:12
|
LL | x[1][{ x = y; 2}]
| ---- ^^^^^ cannot assign
| |
| value is immutable in indexing expression
error[E0510]: cannot assign `x` in indexing expression
--> $DIR/slice-index-bounds-check-invalidation.rs:64:12
|
LL | x[1][{ x = y; 2}][0]
| ---- ^^^^^ cannot assign
| |
| value is immutable in indexing expression
error[E0510]: cannot assign `x` in indexing expression
--> $DIR/slice-index-bounds-check-invalidation.rs:71:12
|
LL | x[1][{ x = y; 2}][0]
| ---- ^^^^^ cannot assign
| |
| value is immutable in indexing expression
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0510`.

View file

@ -0,0 +1,67 @@
// Test evaluation order of assignment expressions is right to left.
// run-pass
// We would previously not finish evaluating borrow and FRU expressions before
// starting on the LHS
struct S(i32);
fn evaluate_reborrow_before_assign() {
let mut x = &1;
let y = &mut &2;
let z = &3;
// There's an implicit reborrow of `x` on the right-hand side of the
// assignement. Note that writing an explicit reborrow would not show this
// bug, as now there would be two reborrows on the right-hand side and at
// least one of them would happen before the left-hand side is evaluated.
*{ x = z; &mut *y } = x;
assert_eq!(*x, 3);
assert_eq!(**y, 1); // y should be assigned the original value of `x`.
}
fn evaluate_mut_reborrow_before_assign() {
let mut x = &mut 1;
let y = &mut &mut 2;
let z = &mut 3;
*{ x = z; &mut *y } = x;
assert_eq!(*x, 3);
assert_eq!(**y, 1); // y should be assigned the original value of `x`.
}
// We should evaluate `x[2]` and borrow the value out *before* evaluating the
// LHS and changing its value.
fn evaluate_ref_to_temp_before_assign_slice() {
let mut x = &[S(0), S(1), S(2)][..];
let y = &mut &S(7);
*{ x = &[S(3), S(4), S(5)]; &mut *y } = &x[2];
assert_eq!(2, y.0);
assert_eq!(5, x[2].0);
}
// We should evaluate `x[2]` and copy the value out *before* evaluating the LHS
// and changing its value.
fn evaluate_fru_to_temp_before_assign_slice() {
let mut x = &[S(0), S(1), S(2)][..];
let y = &mut S(7);
*{ x = &[S(3), S(4), S(5)]; &mut *y } = S { ..x[2] };
assert_eq!(2, y.0);
assert_eq!(5, x[2].0);
}
// We should evaluate `*x` and copy the value out *before* evaluating the LHS
// and dropping `x`.
fn evaluate_fru_to_temp_before_assign_box() {
let x = Box::new(S(0));
let y = &mut S(1);
*{ drop(x); &mut *y } = S { ..*x };
assert_eq!(0, y.0);
}
fn main() {
evaluate_reborrow_before_assign();
evaluate_mut_reborrow_before_assign();
evaluate_ref_to_temp_before_assign_slice();
evaluate_fru_to_temp_before_assign_slice();
evaluate_fru_to_temp_before_assign_box();
}

View file

@ -1,8 +1,8 @@
error[E0597]: `x` does not live long enough
--> $DIR/issue-52534-2.rs:6:9
--> $DIR/issue-52534-2.rs:6:13
|
LL | y = &x
| ^^^^^^ borrowed value does not live long enough
| ^^ borrowed value does not live long enough
LL |
LL | }
| - `x` dropped here while still borrowed

View file

@ -1,8 +1,8 @@
error[E0597]: `a` does not live long enough
--> $DIR/issue-36537.rs:5:9
--> $DIR/issue-36537.rs:5:13
|
LL | p = &a;
| ^^^^^^ borrowed value does not live long enough
| ^^ borrowed value does not live long enough
...
LL | }
| - `a` dropped here while still borrowed