preliminary support for may-dangle attribute and drop constraints
This commit is contained in:
parent
e02937848b
commit
af09f720d6
3 changed files with 164 additions and 14 deletions
|
@ -8,8 +8,15 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use rustc::mir::Mir;
|
use rustc::mir::{Location, Mir};
|
||||||
|
use rustc::mir::transform::MirSource;
|
||||||
use rustc::infer::InferCtxt;
|
use rustc::infer::InferCtxt;
|
||||||
|
use rustc::traits::{self, ObligationCause};
|
||||||
|
use rustc::ty::{self, Ty};
|
||||||
|
use rustc::ty::fold::TypeFoldable;
|
||||||
|
use rustc::util::common::ErrorReported;
|
||||||
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
|
use syntax::codemap::DUMMY_SP;
|
||||||
|
|
||||||
use super::LivenessResults;
|
use super::LivenessResults;
|
||||||
use super::ToRegionIndex;
|
use super::ToRegionIndex;
|
||||||
|
@ -19,6 +26,7 @@ pub(super) fn generate_constraints<'a, 'gcx, 'tcx>(
|
||||||
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
|
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
|
||||||
regioncx: &mut RegionInferenceContext,
|
regioncx: &mut RegionInferenceContext,
|
||||||
mir: &Mir<'tcx>,
|
mir: &Mir<'tcx>,
|
||||||
|
mir_source: MirSource,
|
||||||
liveness: &LivenessResults,
|
liveness: &LivenessResults,
|
||||||
) {
|
) {
|
||||||
ConstraintGeneration {
|
ConstraintGeneration {
|
||||||
|
@ -26,6 +34,7 @@ pub(super) fn generate_constraints<'a, 'gcx, 'tcx>(
|
||||||
regioncx,
|
regioncx,
|
||||||
mir,
|
mir,
|
||||||
liveness,
|
liveness,
|
||||||
|
mir_source,
|
||||||
}.add_constraints();
|
}.add_constraints();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +43,7 @@ struct ConstraintGeneration<'constrain, 'gcx: 'tcx, 'tcx: 'constrain> {
|
||||||
regioncx: &'constrain mut RegionInferenceContext,
|
regioncx: &'constrain mut RegionInferenceContext,
|
||||||
mir: &'constrain Mir<'tcx>,
|
mir: &'constrain Mir<'tcx>,
|
||||||
liveness: &'constrain LivenessResults,
|
liveness: &'constrain LivenessResults,
|
||||||
|
mir_source: MirSource,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'constrain, 'gcx, 'tcx> ConstraintGeneration<'constrain, 'gcx, 'tcx> {
|
impl<'constrain, 'gcx, 'tcx> ConstraintGeneration<'constrain, 'gcx, 'tcx> {
|
||||||
|
@ -47,8 +57,6 @@ impl<'constrain, 'gcx, 'tcx> ConstraintGeneration<'constrain, 'gcx, 'tcx> {
|
||||||
/// > If a variable V is live at point P, then all regions R in the type of V
|
/// > If a variable V is live at point P, then all regions R in the type of V
|
||||||
/// > must include the point P.
|
/// > must include the point P.
|
||||||
fn add_liveness_constraints(&mut self) {
|
fn add_liveness_constraints(&mut self) {
|
||||||
let tcx = self.infcx.tcx;
|
|
||||||
|
|
||||||
debug!("add_liveness_constraints()");
|
debug!("add_liveness_constraints()");
|
||||||
for bb in self.mir.basic_blocks().indices() {
|
for bb in self.mir.basic_blocks().indices() {
|
||||||
debug!("add_liveness_constraints: bb={:?}", bb);
|
debug!("add_liveness_constraints: bb={:?}", bb);
|
||||||
|
@ -56,20 +64,112 @@ impl<'constrain, 'gcx, 'tcx> ConstraintGeneration<'constrain, 'gcx, 'tcx> {
|
||||||
self.liveness
|
self.liveness
|
||||||
.regular
|
.regular
|
||||||
.simulate_block(self.mir, bb, |location, live_locals| {
|
.simulate_block(self.mir, bb, |location, live_locals| {
|
||||||
debug!(
|
|
||||||
"add_liveness_constraints: location={:?} live_locals={:?}",
|
|
||||||
location,
|
|
||||||
live_locals
|
|
||||||
);
|
|
||||||
|
|
||||||
for live_local in live_locals.iter() {
|
for live_local in live_locals.iter() {
|
||||||
let live_local_ty = self.mir.local_decls[live_local].ty;
|
let live_local_ty = self.mir.local_decls[live_local].ty;
|
||||||
tcx.for_each_free_region(&live_local_ty, |live_region| {
|
self.add_regular_live_constraint(live_local_ty, location);
|
||||||
let vid = live_region.to_region_index();
|
}
|
||||||
self.regioncx.add_live_point(vid, location);
|
});
|
||||||
})
|
|
||||||
|
self.liveness
|
||||||
|
.drop
|
||||||
|
.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;
|
||||||
|
self.add_drop_live_constraint(live_local_ty, 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 add_regular_live_constraint<T>(&mut self, live_ty: T, location: Location)
|
||||||
|
where
|
||||||
|
T: TypeFoldable<'tcx>,
|
||||||
|
{
|
||||||
|
debug!(
|
||||||
|
"add_regular_live_constraint(live_ty={:?}, location={:?})",
|
||||||
|
live_ty,
|
||||||
|
location
|
||||||
|
);
|
||||||
|
|
||||||
|
self.infcx
|
||||||
|
.tcx
|
||||||
|
.for_each_free_region(&live_ty, |live_region| {
|
||||||
|
let vid = live_region.to_region_index();
|
||||||
|
self.regioncx.add_live_point(vid, location);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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_ty: Ty<'tcx>, location: Location) {
|
||||||
|
debug!(
|
||||||
|
"add_drop_live_constraint(dropped_ty={:?}, location={:?})",
|
||||||
|
dropped_ty,
|
||||||
|
location
|
||||||
|
);
|
||||||
|
|
||||||
|
let tcx = self.infcx.tcx;
|
||||||
|
let mut types = vec![(dropped_ty, 0)];
|
||||||
|
let mut known = FxHashSet();
|
||||||
|
while let Some((ty, depth)) = types.pop() {
|
||||||
|
let span = DUMMY_SP; // FIXME
|
||||||
|
let result = match tcx.dtorck_constraint_for_ty(span, dropped_ty, depth, ty) {
|
||||||
|
Ok(result) => result,
|
||||||
|
Err(ErrorReported) => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let ty::DtorckConstraint {
|
||||||
|
outlives,
|
||||||
|
dtorck_types,
|
||||||
|
} = result;
|
||||||
|
|
||||||
|
// All things in the `outlives` array may be touched by
|
||||||
|
// the destructor and must be live at this point.
|
||||||
|
for outlive in outlives {
|
||||||
|
if let Some(ty) = outlive.as_type() {
|
||||||
|
self.add_regular_live_constraint(ty, location);
|
||||||
|
} else if let Some(r) = outlive.as_region() {
|
||||||
|
self.add_regular_live_constraint(r, location);
|
||||||
|
} else {
|
||||||
|
bug!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// However, there may also be some types that
|
||||||
|
// `dtorck_constraint_for_ty` could not resolve (e.g.,
|
||||||
|
// associated types and parameters). We need to normalize
|
||||||
|
// associated types here and possibly recursively process.
|
||||||
|
let def_id = tcx.hir.local_def_id(self.mir_source.item_id());
|
||||||
|
let param_env = self.infcx.tcx.param_env(def_id);
|
||||||
|
for ty in dtorck_types {
|
||||||
|
// FIXME -- I think that this may disregard some region obligations
|
||||||
|
// or something. Do we care? -nmatsakis
|
||||||
|
let cause = ObligationCause::dummy();
|
||||||
|
match traits::fully_normalize(self.infcx, cause, param_env, &ty) {
|
||||||
|
Ok(ty) => match ty.sty {
|
||||||
|
ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => {
|
||||||
|
self.add_regular_live_constraint(ty, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => if known.insert(ty) {
|
||||||
|
types.push((ty, depth + 1));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Err(errors) => {
|
||||||
|
self.infcx.report_fulfillment_errors(&errors, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ impl MirPass for NLL {
|
||||||
// Create the region inference context, generate the constraints,
|
// Create the region inference context, generate the constraints,
|
||||||
// and then solve them.
|
// and then solve them.
|
||||||
let regioncx = &mut RegionInferenceContext::new(num_region_variables);
|
let regioncx = &mut RegionInferenceContext::new(num_region_variables);
|
||||||
constraint_generation::generate_constraints(infcx, regioncx, mir, liveness);
|
constraint_generation::generate_constraints(infcx, regioncx, mir, source, liveness);
|
||||||
regioncx.solve(infcx, mir);
|
regioncx.solve(infcx, mir);
|
||||||
|
|
||||||
// Dump MIR results into a file, if that is enabled.
|
// Dump MIR results into a file, if that is enabled.
|
||||||
|
|
50
src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs
Normal file
50
src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
// Copyright 2012-2016 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.
|
||||||
|
|
||||||
|
// Basic test for liveness constraints: the region (`R1`) that appears
|
||||||
|
// in the type of `p` includes the points after `&v[0]` up to (but not
|
||||||
|
// including) the call to `use_x`. The `else` branch is not included.
|
||||||
|
|
||||||
|
// ignore-tidy-linelength
|
||||||
|
// compile-flags:-Znll -Zverbose
|
||||||
|
// ^^^^^^^^^ force compiler to dump more region information
|
||||||
|
|
||||||
|
#![allow(warnings)]
|
||||||
|
|
||||||
|
fn use_x(_: usize) -> bool { true }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut v = [1, 2, 3];
|
||||||
|
let p: Wrap<& /* R1 */ usize> = Wrap { value: &v[0] };
|
||||||
|
if true {
|
||||||
|
use_x(*p.value);
|
||||||
|
} else {
|
||||||
|
use_x(22);
|
||||||
|
}
|
||||||
|
|
||||||
|
// `p` will get dropped here. Because the `#[may_dangle]`
|
||||||
|
// attribute is not present on `Wrap`, we must conservatively
|
||||||
|
// assume that the dtor may access the `value` field, and hence we
|
||||||
|
// must consider R1 to be live.
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Wrap<T> {
|
||||||
|
value: T
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look ma, no `#[may_dangle]` attribute here.
|
||||||
|
impl<T> Drop for Wrap<T> {
|
||||||
|
fn drop(&mut self) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
// END RUST SOURCE
|
||||||
|
// START rustc.node12.nll.0.mir
|
||||||
|
// | R4: {bb1[3], bb1[4], bb1[5], bb2[0], bb2[1], bb2[2], bb3[0], bb4[0], bb4[1], bb4[2], bb6[0], bb7[0], bb7[1], bb8[0]}
|
||||||
|
// END rustc.node12.nll.0.mir
|
Loading…
Reference in a new issue