]> git.lizzy.rs Git - rust.git/commitdiff
Kill the storage for all locals on returning terminators
authorKeith Yeung <kungfukeith11@gmail.com>
Sun, 19 Nov 2017 12:26:23 +0000 (04:26 -0800)
committerKeith Yeung <kungfukeith11@gmail.com>
Fri, 24 Nov 2017 20:00:59 +0000 (12:00 -0800)
src/librustc_mir/borrow_check.rs
src/librustc_mir/dataflow/impls/borrows.rs
src/test/compile-fail/borrowck/borrowck-local-borrow-outlives-fn.rs [new file with mode: 0644]
src/test/compile-fail/borrowck/borrowck-thread-local-static-borrow-outlives-fn.rs [new file with mode: 0644]
src/test/run-fail/borrowck-local-borrow.rs [new file with mode: 0644]

index ddaade98be108d9157aec9338c38893fee89ab80..4d54c6f473cde692dc2552b4605fe8014cdf2923 100644 (file)
@@ -373,10 +373,41 @@ fn visit_terminator_entry(&mut self,
                                      Consume, (value, span), flow_state);
             }
 
-            TerminatorKind::Goto { target: _ } |
             TerminatorKind::Resume |
             TerminatorKind::Return |
-            TerminatorKind::GeneratorDrop |
+            TerminatorKind::GeneratorDrop => {
+                // Returning from the function implicitly kills storage for all locals and statics.
+                // Often, the storage will already have been killed by an explicit
+                // StorageDead, but we don't always emit those (notably on unwind paths),
+                // so this "extra check" serves as a kind of backup.
+                let domain = flow_state.borrows.base_results.operator();
+                for borrow in domain.borrows() {
+                    let root_lvalue = self.prefixes(
+                        &borrow.lvalue,
+                        PrefixSet::All
+                    ).last().unwrap();
+                    match root_lvalue {
+                        Lvalue::Static(_) => {
+                            self.access_lvalue(
+                                ContextKind::StorageDead.new(loc),
+                                (&root_lvalue, self.mir.source_info(borrow.location).span),
+                                (Deep, Write(WriteKind::StorageDeadOrDrop)),
+                                flow_state
+                            );
+                        }
+                        Lvalue::Local(_) => {
+                            self.access_lvalue(
+                                ContextKind::StorageDead.new(loc),
+                                (&root_lvalue, self.mir.source_info(borrow.location).span),
+                                (Shallow(None), Write(WriteKind::StorageDeadOrDrop)),
+                                flow_state
+                            );
+                        }
+                        Lvalue::Projection(_) => ()
+                    }
+                }
+            }
+            TerminatorKind::Goto { target: _ } |
             TerminatorKind::Unreachable |
             TerminatorKind::FalseEdges { .. } => {
                 // no data used, thus irrelevant to borrowck
index acfa195d7b09a336ee920e7c630dd056668f351e..7e2d86003b051bb131987dd5506b2e39fc81e0dd 100644 (file)
@@ -233,6 +233,35 @@ fn statement_effect(&self,
     fn terminator_effect(&self,
                          sets: &mut BlockSets<BorrowIndex>,
                          location: Location) {
+        let block = &self.mir.basic_blocks().get(location.block).unwrap_or_else(|| {
+            panic!("could not find block at location {:?}", location);
+        });
+        match block.terminator().kind {
+            mir::TerminatorKind::Resume |
+            mir::TerminatorKind::Return |
+            mir::TerminatorKind::GeneratorDrop => {
+                // When we return from the function, then all `ReScope`-style regions
+                // are guaranteed to have ended.
+                // Normally, there would be `EndRegion` statements that come before,
+                // and hence most of these loans will already be dead -- but, in some cases
+                // like unwind paths, we do not always emit `EndRegion` statements, so we
+                // add some kills here as a "backup" and to avoid spurious error messages.
+                for (borrow_index, borrow_data) in self.borrows.iter_enumerated() {
+                    if let ReScope(..) = borrow_data.region {
+                        sets.kill(&borrow_index);
+                    }
+                }
+            }
+            mir::TerminatorKind::SwitchInt {..} |
+            mir::TerminatorKind::Drop {..} |
+            mir::TerminatorKind::DropAndReplace {..} |
+            mir::TerminatorKind::Call {..} |
+            mir::TerminatorKind::Assert {..} |
+            mir::TerminatorKind::Yield {..} |
+            mir::TerminatorKind::Goto {..} |
+            mir::TerminatorKind::FalseEdges {..} |
+            mir::TerminatorKind::Unreachable => {}
+        }
         self.kill_loans_out_of_scope_at_location(sets, location);
     }
 
diff --git a/src/test/compile-fail/borrowck/borrowck-local-borrow-outlives-fn.rs b/src/test/compile-fail/borrowck/borrowck-local-borrow-outlives-fn.rs
new file mode 100644 (file)
index 0000000..7ff3aa3
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright 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.
+
+// revisions: ast mir
+//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir
+
+fn cplusplus_mode(x: isize) -> &'static isize {
+    &x //[ast]~ ERROR `x` does not live long enough
+       //[mir]~^ ERROR `x` does not live long enough (Ast)
+       //[mir]~| ERROR borrowed value does not live long enough (Mir)
+}
+
+fn main() {}
diff --git a/src/test/compile-fail/borrowck/borrowck-thread-local-static-borrow-outlives-fn.rs b/src/test/compile-fail/borrowck/borrowck-thread-local-static-borrow-outlives-fn.rs
new file mode 100644 (file)
index 0000000..5a6c86f
--- /dev/null
@@ -0,0 +1,24 @@
+// Copyright 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.
+
+// revisions: ast mir
+//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir
+
+#![feature(thread_local)]
+
+#[thread_local]
+static FOO: u8 = 3;
+
+fn assert_static(_t: &'static u8) {}
+fn main() {
+     assert_static(&FOO); //[ast]~ ERROR [E0597]
+                          //[mir]~^ ERROR (Ast) [E0597]
+                          //[mir]~| ERROR (Mir) [E0597]
+}
diff --git a/src/test/run-fail/borrowck-local-borrow.rs b/src/test/run-fail/borrowck-local-borrow.rs
new file mode 100644 (file)
index 0000000..7f11f45
--- /dev/null
@@ -0,0 +1,19 @@
+// Copyright 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.
+// error-pattern:panic 1
+
+// revisions: ast mir
+//[mir]compile-flags: -Z emit-end-regions -Z borrowck-mir
+
+fn main() {
+    let x = 2;
+    let y = &x;
+    panic!("panic 1");
+}