1 use rustc::ty::{self, TyCtxt};
2 use rustc::mir::visit::Visitor;
3 use rustc::mir::{BasicBlock, Location, Body, Place, ReadOnlyBodyAndCache, Rvalue};
4 use rustc::mir::{Statement, StatementKind};
5 use rustc::mir::TerminatorKind;
6 use rustc::mir::{Operand, BorrowKind};
7 use rustc_data_structures::graph::dominators::Dominators;
9 use crate::dataflow::indexes::BorrowIndex;
11 use crate::borrow_check::{
12 borrow_set::BorrowSet,
13 location::LocationTable,
16 JustWrite, WriteAndRead, AccessDepth, Deep, Shallow, ReadOrWrite, Activation, Read,
17 Reservation, Write, LocalMutationIsAllowed, MutateMode, ArtificialField, ReadKind, WriteKind,
20 pub(super) fn generate_invalidates<'tcx>(
22 param_env: ty::ParamEnv<'tcx>,
23 all_facts: &mut Option<AllFacts>,
24 location_table: &LocationTable,
25 body: ReadOnlyBodyAndCache<'_, 'tcx>,
26 borrow_set: &BorrowSet<'tcx>,
28 if all_facts.is_none() {
29 // Nothing to do if we don't have any facts
33 if let Some(all_facts) = all_facts {
34 let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation");
35 let dominators = body.dominators();
36 let mut ig = InvalidationGenerator {
49 struct InvalidationGenerator<'cx, 'tcx> {
51 param_env: ty::ParamEnv<'tcx>,
52 all_facts: &'cx mut AllFacts,
53 location_table: &'cx LocationTable,
54 body: &'cx Body<'tcx>,
55 dominators: Dominators<BasicBlock>,
56 borrow_set: &'cx BorrowSet<'tcx>,
59 /// Visits the whole MIR and generates `invalidates()` facts.
60 /// Most of the code implementing this was stolen from `borrow_check/mod.rs`.
61 impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
64 statement: &Statement<'tcx>,
67 self.check_activations(location);
69 match statement.kind {
70 StatementKind::Assign(box(ref lhs, ref rhs)) => {
83 StatementKind::FakeRead(_, _) => {
84 // Only relavent for initialized/liveness/safety checks.
86 StatementKind::SetDiscriminant {
97 StatementKind::InlineAsm(ref asm) => {
98 for (o, output) in asm.asm.outputs.iter().zip(asm.outputs.iter()) {
100 // FIXME(eddyb) indirect inline asm outputs should
101 // be encoded through MIR place derefs instead.
105 (Deep, Read(ReadKind::Copy)),
106 LocalMutationIsAllowed::No,
112 if o.is_rw { Deep } else { Shallow(None) },
113 if o.is_rw { WriteAndRead } else { JustWrite },
117 for (_, input) in asm.inputs.iter() {
118 self.consume_operand(location, input);
122 StatementKind::AscribeUserType(..) |
123 StatementKind::Retag { .. } |
124 StatementKind::StorageLive(..) => {
125 // `Nop`, `AscribeUserType`, `Retag`, and `StorageLive` are irrelevant
128 StatementKind::StorageDead(local) => {
132 (Shallow(None), Write(WriteKind::StorageDeadOrDrop)),
133 LocalMutationIsAllowed::Yes,
138 self.super_statement(statement, location);
141 fn visit_terminator_kind(
143 kind: &TerminatorKind<'tcx>,
146 self.check_activations(location);
149 TerminatorKind::SwitchInt {
155 self.consume_operand(location, discr);
157 TerminatorKind::Drop {
158 location: ref drop_place,
165 (AccessDepth::Drop, Write(WriteKind::StorageDeadOrDrop)),
166 LocalMutationIsAllowed::Yes,
169 TerminatorKind::DropAndReplace {
170 location: ref drop_place,
171 value: ref new_value,
181 self.consume_operand(
186 TerminatorKind::Call {
193 self.consume_operand(location, func);
195 self.consume_operand(location, arg);
197 if let Some((ref dest, _ /*bb*/)) = *destination {
206 TerminatorKind::Assert {
213 self.consume_operand(location, cond);
214 use rustc::mir::interpret::PanicInfo;
215 if let PanicInfo::BoundsCheck { ref len, ref index } = *msg {
216 self.consume_operand(location, len);
217 self.consume_operand(location, index);
220 TerminatorKind::Yield {
225 self.consume_operand(location, value);
227 // Invalidate all borrows of local places
228 let borrow_set = self.borrow_set.clone();
229 let resume = self.location_table.start_index(resume.start_location());
230 for i in borrow_set.borrows.indices() {
231 if borrow_of_local_data(&borrow_set.borrows[i].borrowed_place) {
232 self.all_facts.invalidates.push((resume, i));
236 TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => {
237 // Invalidate all borrows of local places
238 let borrow_set = self.borrow_set.clone();
239 let start = self.location_table.start_index(location);
240 for i in borrow_set.borrows.indices() {
241 if borrow_of_local_data(&borrow_set.borrows[i].borrowed_place) {
242 self.all_facts.invalidates.push((start, i));
246 TerminatorKind::Goto { target: _ }
247 | TerminatorKind::Abort
248 | TerminatorKind::Unreachable
249 | TerminatorKind::FalseEdges {
253 | TerminatorKind::FalseUnwind {
257 // no data used, thus irrelevant to borrowck
261 self.super_terminator_kind(kind, location);
265 impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> {
266 /// Simulates mutation of a place.
277 (kind, Write(WriteKind::Mutate)),
278 LocalMutationIsAllowed::ExceptUpvars,
282 /// Simulates consumption of an operand.
286 operand: &Operand<'tcx>,
289 Operand::Copy(ref place) => {
293 (Deep, Read(ReadKind::Copy)),
294 LocalMutationIsAllowed::No,
297 Operand::Move(ref place) => {
301 (Deep, Write(WriteKind::Move)),
302 LocalMutationIsAllowed::Yes,
305 Operand::Constant(_) => {}
309 // Simulates consumption of an rvalue
313 rvalue: &Rvalue<'tcx>,
316 Rvalue::Ref(_ /*rgn*/, bk, ref place) => {
317 let access_kind = match bk {
318 BorrowKind::Shallow => {
319 (Shallow(Some(ArtificialField::ShallowBorrow)), Read(ReadKind::Borrow(bk)))
321 BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
322 BorrowKind::Unique | BorrowKind::Mut { .. } => {
323 let wk = WriteKind::MutableBorrow(bk);
324 if allow_two_phase_borrow(bk) {
325 (Deep, Reservation(wk))
336 LocalMutationIsAllowed::No,
340 Rvalue::Use(ref operand)
341 | Rvalue::Repeat(ref operand, _)
342 | Rvalue::UnaryOp(_ /*un_op*/, ref operand)
343 | Rvalue::Cast(_ /*cast_kind*/, ref operand, _ /*ty*/) => {
344 self.consume_operand(location, operand)
347 Rvalue::Len(ref place) | Rvalue::Discriminant(ref place) => {
348 let af = match *rvalue {
349 Rvalue::Len(..) => Some(ArtificialField::ArrayLength),
350 Rvalue::Discriminant(..) => None,
356 (Shallow(af), Read(ReadKind::Copy)),
357 LocalMutationIsAllowed::No,
361 Rvalue::BinaryOp(_bin_op, ref operand1, ref operand2)
362 | Rvalue::CheckedBinaryOp(_bin_op, ref operand1, ref operand2) => {
363 self.consume_operand(location, operand1);
364 self.consume_operand(location, operand2);
367 Rvalue::NullaryOp(_op, _ty) => {
370 Rvalue::Aggregate(_, ref operands) => {
371 for operand in operands {
372 self.consume_operand(location, operand);
378 /// Simulates an access to a place.
383 kind: (AccessDepth, ReadOrWrite),
384 _is_local_mutation_allowed: LocalMutationIsAllowed,
387 // note: not doing check_access_permissions checks because they don't generate invalidates
388 self.check_access_for_conflict(location, place, sd, rw);
391 fn check_access_for_conflict(
399 "invalidation::check_access_for_conflict(location={:?}, place={:?}, sd={:?}, \
407 let body = self.body;
408 let param_env = self.param_env;
409 let borrow_set = self.borrow_set.clone();
410 let indices = self.borrow_set.borrows.indices();
411 each_borrow_involving_path(
420 |this, borrow_index, borrow| {
421 match (rw, borrow.kind) {
422 // Obviously an activation is compatible with its own
423 // reservation (or even prior activating uses of same
424 // borrow); so don't check if they interfere.
426 // NOTE: *reservations* do conflict with themselves;
427 // thus aren't injecting unsoundenss w/ this check.)
428 (Activation(_, activating), _) if activating == borrow_index => {
429 // Activating a borrow doesn't generate any invalidations, since we
430 // have already taken the reservation
433 (Read(_), BorrowKind::Shallow)
434 | (Read(_), BorrowKind::Shared)
435 | (Read(ReadKind::Borrow(BorrowKind::Shallow)), BorrowKind::Unique)
436 | (Read(ReadKind::Borrow(BorrowKind::Shallow)), BorrowKind::Mut { .. }) => {
437 // Reads don't invalidate shared or shallow borrows
440 (Read(_), BorrowKind::Unique) | (Read(_), BorrowKind::Mut { .. }) => {
441 // Reading from mere reservations of mutable-borrows is OK.
442 if !is_active(&this.dominators, borrow, location) {
443 // If the borrow isn't active yet, reads don't invalidate it
444 assert!(allow_two_phase_borrow(borrow.kind));
445 return Control::Continue;
448 // Unique and mutable borrows are invalidated by reads from any
450 this.generate_invalidates(borrow_index, location);
454 | (Activation(_, _), _)
456 // unique or mutable borrows are invalidated by writes.
457 // Reservations count as writes since we need to check
458 // that activating the borrow will be OK
459 // FIXME(bob_twinkles) is this actually the right thing to do?
460 this.generate_invalidates(borrow_index, location);
469 /// Generates a new `invalidates(L, B)` fact.
470 fn generate_invalidates(&mut self, b: BorrowIndex, l: Location) {
471 let lidx = self.location_table.start_index(l);
472 self.all_facts.invalidates.push((lidx, b));
475 fn check_activations(
479 // Two-phase borrow support: For each activation that is newly
480 // generated at this statement, check if it interferes with
482 for &borrow_index in self.borrow_set.activations_at_location(location) {
483 let borrow = &self.borrow_set[borrow_index];
485 // only mutable borrows should be 2-phase
486 assert!(match borrow.kind {
487 BorrowKind::Shared | BorrowKind::Shallow => false,
488 BorrowKind::Unique | BorrowKind::Mut { .. } => true,
493 &borrow.borrowed_place,
496 Activation(WriteKind::MutableBorrow(borrow.kind), borrow_index),
498 LocalMutationIsAllowed::No,
501 // We do not need to call `check_if_path_or_subpath_is_moved`
502 // again, as we already called it when we made the
503 // initial reservation.