]> git.lizzy.rs Git - rust.git/commitdiff
clear discriminant drop flag at the bottom of a ladder
authorAriel Ben-Yehuda <arielb1@mail.tau.ac.il>
Sun, 25 Dec 2016 16:44:19 +0000 (18:44 +0200)
committerAriel Ben-Yehuda <arielb1@mail.tau.ac.il>
Sun, 25 Dec 2016 16:44:19 +0000 (18:44 +0200)
Fixes #38437.

src/librustc_borrowck/borrowck/mir/elaborate_drops.rs
src/test/run-pass/issue-38437.rs [new file with mode: 0644]

index 4f49bfc9725b345078da7699fe1b680b9a5cfd31..88e5bae483d73deddeaf807d8162f13e5c5faf24 100644 (file)
@@ -481,54 +481,55 @@ fn drop_halfladder<'a>(&mut self,
                            is_cleanup: bool)
                            -> Vec<BasicBlock>
     {
-        let mut succ = succ;
         let mut unwind_succ = if is_cleanup {
             None
         } else {
             c.unwind
         };
-        let mut update_drop_flag = true;
+
+        let mut succ = self.new_block(
+            c, c.is_cleanup, TerminatorKind::Goto { target: succ }
+        );
+
+        // Always clear the "master" drop flag at the bottom of the
+        // ladder. This is needed because the "master" drop flag
+        // protects the ADT's discriminant, which is invalidated
+        // after the ADT is dropped.
+        self.set_drop_flag(
+            Location { block: succ, statement_index: 0 },
+            c.path,
+            DropFlagState::Absent
+        );
 
         fields.iter().rev().enumerate().map(|(i, &(ref lv, path))| {
-            let drop_block = match path {
-                Some(path) => {
-                    debug!("drop_ladder: for std field {} ({:?})", i, lv);
-
-                    self.elaborated_drop_block(&DropCtxt {
-                        source_info: c.source_info,
-                        is_cleanup: is_cleanup,
-                        init_data: c.init_data,
-                        lvalue: lv,
-                        path: path,
-                        succ: succ,
-                        unwind: unwind_succ,
-                    })
-                }
-                None => {
-                    debug!("drop_ladder: for rest field {} ({:?})", i, lv);
-
-                    let blk = self.complete_drop(&DropCtxt {
-                        source_info: c.source_info,
-                        is_cleanup: is_cleanup,
-                        init_data: c.init_data,
-                        lvalue: lv,
-                        path: c.path,
-                        succ: succ,
-                        unwind: unwind_succ,
-                    }, update_drop_flag);
-
-                    // the drop flag has been updated - updating
-                    // it again would clobber it.
-                    update_drop_flag = false;
-
-                    blk
-                }
+            succ = if let Some(path) = path {
+                debug!("drop_ladder: for std field {} ({:?})", i, lv);
+
+                self.elaborated_drop_block(&DropCtxt {
+                    source_info: c.source_info,
+                    is_cleanup: is_cleanup,
+                    init_data: c.init_data,
+                    lvalue: lv,
+                    path: path,
+                    succ: succ,
+                    unwind: unwind_succ,
+                })
+            } else {
+                debug!("drop_ladder: for rest field {} ({:?})", i, lv);
+
+                self.complete_drop(&DropCtxt {
+                    source_info: c.source_info,
+                    is_cleanup: is_cleanup,
+                    init_data: c.init_data,
+                    lvalue: lv,
+                    path: c.path,
+                    succ: succ,
+                    unwind: unwind_succ,
+                }, false)
             };
 
-            succ = drop_block;
             unwind_succ = unwind_ladder.as_ref().map(|p| p[i]);
-
-            drop_block
+            succ
         }).collect()
     }
 
diff --git a/src/test/run-pass/issue-38437.rs b/src/test/run-pass/issue-38437.rs
new file mode 100644 (file)
index 0000000..a6e7df1
--- /dev/null
@@ -0,0 +1,54 @@
+// 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.
+
+// Check that drop elaboration clears the "master" discriminant
+// drop flag even if it protects no fields.
+
+struct Good(usize);
+impl Drop for Good {
+    #[inline(never)]
+    fn drop(&mut self) {
+        println!("dropping Good({})", self.0);
+    }
+}
+
+struct Void;
+impl Drop for Void {
+    #[inline(never)]
+    fn drop(&mut self) {
+        panic!("Suddenly, a Void appears.");
+    }
+}
+
+enum E {
+    Never(Void),
+    Fine(Good)
+}
+
+fn main() {
+    let mut go = true;
+
+    loop {
+        let next;
+        match go {
+            true => next = E::Fine(Good(123)),
+            false => return,
+        }
+
+        match next {
+            E::Never(_) => return,
+            E::Fine(_good) => go = false,
+        }
+
+        // `next` is dropped and StorageDead'd here. We must reset the
+        // discriminant's drop flag to avoid random variants being
+        // dropped.
+    }
+}