diff --git a/src/librustc_mir/transform/nll/constraint_generation.rs b/src/librustc_mir/transform/nll/constraint_generation.rs index 518e140b5dd..1e008ec38f2 100644 --- a/src/librustc_mir/transform/nll/constraint_generation.rs +++ b/src/librustc_mir/transform/nll/constraint_generation.rs @@ -8,8 +8,15 @@ // option. This file may not be copied, modified, or distributed // 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::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::ToRegionIndex; @@ -19,6 +26,7 @@ pub(super) fn generate_constraints<'a, 'gcx, 'tcx>( infcx: &InferCtxt<'a, 'gcx, 'tcx>, regioncx: &mut RegionInferenceContext, mir: &Mir<'tcx>, + mir_source: MirSource, liveness: &LivenessResults, ) { ConstraintGeneration { @@ -26,6 +34,7 @@ pub(super) fn generate_constraints<'a, 'gcx, 'tcx>( regioncx, mir, liveness, + mir_source, }.add_constraints(); } @@ -34,6 +43,7 @@ struct ConstraintGeneration<'constrain, 'gcx: 'tcx, 'tcx: 'constrain> { regioncx: &'constrain mut RegionInferenceContext, mir: &'constrain Mir<'tcx>, liveness: &'constrain LivenessResults, + mir_source: MirSource, } 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 /// > must include the point P. fn add_liveness_constraints(&mut self) { - let tcx = self.infcx.tcx; - debug!("add_liveness_constraints()"); for bb in self.mir.basic_blocks().indices() { debug!("add_liveness_constraints: bb={:?}", bb); @@ -56,20 +64,112 @@ impl<'constrain, 'gcx, 'tcx> ConstraintGeneration<'constrain, 'gcx, 'tcx> { self.liveness .regular .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() { let live_local_ty = self.mir.local_decls[live_local].ty; - tcx.for_each_free_region(&live_local_ty, |live_region| { - let vid = live_region.to_region_index(); - self.regioncx.add_live_point(vid, location); - }) + self.add_regular_live_constraint(live_local_ty, 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(&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); + } + } + } + } + } } diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs index 131f088d91c..6139be69566 100644 --- a/src/librustc_mir/transform/nll/mod.rs +++ b/src/librustc_mir/transform/nll/mod.rs @@ -65,7 +65,7 @@ impl MirPass for NLL { // Create the region inference context, generate the constraints, // and then solve them. 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); // Dump MIR results into a file, if that is enabled. diff --git a/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs new file mode 100644 index 00000000000..5f4da966281 --- /dev/null +++ b/src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs @@ -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 or the MIT license +// , 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 { + value: T +} + +// Look ma, no `#[may_dangle]` attribute here. +impl Drop for Wrap { + 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