]> git.lizzy.rs Git - rust.git/commitdiff
preliminary support for may-dangle attribute and drop constraints
authorNiko Matsakis <niko@alum.mit.edu>
Tue, 24 Oct 2017 21:14:39 +0000 (17:14 -0400)
committerNiko Matsakis <niko@alum.mit.edu>
Tue, 31 Oct 2017 16:41:38 +0000 (12:41 -0400)
src/librustc_mir/transform/nll/constraint_generation.rs
src/librustc_mir/transform/nll/mod.rs
src/test/mir-opt/nll/region-liveness-drop-no-may-dangle.rs [new file with mode: 0644]

index 518e140b5dd1b0530a8ddcb2cd5d0f4119d6a2ae..1e008ec38f22a75df333192765a94d6c1bbdac9e 100644 (file)
@@ -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 @@ fn add_constraints(&mut self) {
     /// > 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 @@ fn add_liveness_constraints(&mut self) {
             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;
+                        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;
-                        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_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);
+                    }
+                }
+            }
+        }
+    }
 }
index 131f088d91c7534c44e01363b4c0cc1d0716e2d2..6139be69566c7933ae6491907e7e628afc6f280d 100644 (file)
@@ -65,7 +65,7 @@ fn run_pass<'a, 'tcx>(
             // 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 (file)
index 0000000..5f4da96
--- /dev/null
@@ -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