Skip to content

Commit d7c0f7d

Browse files
committed
librustc: Don't use the same alloca for match binding which we reassign to in arm body.
1 parent 1712ab2 commit d7c0f7d

File tree

4 files changed

+136
-3
lines changed

4 files changed

+136
-3
lines changed

src/librustc/middle/trans/_match.rs

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,9 @@
189189
#![allow(non_camel_case_types)]
190190

191191
use back::abi;
192+
use mc = middle::mem_categorization;
192193
use driver::config::FullDebugInfo;
194+
use euv = middle::expr_use_visitor;
193195
use llvm;
194196
use llvm::{ValueRef, BasicBlockRef};
195197
use middle::const_eval;
@@ -1292,13 +1294,55 @@ pub fn trans_match<'a>(
12921294
trans_match_inner(bcx, match_expr.id, discr_expr, arms, dest)
12931295
}
12941296

1295-
fn create_bindings_map(bcx: &Block, pat: Gc<ast::Pat>) -> BindingsMap {
1297+
/// Checks whether the binding in `discr` is assigned to anywhere in the expression `body`
1298+
fn is_discr_reassigned(bcx: &Block, discr: &ast::Expr, body: &ast::Expr) -> bool {
1299+
match discr.node {
1300+
ast::ExprPath(..) => match bcx.def(discr.id) {
1301+
def::DefLocal(vid, _) | def::DefBinding(vid, _) => {
1302+
let mut rc = ReassignmentChecker {
1303+
node: vid,
1304+
reassigned: false
1305+
};
1306+
{
1307+
let mut visitor = euv::ExprUseVisitor::new(&mut rc, bcx);
1308+
visitor.walk_expr(body);
1309+
}
1310+
rc.reassigned
1311+
}
1312+
_ => false
1313+
},
1314+
_ => false
1315+
}
1316+
}
1317+
1318+
struct ReassignmentChecker {
1319+
node: ast::NodeId,
1320+
reassigned: bool
1321+
}
1322+
1323+
impl euv::Delegate for ReassignmentChecker {
1324+
fn consume(&mut self, _: ast::NodeId, _: Span, _: mc::cmt, _: euv::ConsumeMode) {}
1325+
fn consume_pat(&mut self, _: &ast::Pat, _: mc::cmt, _: euv::ConsumeMode) {}
1326+
fn borrow(&mut self, _: ast::NodeId, _: Span, _: mc::cmt, _: ty::Region,
1327+
_: ty::BorrowKind, _: euv::LoanCause) {}
1328+
fn decl_without_init(&mut self, _: ast::NodeId, _: Span) {}
1329+
fn mutate(&mut self, _: ast::NodeId, _: Span, cmt: mc::cmt, _: euv::MutateMode) {
1330+
match cmt.cat {
1331+
mc::cat_local(vid) => self.reassigned = self.node == vid,
1332+
_ => {}
1333+
}
1334+
}
1335+
}
1336+
1337+
fn create_bindings_map(bcx: &Block, pat: Gc<ast::Pat>,
1338+
discr: &ast::Expr, body: &ast::Expr) -> BindingsMap {
12961339
// Create the bindings map, which is a mapping from each binding name
12971340
// to an alloca() that will be the value for that local variable.
12981341
// Note that we use the names because each binding will have many ids
12991342
// from the various alternatives.
13001343
let ccx = bcx.ccx();
13011344
let tcx = bcx.tcx();
1345+
let reassigned = is_discr_reassigned(bcx, discr, body);
13021346
let mut bindings_map = HashMap::new();
13031347
pat_bindings(&tcx.def_map, &*pat, |bm, p_id, span, path1| {
13041348
let ident = path1.node;
@@ -1310,7 +1354,7 @@ fn create_bindings_map(bcx: &Block, pat: Gc<ast::Pat>) -> BindingsMap {
13101354
let trmode;
13111355
match bm {
13121356
ast::BindByValue(_)
1313-
if !ty::type_moves_by_default(tcx, variable_ty) => {
1357+
if !ty::type_moves_by_default(tcx, variable_ty) || reassigned => {
13141358
llmatch = alloca_no_lifetime(bcx,
13151359
llvariable_ty.ptr_to(),
13161360
"__llmatch");
@@ -1371,7 +1415,7 @@ fn trans_match_inner<'a>(scope_cx: &'a Block<'a>,
13711415
let arm_datas: Vec<ArmData> = arms.iter().map(|arm| ArmData {
13721416
bodycx: fcx.new_id_block("case_body", arm.body.id),
13731417
arm: arm,
1374-
bindings_map: create_bindings_map(bcx, *arm.pats.get(0))
1418+
bindings_map: create_bindings_map(bcx, *arm.pats.get(0), discr_expr, &*arm.body)
13751419
}).collect();
13761420

13771421
let mut static_inliner = StaticInliner { tcx: scope_cx.tcx() };

src/librustc/middle/trans/common.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use driver::session::Session;
1616
use llvm;
1717
use llvm::{ValueRef, BasicBlockRef, BuilderRef};
1818
use llvm::{True, False, Bool};
19+
use mc = middle::mem_categorization;
1920
use middle::def;
2021
use middle::lang_items::LangItem;
2122
use middle::subst;
@@ -481,6 +482,36 @@ impl<'a> Block<'a> {
481482
}
482483
}
483484

485+
impl<'a> mc::Typer for Block<'a> {
486+
fn tcx<'a>(&'a self) -> &'a ty::ctxt {
487+
self.tcx()
488+
}
489+
490+
fn node_ty(&self, id: ast::NodeId) -> mc::McResult<ty::t> {
491+
Ok(node_id_type(self, id))
492+
}
493+
494+
fn node_method_ty(&self, method_call: typeck::MethodCall) -> Option<ty::t> {
495+
self.tcx().method_map.borrow().find(&method_call).map(|method| method.ty)
496+
}
497+
498+
fn adjustments<'a>(&'a self) -> &'a RefCell<NodeMap<ty::AutoAdjustment>> {
499+
&self.tcx().adjustments
500+
}
501+
502+
fn is_method_call(&self, id: ast::NodeId) -> bool {
503+
self.tcx().method_map.borrow().contains_key(&typeck::MethodCall::expr(id))
504+
}
505+
506+
fn temporary_scope(&self, rvalue_id: ast::NodeId) -> Option<ast::NodeId> {
507+
self.tcx().region_maps.temporary_scope(rvalue_id)
508+
}
509+
510+
fn upvar_borrow(&self, upvar_id: ty::UpvarId) -> ty::UpvarBorrow {
511+
self.tcx().upvar_borrow_map.borrow().get_copy(&upvar_id)
512+
}
513+
}
514+
484515
pub struct Result<'a> {
485516
pub bcx: &'a Block<'a>,
486517
pub val: ValueRef

src/test/run-pass/issue-15571.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn main() {
12+
let mut foo = Some(box 5i);
13+
match foo {
14+
None => {},
15+
Some(x) => {
16+
foo = Some(x);
17+
}
18+
};
19+
println!("'{}'", foo.unwrap());
20+
}

src/test/run-pass/issue-16151.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use std::mem;
12+
13+
static mut DROP_COUNT: uint = 0;
14+
15+
struct Fragment;
16+
17+
impl Drop for Fragment {
18+
fn drop(&mut self) {
19+
unsafe {
20+
DROP_COUNT += 1;
21+
}
22+
}
23+
}
24+
25+
fn main() {
26+
{
27+
let mut fragments = vec![Fragment, Fragment, Fragment];
28+
let _new_fragments: Vec<Fragment> = mem::replace(&mut fragments, vec![])
29+
.move_iter()
30+
.skip_while(|_fragment| {
31+
true
32+
}).collect();
33+
}
34+
unsafe {
35+
assert_eq!(DROP_COUNT, 3);
36+
}
37+
}
38+

0 commit comments

Comments
 (0)