about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-12-07 19:02:18 +0000
committerbors <bors@rust-lang.org>2014-12-07 19:02:18 +0000
commit77cd5cc54eda4243614be32b893db512beab0f8e (patch)
treef9eaccb9816417e85ef1d9c62b594d48580daabd /src
parent558f8d8e3ea6b7da3d6d338740637149a7e45840 (diff)
parent2dccb5a77fe6f163ee090800c61efef4a62775d9 (diff)
downloadrust-77cd5cc54eda4243614be32b893db512beab0f8e.tar.gz
rust-77cd5cc54eda4243614be32b893db512beab0f8e.zip
auto merge of #19548 : luqmana/rust/mfb, r=nikomatsakis
Fixes #19367.
Diffstat (limited to 'src')
-rw-r--r--src/librustc_trans/trans/_match.rs57
-rw-r--r--src/test/run-pass/issue-19367.rs42
2 files changed, 85 insertions, 14 deletions
diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs
index ada46ab7db7..f5155852aa0 100644
--- a/src/librustc_trans/trans/_match.rs
+++ b/src/librustc_trans/trans/_match.rs
@@ -1226,30 +1226,50 @@ pub fn trans_match<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
 
 /// Checks whether the binding in `discr` is assigned to anywhere in the expression `body`
 fn is_discr_reassigned(bcx: Block, discr: &ast::Expr, body: &ast::Expr) -> bool {
-    match discr.node {
+    let (vid, field) = match discr.node {
         ast::ExprPath(..) => match bcx.def(discr.id) {
-            def::DefLocal(vid) | def::DefUpvar(vid, _, _) => {
-                let mut rc = ReassignmentChecker {
-                    node: vid,
-                    reassigned: false
-                };
-                {
-                    let mut visitor = euv::ExprUseVisitor::new(&mut rc, bcx);
-                    visitor.walk_expr(body);
-                }
-                rc.reassigned
-            }
-            _ => false
+            def::DefLocal(vid) | def::DefUpvar(vid, _, _) => (vid, None),
+            _ => return false
+        },
+        ast::ExprField(ref base, field) => {
+            let vid = match bcx.tcx().def_map.borrow().get(&base.id) {
+                Some(&def::DefLocal(vid)) | Some(&def::DefUpvar(vid, _, _)) => vid,
+                _ => return false
+            };
+            (vid, Some(mc::NamedField(field.node.name)))
+        },
+        ast::ExprTupField(ref base, field) => {
+            let vid = match bcx.tcx().def_map.borrow().get(&base.id) {
+                Some(&def::DefLocal(vid)) | Some(&def::DefUpvar(vid, _, _)) => vid,
+                _ => return false
+            };
+            (vid, Some(mc::PositionalField(field.node)))
         },
-        _ => false
+        _ => return false
+    };
+
+    let mut rc = ReassignmentChecker {
+        node: vid,
+        field: field,
+        reassigned: false
+    };
+    {
+        let mut visitor = euv::ExprUseVisitor::new(&mut rc, bcx);
+        visitor.walk_expr(body);
     }
+    rc.reassigned
 }
 
 struct ReassignmentChecker {
     node: ast::NodeId,
+    field: Option<mc::FieldName>,
     reassigned: bool
 }
 
+// Determine if the expression we're matching on is reassigned to within
+// the body of the match's arm.
+// We only care for the `mutate` callback since this check only matters
+// for cases where the matched value is moved.
 impl<'tcx> euv::Delegate<'tcx> for ReassignmentChecker {
     fn consume(&mut self, _: ast::NodeId, _: Span, _: mc::cmt, _: euv::ConsumeMode) {}
     fn matched_pat(&mut self, _: &ast::Pat, _: mc::cmt, _: euv::MatchMode) {}
@@ -1262,6 +1282,15 @@ impl<'tcx> euv::Delegate<'tcx> for ReassignmentChecker {
         match cmt.cat {
             mc::cat_upvar(mc::Upvar { id: ty::UpvarId { var_id: vid, .. }, .. }) |
             mc::cat_local(vid) => self.reassigned = self.node == vid,
+            mc::cat_interior(ref base_cmt, mc::InteriorField(field)) => {
+                match base_cmt.cat {
+                    mc::cat_upvar(mc::Upvar { id: ty::UpvarId { var_id: vid, .. }, .. }) |
+                    mc::cat_local(vid) => {
+                        self.reassigned = self.node == vid && Some(field) == self.field
+                    },
+                    _ => {}
+                }
+            },
             _ => {}
         }
     }
diff --git a/src/test/run-pass/issue-19367.rs b/src/test/run-pass/issue-19367.rs
new file mode 100644
index 00000000000..6083b340825
--- /dev/null
+++ b/src/test/run-pass/issue-19367.rs
@@ -0,0 +1,42 @@
+// Copyright 2014 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.
+
+#![feature(tuple_indexing)]
+struct S {
+    o: Option<String>
+}
+
+// Make sure we don't reuse the same alloca when matching
+// on field of struct or tuple which we reassign in the match body.
+
+fn main() {
+    let mut a = (0i, Some("right".into_string()));
+    let b = match a.1 {
+        Some(v) => {
+            a.1 = Some("wrong".into_string());
+            v
+        }
+        None => String::new()
+    };
+    println!("{}", b);
+    assert_eq!(b, "right");
+
+
+    let mut s = S{ o: Some("right".into_string()) };
+    let b = match s.o {
+        Some(v) => {
+            s.o = Some("wrong".into_string());
+            v
+        }
+        None => String::new(),
+    };
+    println!("{}", b);
+    assert_eq!(b, "right");
+}