blob: 80f5fe4184f9db508757dd51fd05f566fce0d1f1 [file] [log] [blame]
// Copyright 2017 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.
use borrow_check::nll::region_infer::Cause;
use borrow_check::nll::type_check::AtLocation;
use dataflow::move_paths::{HasMoveData, MoveData};
use dataflow::MaybeInitializedPlaces;
use dataflow::{FlowAtLocation, FlowsAtLocation};
use rustc::infer::region_constraints::RegionConstraintData;
use rustc::mir::Local;
use rustc::mir::{BasicBlock, Location, Mir};
use rustc::traits::ObligationCause;
use rustc::ty::subst::Kind;
use rustc::ty::{Ty, TypeFoldable};
use rustc_data_structures::fx::FxHashMap;
use std::rc::Rc;
use util::liveness::LivenessResults;
use super::TypeChecker;
/// Combines liveness analysis with initialization analysis to
/// determine which variables are live at which points, both due to
/// ordinary uses and drops. Returns a set of (ty, location) pairs
/// that indicate which types must be live at which point in the CFG.
/// This vector is consumed by `constraint_generation`.
///
/// NB. This computation requires normalization; therefore, it must be
/// performed before
pub(super) fn generate<'gcx, 'tcx>(
cx: &mut TypeChecker<'_, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
liveness: &LivenessResults,
flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>,
) {
let mut generator = TypeLivenessGenerator {
cx,
mir,
liveness,
flow_inits,
move_data,
drop_data: FxHashMap(),
};
for bb in mir.basic_blocks().indices() {
generator.add_liveness_constraints(bb);
}
}
struct TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx>
where
'typeck: 'gen,
'flow: 'gen,
'tcx: 'typeck + 'flow,
'gcx: 'tcx,
{
cx: &'gen mut TypeChecker<'typeck, 'gcx, 'tcx>,
mir: &'gen Mir<'tcx>,
liveness: &'gen LivenessResults,
flow_inits: &'gen mut FlowAtLocation<MaybeInitializedPlaces<'flow, 'gcx, 'tcx>>,
move_data: &'gen MoveData<'tcx>,
drop_data: FxHashMap<Ty<'tcx>, DropData<'tcx>>,
}
struct DropData<'tcx> {
dropped_kinds: Vec<Kind<'tcx>>,
region_constraint_data: Option<Rc<RegionConstraintData<'tcx>>>,
}
impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx> {
/// Liveness constraints:
///
/// > If a variable V is live at point P, then all regions R in the type of V
/// > must include the point P.
fn add_liveness_constraints(&mut self, bb: BasicBlock) {
debug!("add_liveness_constraints(bb={:?})", bb);
self.liveness
.regular
.simulate_block(self.mir, bb, |location, live_locals| {
for live_local in live_locals.iter() {
let live_local_ty = self.mir.local_decls[live_local].ty;
let cause = Cause::LiveVar(live_local, location);
Self::push_type_live_constraint(&mut self.cx, live_local_ty, location, cause);
}
});
let mut all_live_locals: Vec<(Location, Vec<Local>)> = vec![];
self.liveness
.drop
.simulate_block(self.mir, bb, |location, live_locals| {
all_live_locals.push((location, live_locals.iter().collect()));
});
debug!(
"add_liveness_constraints: all_live_locals={:#?}",
all_live_locals
);
let terminator_index = self.mir.basic_blocks()[bb].statements.len();
self.flow_inits.reset_to_entry_of(bb);
while let Some((location, live_locals)) = all_live_locals.pop() {
for live_local in live_locals {
debug!(
"add_liveness_constraints: location={:?} live_local={:?}",
location, live_local
);
if log_enabled!(::log::Level::Debug) {
self.flow_inits.each_state_bit(|mpi_init| {
debug!(
"add_liveness_constraints: location={:?} initialized={:?}",
location,
&self.flow_inits.operator().move_data().move_paths[mpi_init]
);
});
}
let mpi = self.move_data.rev_lookup.find_local(live_local);
if let Some(initialized_child) = self.flow_inits.has_any_child_of(mpi) {
debug!(
"add_liveness_constraints: mpi={:?} has initialized child {:?}",
self.move_data.move_paths[mpi],
self.move_data.move_paths[initialized_child]
);
let live_local_ty = self.mir.local_decls[live_local].ty;
self.add_drop_live_constraint(live_local, live_local_ty, location);
}
}
if location.statement_index == terminator_index {
debug!(
"add_liveness_constraints: reconstruct_terminator_effect from {:#?}",
location
);
self.flow_inits.reconstruct_terminator_effect(location);
} else {
debug!(
"add_liveness_constraints: reconstruct_statement_effect from {:#?}",
location
);
self.flow_inits.reconstruct_statement_effect(location);
}
self.flow_inits.apply_local_effect(location);
}
}
/// Some variable with type `live_ty` is "regular live" at
/// `location` -- i.e., it may be used later. This means that all
/// regions appearing in the type `live_ty` must be live at
/// `location`.
fn push_type_live_constraint<T>(
cx: &mut TypeChecker<'_, 'gcx, 'tcx>,
value: T,
location: Location,
cause: Cause,
) where
T: TypeFoldable<'tcx>,
{
debug!(
"push_type_live_constraint(live_ty={:?}, location={:?})",
value, location
);
cx.tcx().for_each_free_region(&value, |live_region| {
cx
.constraints
.liveness_set
.push((live_region, location, cause.clone()));
});
}
/// Some variable with type `live_ty` is "drop live" at `location`
/// -- i.e., it may be dropped later. This means that *some* of
/// the regions in its type must be live at `location`. The
/// precise set will depend on the dropck constraints, and in
/// particular this takes `#[may_dangle]` into account.
fn add_drop_live_constraint(
&mut self,
dropped_local: Local,
dropped_ty: Ty<'tcx>,
location: Location,
) {
debug!(
"add_drop_live_constraint(dropped_local={:?}, dropped_ty={:?}, location={:?})",
dropped_local, dropped_ty, location
);
let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({
let cx = &mut self.cx;
move || Self::compute_drop_data(cx, dropped_ty)
});
if let Some(data) = &drop_data.region_constraint_data {
self.cx
.push_region_constraints(location.at_self(), data.clone());
}
// All things in the `outlives` array may be touched by
// the destructor and must be live at this point.
let cause = Cause::DropVar(dropped_local, location);
for &kind in &drop_data.dropped_kinds {
Self::push_type_live_constraint(&mut self.cx, kind, location, cause);
}
}
fn compute_drop_data(
cx: &mut TypeChecker<'_, 'gcx, 'tcx>,
dropped_ty: Ty<'tcx>,
) -> DropData<'tcx> {
debug!("compute_drop_data(dropped_ty={:?})", dropped_ty,);
let (dropped_kinds, region_constraint_data) =
cx.fully_perform_op_and_get_region_constraint_data(
|| format!("compute_drop_data(dropped_ty={:?})", dropped_ty),
|cx| {
Ok(cx
.infcx
.at(&ObligationCause::dummy(), cx.param_env)
.dropck_outlives(dropped_ty))
},
).unwrap();
DropData {
dropped_kinds,
region_constraint_data,
}
}
}