auto merge of #7262 : nikomatsakis/rust/ref-bindings-in-irrefut-patterns, r=catamorphism

Correct treatment of irrefutable patterns. The old code was wrong in many, many ways. `ref` bindings didn't work, it sometimes copied when it should have moved, the borrow checker didn't even look at such patterns at all, we weren't consistent about preventing values with destructors from being pulled apart, etc.

Fixes #3224.
Fixes #3225.
Fixes #3255.
Fixes #6225.
Fixes #6386.

r? @catamorphism
This commit is contained in:
bors 2013-07-08 18:49:46 -07:00
commit a48ca3290d
72 changed files with 1140 additions and 696 deletions

View File

@ -418,8 +418,8 @@ mod test {
fn make_file(path : &Path, contents: &[~str]) {
let file = io::file_writer(path, [io::Create, io::Truncate]).get();
for contents.iter().advance |&str| {
file.write_str(str);
for contents.iter().advance |str| {
file.write_str(*str);
file.write_char('\n');
}
}
@ -445,7 +445,7 @@ mod test {
|i| fmt!("tmp/lib-fileinput-test-fileinput-read-byte-%u.tmp", i)), true);
// 3 files containing 0\n, 1\n, and 2\n respectively
for filenames.iter().enumerate().advance |(i, &filename)| {
for filenames.iter().enumerate().advance |(i, filename)| {
make_file(filename.get_ref(), [fmt!("%u", i)]);
}
@ -475,7 +475,7 @@ mod test {
|i| fmt!("tmp/lib-fileinput-test-fileinput-read-%u.tmp", i)), true);
// 3 files containing 1\n, 2\n, and 3\n respectively
for filenames.iter().enumerate().advance |(i, &filename)| {
for filenames.iter().enumerate().advance |(i, filename)| {
make_file(filename.get_ref(), [fmt!("%u", i)]);
}
@ -495,10 +495,11 @@ mod test {
3,
|i| fmt!("tmp/lib-fileinput-test-input-vec-%u.tmp", i)), true);
for filenames.iter().enumerate().advance |(i, &filename)| {
for filenames.iter().enumerate().advance |(i, filename)| {
let contents =
vec::from_fn(3, |j| fmt!("%u %u", i, j));
make_file(filename.get_ref(), contents);
debug!("contents=%?", contents);
all_lines.push_all(contents);
}
@ -515,7 +516,7 @@ mod test {
3,
|i| fmt!("tmp/lib-fileinput-test-input-vec-state-%u.tmp", i)),true);
for filenames.iter().enumerate().advance |(i, &filename)| {
for filenames.iter().enumerate().advance |(i, filename)| {
let contents =
vec::from_fn(3, |j| fmt!("%u %u", i, j + 1));
make_file(filename.get_ref(), contents);
@ -579,10 +580,10 @@ mod test {
3,
|i| fmt!("tmp/lib-fileinput-test-next-file-%u.tmp", i)),true);
for filenames.iter().enumerate().advance |(i, &filename)| {
for filenames.iter().enumerate().advance |(i, filename)| {
let contents =
vec::from_fn(3, |j| fmt!("%u %u", i, j + 1));
make_file(&filename.get(), contents);
make_file(filename.get_ref(), contents);
}
let in = FileInput::from_vec(filenames);

View File

@ -1571,10 +1571,10 @@ mod biguint_tests {
fn test_to_str_radix() {
let r = to_str_pairs();
for r.iter().advance |num_pair| {
let &(n, rs) = num_pair;
let &(ref n, ref rs) = num_pair;
for rs.iter().advance |str_pair| {
let &(radix, str) = str_pair;
assert_eq!(n.to_str_radix(radix), str);
let &(ref radix, ref str) = str_pair;
assert_eq!(&n.to_str_radix(*radix), str);
}
}
}
@ -1583,10 +1583,10 @@ mod biguint_tests {
fn test_from_str_radix() {
let r = to_str_pairs();
for r.iter().advance |num_pair| {
let &(n, rs) = num_pair;
let &(ref n, ref rs) = num_pair;
for rs.iter().advance |str_pair| {
let &(radix, str) = str_pair;
assert_eq!(&n, &FromStrRadix::from_str_radix(str, radix).get());
let &(ref radix, ref str) = str_pair;
assert_eq!(n, &FromStrRadix::from_str_radix(*str, *radix).get());
}
}

View File

@ -73,7 +73,7 @@ impl<T> Drop for Rc<T> {
if self.ptr.is_not_null() {
(*self.ptr).count -= 1;
if (*self.ptr).count == 0 {
ptr::replace_ptr(self.ptr, intrinsics::uninit());
ptr::read_ptr(self.ptr);
free(self.ptr as *c_void)
}
}

View File

@ -119,8 +119,8 @@ impl Terminal {
pub fn reset(&self) {
let mut vars = Variables::new();
let s = do self.ti.strings.find_equiv(&("op"))
.map_consume_default(Err(~"can't find terminfo capability `op`")) |&op| {
expand(op, [], &mut vars)
.map_consume_default(Err(~"can't find terminfo capability `op`")) |op| {
expand(copy *op, [], &mut vars)
};
if s.is_ok() {
self.out.write(s.unwrap());

View File

@ -81,8 +81,8 @@ pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
// Copy parameters into a local vector for mutability
let mut mparams = [Number(0), ..9];
for mparams.mut_iter().zip(params.iter()).advance |(dst, &src)| {
*dst = src;
for mparams.mut_iter().zip(params.iter()).advance |(dst, src)| {
*dst = copy *src;
}
for cap.iter().transform(|&x| x).advance |c| {

View File

@ -773,15 +773,15 @@ mod test_treemap {
map: &TreeMap<K, V>) {
assert_eq!(ctrl.is_empty(), map.is_empty());
for ctrl.iter().advance |x| {
let &(k, v) = x;
assert!(map.find(&k).unwrap() == &v)
let &(ref k, ref v) = x;
assert!(map.find(k).unwrap() == v)
}
for map.iter().advance |(map_k, map_v)| {
let mut found = false;
for ctrl.iter().advance |x| {
let &(ctrl_k, ctrl_v) = x;
if *map_k == ctrl_k {
assert!(*map_v == ctrl_v);
let &(ref ctrl_k, ref ctrl_v) = x;
if *map_k == *ctrl_k {
assert!(*map_v == *ctrl_v);
found = true;
break;
}

View File

@ -157,8 +157,8 @@ impl<D:Decoder> Decodable<D> for WorkMap {
fn decode(d: &mut D) -> WorkMap {
let v : ~[(WorkKey,~str)] = Decodable::decode(d);
let mut w = WorkMap::new();
for v.iter().advance |&(k, v)| {
w.insert(copy k, copy v);
for v.iter().advance |pair| {
w.insert(pair.first(), pair.second());
}
w
}

View File

@ -165,10 +165,10 @@ pub fn create_standard_passes(level: OptLevel) -> ~[~str] {
}
pub fn populate_pass_manager(sess: Session, pm: &mut PassManager, pass_list:&[~str]) {
for pass_list.iter().advance |&nm| {
match create_pass(nm) {
for pass_list.iter().advance |nm| {
match create_pass(*nm) {
Some(p) => pm.add_pass(p),
None => sess.warn(fmt!("Unknown pass %s", nm))
None => sess.warn(fmt!("Unknown pass %s", *nm))
}
}
}

View File

@ -314,7 +314,6 @@ pub fn compile_rest(sess: Session,
method_map: method_map,
vtable_map: vtable_map,
write_guard_map: write_guard_map,
moves_map: moves_map,
capture_map: capture_map
};

View File

@ -127,7 +127,9 @@ fn find_library_crate_aux(
cx.diag.span_err(
cx.span, fmt!("multiple matching crates for `%s`", crate_name));
cx.diag.handler().note("candidates:");
for matches.iter().advance |&(ident, data)| {
for matches.iter().advance |pair| {
let ident = pair.first();
let data = pair.second();
cx.diag.handler().note(fmt!("path: %s", ident));
let attrs = decoder::get_crate_attributes(data);
note_linkage_attrs(cx.intr, cx.diag, attrs);

View File

@ -53,7 +53,6 @@ pub struct Maps {
method_map: middle::typeck::method_map,
vtable_map: middle::typeck::vtable_map,
write_guard_map: middle::borrowck::write_guard_map,
moves_map: middle::moves::MovesMap,
capture_map: middle::moves::CaptureMap,
}
@ -952,12 +951,6 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext,
}
}
if maps.moves_map.contains(&id) {
do ebml_w.tag(c::tag_table_moves_map) |ebml_w| {
ebml_w.id(id);
}
}
{
let r = maps.capture_map.find(&id);
for r.iter().advance |&cap_vars| {
@ -1121,9 +1114,7 @@ fn decode_side_tables(xcx: @ExtendedDecodeContext,
xcx.dcx.tcx.sess.bug(
fmt!("unknown tag found in side tables: %x", tag));
}
Some(value) => if value == c::tag_table_moves_map {
dcx.maps.moves_map.insert(id);
} else {
Some(value) => {
let val_doc = entry_doc.get(c::tag_table_val as uint);
let mut val_dsr = reader::Decoder(val_doc);
let val_dsr = &mut val_dsr;

View File

@ -65,7 +65,7 @@ pub fn check_loans(bccx: @BorrowckCtxt,
enum MoveError {
MoveOk,
MoveWhileBorrowed(/*move*/@LoanPath, /*loan*/@LoanPath, /*loan*/span)
MoveWhileBorrowed(/*loan*/@LoanPath, /*loan*/span)
}
impl<'self> CheckLoanCtxt<'self> {
@ -348,7 +348,7 @@ impl<'self> CheckLoanCtxt<'self> {
cmt = b;
}
mc::cat_rvalue |
mc::cat_rvalue(*) |
mc::cat_static_item |
mc::cat_implicit_self |
mc::cat_copied_upvar(*) |
@ -547,45 +547,50 @@ impl<'self> CheckLoanCtxt<'self> {
self.bccx.loan_path_to_str(loan_path)));
}
pub fn check_move_out_from_expr(&self, ex: @ast::expr) {
match ex.node {
ast::expr_paren(*) => {
/* In the case of an expr_paren(), the expression inside
* the parens will also be marked as being moved. Ignore
* the parents then so as not to report duplicate errors. */
fn check_move_out_from_expr(&self, expr: @ast::expr) {
match expr.node {
ast::expr_fn_block(*) => {
// moves due to capture clauses are checked
// in `check_loans_in_fn`, so that we can
// give a better error message
}
_ => {
let cmt = self.bccx.cat_expr(ex);
match self.analyze_move_out_from_cmt(cmt) {
MoveOk => {}
MoveWhileBorrowed(move_path, loan_path, loan_span) => {
self.bccx.span_err(
cmt.span,
fmt!("cannot move out of `%s` \
because it is borrowed",
self.bccx.loan_path_to_str(move_path)));
self.bccx.span_note(
loan_span,
fmt!("borrow of `%s` occurs here",
self.bccx.loan_path_to_str(loan_path)));
}
self.check_move_out_from_id(expr.id, expr.span)
}
}
}
fn check_move_out_from_id(&self, id: ast::node_id, span: span) {
for self.move_data.each_path_moved_by(id) |_, move_path| {
match self.analyze_move_out_from(id, move_path) {
MoveOk => {}
MoveWhileBorrowed(loan_path, loan_span) => {
self.bccx.span_err(
span,
fmt!("cannot move out of `%s` \
because it is borrowed",
self.bccx.loan_path_to_str(move_path)));
self.bccx.span_note(
loan_span,
fmt!("borrow of `%s` occurs here",
self.bccx.loan_path_to_str(loan_path)));
}
}
}
}
pub fn analyze_move_out_from_cmt(&self, cmt: mc::cmt) -> MoveError {
debug!("analyze_move_out_from_cmt(cmt=%s)", cmt.repr(self.tcx()));
pub fn analyze_move_out_from(&self,
expr_id: ast::node_id,
move_path: @LoanPath) -> MoveError {
debug!("analyze_move_out_from(expr_id=%?, move_path=%s)",
expr_id, move_path.repr(self.tcx()));
// FIXME(#4384) inadequare if/when we permit `move a.b`
// check for a conflicting loan:
let r = opt_loan_path(cmt);
for r.iter().advance |&lp| {
for self.each_in_scope_restriction(cmt.id, lp) |loan, _| {
// Any restriction prevents moves.
return MoveWhileBorrowed(lp, loan.loan_path, loan.span);
}
for self.each_in_scope_restriction(expr_id, move_path) |loan, _| {
// Any restriction prevents moves.
return MoveWhileBorrowed(loan.loan_path, loan.span);
}
MoveOk
@ -652,13 +657,11 @@ fn check_loans_in_fn<'a>(fk: &visit::fn_kind,
closure_id: ast::node_id,
cap_var: &moves::CaptureVar) {
let var_id = ast_util::def_id_of_def(cap_var.def).node;
let ty = ty::node_id_to_type(this.tcx(), var_id);
let cmt = this.bccx.cat_def(closure_id, cap_var.span,
ty, cap_var.def);
let move_err = this.analyze_move_out_from_cmt(cmt);
let move_path = @LpVar(var_id);
let move_err = this.analyze_move_out_from(closure_id, move_path);
match move_err {
MoveOk => {}
MoveWhileBorrowed(move_path, loan_path, loan_span) => {
MoveWhileBorrowed(loan_path, loan_span) => {
this.bccx.span_err(
cap_var.span,
fmt!("cannot move `%s` into closure \
@ -689,10 +692,7 @@ fn check_loans_in_expr<'a>(expr: @ast::expr,
expr.repr(this.tcx()));
this.check_for_conflicting_loans(expr.id);
if this.bccx.moves_map.contains(&expr.id) {
this.check_move_out_from_expr(expr);
}
this.check_move_out_from_expr(expr);
match expr.node {
ast::expr_self |
@ -742,18 +742,7 @@ fn check_loans_in_pat<'a>(pat: @ast::pat,
visit::vt<@mut CheckLoanCtxt<'a>>))
{
this.check_for_conflicting_loans(pat.id);
// Note: moves out of pattern bindings are not checked by
// the borrow checker, at least not directly. What happens
// is that if there are any moved bindings, the discriminant
// will be considered a move, and this will be checked as
// normal. Then, in `middle::check_match`, we will check
// that no move occurs in a binding that is underneath an
// `@` or `&`. Together these give the same guarantees as
// `check_move_out_from_expr()` without requiring us to
// rewalk the patterns and rebuild the pattern
// categorizations.
this.check_move_out_from_id(pat.id, pat.span);
visit::visit_pat(pat, (this, vt));
}

View File

@ -67,7 +67,7 @@ impl GuaranteeLifetimeContext {
//! Main routine. Walks down `cmt` until we find the "guarantor".
match cmt.cat {
mc::cat_rvalue |
mc::cat_rvalue(*) |
mc::cat_implicit_self |
mc::cat_copied_upvar(*) | // L-Local
mc::cat_local(*) | // L-Local
@ -179,7 +179,7 @@ impl GuaranteeLifetimeContext {
//! lvalue.
cmt.mutbl.is_immutable() || match cmt.guarantor().cat {
mc::cat_rvalue => true,
mc::cat_rvalue(*) => true,
_ => false
}
}
@ -299,7 +299,7 @@ impl GuaranteeLifetimeContext {
mc::cat_arg(id) => {
self.bccx.moved_variables_set.contains(&id)
}
mc::cat_rvalue |
mc::cat_rvalue(*) |
mc::cat_static_item |
mc::cat_implicit_self |
mc::cat_copied_upvar(*) |
@ -325,8 +325,8 @@ impl GuaranteeLifetimeContext {
// See the SCOPE(LV) function in doc.rs
match cmt.cat {
mc::cat_rvalue => {
ty::re_scope(self.bccx.tcx.region_maps.cleanup_scope(cmt.id))
mc::cat_rvalue(cleanup_scope_id) => {
ty::re_scope(cleanup_scope_id)
}
mc::cat_implicit_self |
mc::cat_copied_upvar(_) => {

View File

@ -73,6 +73,7 @@ struct GatherLoanCtxt {
}
pub fn gather_loans(bccx: @BorrowckCtxt,
decl: &ast::fn_decl,
body: &ast::blk)
-> (id_range, @mut ~[Loan], @mut move_data::MoveData) {
let glcx = @mut GatherLoanCtxt {
@ -83,6 +84,7 @@ pub fn gather_loans(bccx: @BorrowckCtxt,
repeating_ids: ~[body.node.id],
move_data: @mut MoveData::new()
};
glcx.gather_fn_arg_patterns(decl, body);
let v = visit::mk_vt(@visit::Visitor {visit_expr: gather_loans_in_expr,
visit_block: gather_loans_in_block,
visit_fn: gather_loans_in_fn,
@ -124,6 +126,7 @@ fn gather_loans_in_fn(fk: &visit::fn_kind,
this.push_repeating_id(body.node.id);
visit::visit_fn(fk, decl, body, sp, id, (this, v));
this.pop_repeating_id(body.node.id);
this.gather_fn_arg_patterns(decl, body);
}
}
}
@ -138,26 +141,33 @@ fn gather_loans_in_block(blk: &ast::blk,
fn gather_loans_in_local(local: @ast::local,
(this, vt): (@mut GatherLoanCtxt,
visit::vt<@mut GatherLoanCtxt>)) {
if local.node.init.is_none() {
// Variable declarations without initializers are considered "moves":
let tcx = this.bccx.tcx;
do pat_util::pat_bindings(tcx.def_map, local.node.pat) |_, id, span, _| {
gather_moves::gather_decl(this.bccx,
this.move_data,
id,
span,
id);
match local.node.init {
None => {
// Variable declarations without initializers are considered "moves":
let tcx = this.bccx.tcx;
do pat_util::pat_bindings(tcx.def_map, local.node.pat)
|_, id, span, _| {
gather_moves::gather_decl(this.bccx,
this.move_data,
id,
span,
id);
}
}
} else {
// Variable declarations with initializers are considered "assigns":
let tcx = this.bccx.tcx;
do pat_util::pat_bindings(tcx.def_map, local.node.pat) |_, id, span, _| {
gather_moves::gather_assignment(this.bccx,
this.move_data,
id,
span,
@LpVar(id),
id);
Some(init) => {
// Variable declarations with initializers are considered "assigns":
let tcx = this.bccx.tcx;
do pat_util::pat_bindings(tcx.def_map, local.node.pat)
|_, id, span, _| {
gather_moves::gather_assignment(this.bccx,
this.move_data,
id,
span,
@LpVar(id),
id);
}
let init_cmt = this.bccx.cat_expr(init);
this.gather_pat(init_cmt, local.node.pat, None);
}
}
@ -230,7 +240,7 @@ fn gather_loans_in_expr(ex: @ast::expr,
let cmt = this.bccx.cat_expr(ex_v);
for arms.iter().advance |arm| {
for arm.pats.iter().advance |pat| {
this.gather_pat(cmt, *pat, arm.body.node.id, ex.id);
this.gather_pat(cmt, *pat, Some((arm.body.node.id, ex.id)));
}
}
visit::visit_expr(ex, (this, vt));
@ -596,11 +606,40 @@ impl GatherLoanCtxt {
}
}
pub fn gather_pat(&mut self,
discr_cmt: mc::cmt,
root_pat: @ast::pat,
arm_body_id: ast::node_id,
match_id: ast::node_id) {
fn gather_fn_arg_patterns(&mut self,
decl: &ast::fn_decl,
body: &ast::blk) {
/*!
* Walks the patterns for fn arguments, checking that they
* do not attempt illegal moves or create refs that outlive
* the arguments themselves. Just a shallow wrapper around
* `gather_pat()`.
*/
let mc_ctxt = self.bccx.mc_ctxt();
for decl.inputs.iter().advance |arg| {
let arg_ty = ty::node_id_to_type(self.tcx(), arg.pat.id);
let arg_cmt = mc_ctxt.cat_rvalue(
arg.id,
arg.pat.span,
body.node.id, // Arguments live only as long as the fn body.
arg_ty);
self.gather_pat(arg_cmt, arg.pat, None);
}
}
fn gather_pat(&mut self,
discr_cmt: mc::cmt,
root_pat: @ast::pat,
arm_match_ids: Option<(ast::node_id, ast::node_id)>) {
/*!
* Walks patterns, examining the bindings to determine if they
* cause borrows (`ref` bindings, vector patterns) or
* moves (non-`ref` bindings with linear type).
*/
do self.bccx.cat_pattern(discr_cmt, root_pat) |cmt, pat| {
match pat.node {
ast::pat_ident(bm, _, _) if self.pat_is_binding(pat) => {
@ -621,15 +660,19 @@ impl GatherLoanCtxt {
// with a cat_discr() node. There is a detailed
// discussion of the function of this node in
// `lifetime.rs`:
let arm_scope = ty::re_scope(arm_body_id);
if self.bccx.is_subregion_of(scope_r, arm_scope) {
let cmt_discr = self.bccx.cat_discr(cmt, match_id);
self.guarantee_valid(pat.id, pat.span,
cmt_discr, mutbl, scope_r);
} else {
self.guarantee_valid(pat.id, pat.span,
cmt, mutbl, scope_r);
}
let cmt_discr = match arm_match_ids {
None => cmt,
Some((arm_id, match_id)) => {
let arm_scope = ty::re_scope(arm_id);
if self.bccx.is_subregion_of(scope_r, arm_scope) {
self.bccx.cat_discr(cmt, match_id)
} else {
cmt
}
}
};
self.guarantee_valid(pat.id, pat.span,
cmt_discr, mutbl, scope_r);
}
ast::bind_infer => {
// No borrows here, but there may be moves
@ -652,6 +695,24 @@ impl GatherLoanCtxt {
self.vec_slice_info(slice_pat, slice_ty);
let mcx = self.bccx.mc_ctxt();
let cmt_index = mcx.cat_index(slice_pat, cmt, 0);
// Note: We declare here that the borrow occurs upon
// entering the `[...]` pattern. This implies that
// something like `[a, ..b]` where `a` is a move is
// illegal, because the borrow is already in effect.
// In fact such a move would be safe-ish, but it
// effectively *requires* that we use the nulling
// out semantics to indicate when a value has been
// moved, which we are trying to move away from.
// Otherwise, how can we indicate that the first
// element in the vector has been moved?
// Eventually, we could perhaps modify this rule to
// permit `[..a, b]` where `b` is a move, because in
// that case we can adjust the length of the
// original vec accordingly, but we'd have to make
// trans do the right thing, and it would only work
// for `~` vectors. It seems simpler to just require
// that people call `vec.pop()` or `vec.unshift()`.
self.guarantee_valid(pat.id, pat.span,
cmt_index, slice_mutbl, slice_r);
}

View File

@ -64,7 +64,7 @@ impl RestrictionsContext {
}
match cmt.cat {
mc::cat_rvalue => {
mc::cat_rvalue(*) => {
// Effectively, rvalues are stored into a
// non-aliasable temporary on the stack. Since they
// are inherently non-aliasable, they can only be

View File

@ -124,7 +124,7 @@ fn borrowck_fn(fk: &visit::fn_kind,
// Check the body of fn items.
let (id_range, all_loans, move_data) =
gather_loans::gather_loans(this, body);
gather_loans::gather_loans(this, decl, body);
let mut loan_dfcx =
DataFlowContext::new(this.tcx,
this.method_map,
@ -264,7 +264,7 @@ pub fn opt_loan_path(cmt: mc::cmt) -> Option<@LoanPath> {
//! traverses the CMT.
match cmt.cat {
mc::cat_rvalue |
mc::cat_rvalue(*) |
mc::cat_static_item |
mc::cat_copied_upvar(_) |
mc::cat_implicit_self => {
@ -485,7 +485,7 @@ impl BorrowckCtxt {
pub fn mc_ctxt(&self) -> mc::mem_categorization_ctxt {
mc::mem_categorization_ctxt {tcx: self.tcx,
method_map: self.method_map}
method_map: self.method_map}
}
pub fn cat_pattern(&self,

View File

@ -474,6 +474,24 @@ impl FlowedMoveData {
}
}
pub fn each_path_moved_by(&self,
id: ast::node_id,
f: &fn(&Move, @LoanPath) -> bool)
-> bool {
/*!
* Iterates through each path moved by `id`
*/
for self.dfcx_moves.each_gen_bit_frozen(id) |index| {
let move = &self.move_data.moves[index];
let moved_path = move.path;
if !f(move, self.move_data.path(moved_path).loan_path) {
return false;
}
}
return true;
}
pub fn each_move_of(&self,
id: ast::node_id,
loan_path: @LoanPath,

View File

@ -49,23 +49,13 @@ pub fn check_crate(tcx: ty::ctxt,
tcx.sess.abort_if_errors();
}
pub fn expr_is_non_moving_lvalue(cx: &MatchCheckCtxt, expr: &expr) -> bool {
if !ty::expr_is_lval(cx.tcx, cx.method_map, expr) {
return false;
}
!cx.moves_map.contains(&expr.id)
}
pub fn check_expr(cx: @MatchCheckCtxt, ex: @expr, (s, v): ((), visit::vt<()>)) {
visit::visit_expr(ex, (s, v));
match ex.node {
expr_match(scrut, ref arms) => {
// First, check legality of move bindings.
let is_non_moving_lvalue = expr_is_non_moving_lvalue(cx, ex);
for arms.iter().advance |arm| {
check_legality_of_move_bindings(cx,
is_non_moving_lvalue,
arm.guard.is_some(),
arm.pats);
}
@ -758,11 +748,7 @@ pub fn check_local(cx: &MatchCheckCtxt,
}
// Check legality of move bindings.
let is_lvalue = match loc.node.init {
Some(init) => expr_is_non_moving_lvalue(cx, init),
None => true
};
check_legality_of_move_bindings(cx, is_lvalue, false, [ loc.node.pat ]);
check_legality_of_move_bindings(cx, false, [ loc.node.pat ]);
}
pub fn check_fn(cx: &MatchCheckCtxt,
@ -821,7 +807,6 @@ pub fn is_refutable(cx: &MatchCheckCtxt, pat: &pat) -> bool {
// Legality of move bindings checking
pub fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
is_lvalue: bool,
has_guard: bool,
pats: &[@pat]) {
let tcx = cx.tcx;
@ -861,11 +846,6 @@ pub fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
tcx.sess.span_note(
by_ref_span.get(),
"by-ref binding occurs here");
} else if is_lvalue {
tcx.sess.span_err(
p.span,
"cannot bind by-move when \
matching an lvalue");
}
};

View File

@ -187,7 +187,6 @@ pub fn lookup_const_by_id(tcx: ty::ctxt,
method_map: @mut HashMap::new(),
vtable_map: @mut HashMap::new(),
write_guard_map: @mut HashSet::new(),
moves_map: @mut HashSet::new(),
capture_map: @mut HashMap::new()
};
match csearch::maybe_get_item_ast(tcx, def_id,

View File

@ -422,8 +422,8 @@ impl<'self, O:DataFlowOperator> PropagationContext<'self, O> {
loop_scopes: &mut ~[LoopScope]) {
match decl.node {
ast::decl_local(local) => {
self.walk_pat(local.node.pat, in_out, loop_scopes);
self.walk_opt_expr(local.node.init, in_out, loop_scopes);
self.walk_pat(local.node.pat, in_out, loop_scopes);
}
ast::decl_item(_) => {}

View File

@ -60,7 +60,7 @@ use syntax::print::pprust;
#[deriving(Eq)]
pub enum categorization {
cat_rvalue, // result of eval'ing some misc expr
cat_rvalue(ast::node_id), // temporary val, argument is its scope
cat_static_item,
cat_implicit_self,
cat_copied_upvar(CopiedUpvar), // upvar copied into @fn or ~fn env
@ -350,7 +350,7 @@ impl mem_categorization_ctxt {
// Convert a bare fn to a closure by adding NULL env.
// Result is an rvalue.
let expr_ty = ty::expr_ty_adjusted(self.tcx, expr);
self.cat_rvalue(expr, expr_ty)
self.cat_rvalue_node(expr, expr_ty)
}
Some(
@ -360,7 +360,7 @@ impl mem_categorization_ctxt {
// Equivalent to &*expr or something similar.
// Result is an rvalue.
let expr_ty = ty::expr_ty_adjusted(self.tcx, expr);
self.cat_rvalue(expr, expr_ty)
self.cat_rvalue_node(expr, expr_ty)
}
Some(
@ -390,7 +390,7 @@ impl mem_categorization_ctxt {
match expr.node {
ast::expr_unary(_, ast::deref, e_base) => {
if self.method_map.contains_key(&expr.id) {
return self.cat_rvalue(expr, expr_ty);
return self.cat_rvalue_node(expr, expr_ty);
}
let base_cmt = self.cat_expr(e_base);
@ -408,7 +408,7 @@ impl mem_categorization_ctxt {
ast::expr_index(_, base, _) => {
if self.method_map.contains_key(&expr.id) {
return self.cat_rvalue(expr, expr_ty);
return self.cat_rvalue_node(expr, expr_ty);
}
let base_cmt = self.cat_expr(base);
@ -433,7 +433,7 @@ impl mem_categorization_ctxt {
ast::expr_match(*) | ast::expr_lit(*) | ast::expr_break(*) |
ast::expr_mac(*) | ast::expr_again(*) | ast::expr_struct(*) |
ast::expr_repeat(*) | ast::expr_inline_asm(*) => {
return self.cat_rvalue(expr, expr_ty);
return self.cat_rvalue_node(expr, expr_ty);
}
}
}
@ -577,11 +577,24 @@ impl mem_categorization_ctxt {
}
}
pub fn cat_rvalue<N:ast_node>(&self, elt: N, expr_ty: ty::t) -> cmt {
pub fn cat_rvalue_node<N:ast_node>(&self,
node: N,
expr_ty: ty::t) -> cmt {
self.cat_rvalue(node.id(),
node.span(),
self.tcx.region_maps.cleanup_scope(node.id()),
expr_ty)
}
pub fn cat_rvalue(&self,
cmt_id: ast::node_id,
span: span,
cleanup_scope_id: ast::node_id,
expr_ty: ty::t) -> cmt {
@cmt_ {
id:elt.id(),
span:elt.span(),
cat:cat_rvalue,
id:cmt_id,
span:span,
cat:cat_rvalue(cleanup_scope_id),
mutbl:McDeclared,
ty:expr_ty
}
@ -970,7 +983,7 @@ impl mem_categorization_ctxt {
}
for slice.iter().advance |&slice_pat| {
let slice_ty = self.pat_ty(slice_pat);
let slice_cmt = self.cat_rvalue(pat, slice_ty);
let slice_cmt = self.cat_rvalue_node(pat, slice_ty);
self.cat_pattern(slice_cmt, slice_pat, |x,y| op(x,y));
}
for after.iter().advance |&after_pat| {
@ -1003,7 +1016,7 @@ impl mem_categorization_ctxt {
cat_copied_upvar(_) => {
~"captured outer variable in a heap closure"
}
cat_rvalue => {
cat_rvalue(*) => {
~"non-lvalue"
}
cat_local(_) => {
@ -1100,7 +1113,7 @@ impl cmt_ {
//! determines how long the value in `self` remains live.
match self.cat {
cat_rvalue |
cat_rvalue(*) |
cat_static_item |
cat_implicit_self |
cat_copied_upvar(*) |
@ -1187,11 +1200,13 @@ impl Repr for categorization {
match *self {
cat_static_item |
cat_implicit_self |
cat_rvalue |
cat_rvalue(*) |
cat_copied_upvar(*) |
cat_local(*) |
cat_self(*) |
cat_arg(*) => fmt!("%?", *self),
cat_arg(*) => {
fmt!("%?", *self)
}
cat_deref(cmt, derefs, ptr) => {
fmt!("%s->(%s, %u)", cmt.cat.repr(tcx),
ptr_sigil(ptr), derefs)
@ -1205,7 +1220,9 @@ impl Repr for categorization {
fmt!("%s->(enum)", cmt.cat.repr(tcx))
}
cat_stack_upvar(cmt) |
cat_discr(cmt, _) => cmt.cat.repr(tcx)
cat_discr(cmt, _) => {
cmt.cat.repr(tcx)
}
}
}
}

View File

@ -193,7 +193,9 @@ pub fn compute_moves(tcx: ty::ctxt,
crate: &crate) -> MoveMaps
{
let visitor = visit::mk_vt(@visit::Visitor {
visit_fn: compute_modes_for_fn,
visit_expr: compute_modes_for_expr,
visit_local: compute_modes_for_local,
.. *visit::default_visitor()
});
let visit_cx = VisitContext {
@ -220,9 +222,31 @@ pub fn moved_variable_node_id_from_def(def: def) -> Option<node_id> {
}
}
// ______________________________________________________________________
///////////////////////////////////////////////////////////////////////////
// Expressions
fn compute_modes_for_local<'a>(local: @local,
(cx, v): (VisitContext,
vt<VisitContext>)) {
cx.use_pat(local.node.pat);
for local.node.init.iter().advance |&init| {
cx.use_expr(init, Read, v);
}
}
fn compute_modes_for_fn(fk: &visit::fn_kind,
decl: &fn_decl,
body: &blk,
span: span,
id: node_id,
(cx, v): (VisitContext,
vt<VisitContext>)) {
for decl.inputs.iter().advance |a| {
cx.use_pat(a.pat);
}
visit::visit_fn(fk, decl, body, span, id, (cx, v));
}
fn compute_modes_for_expr(expr: @expr,
(cx, v): (VisitContext,
vt<VisitContext>))
@ -522,7 +546,10 @@ impl VisitContext {
self.use_expr(base, comp_mode, visitor);
}
expr_fn_block(_, ref body) => {
expr_fn_block(ref decl, ref body) => {
for decl.inputs.iter().advance |a| {
self.use_pat(a.pat);
}
let cap_vars = self.compute_captures(expr.id);
self.move_maps.capture_map.insert(expr.id, cap_vars);
self.consume_block(body, visitor);
@ -580,13 +607,15 @@ impl VisitContext {
* into itself or not based on its type and annotation.
*/
do pat_bindings(self.tcx.def_map, pat) |bm, id, _span, _path| {
do pat_bindings(self.tcx.def_map, pat) |bm, id, _span, path| {
let binding_moves = match bm {
bind_by_ref(_) => false,
bind_infer => {
let pat_ty = ty::node_id_to_type(self.tcx, id);
debug!("pattern %? type is %s",
id, pat_ty.repr(self.tcx));
debug!("pattern %? %s type is %s",
id,
ast_util::path_to_ident(path).repr(self.tcx),
pat_ty.repr(self.tcx));
ty::type_moves_by_default(self.tcx, pat_ty)
}
};

View File

@ -268,7 +268,7 @@ pub fn trans_opt(bcx: block, o: &Opt) -> opt_result {
}
lit(UnitLikeStructLit(pat_id)) => {
let struct_ty = ty::node_id_to_type(bcx.tcx(), pat_id);
let datumblock = datum::scratch_datum(bcx, struct_ty, true);
let datumblock = datum::scratch_datum(bcx, struct_ty, "", true);
return single_result(datumblock.to_result(bcx));
}
lit(ConstLit(lit_id)) => {
@ -316,7 +316,7 @@ pub fn variant_opt(bcx: block, pat_id: ast::node_id)
}
pub enum TransBindingMode {
TrByValue(/*ismove:*/ bool, /*llbinding:*/ ValueRef),
TrByValue(/*llbinding:*/ ValueRef),
TrByRef,
}
@ -927,7 +927,7 @@ pub fn extract_vec_elems(bcx: block,
ty::mt {ty: vt.unit_ty, mutbl: ast::m_imm},
ty::vstore_slice(ty::re_static)
);
let scratch = scratch_datum(bcx, slice_ty, false);
let scratch = scratch_datum(bcx, slice_ty, "", false);
Store(bcx, slice_begin,
GEPi(bcx, scratch.val, [0u, abi::slice_elt_base])
);
@ -1095,9 +1095,9 @@ pub fn compare_values(cx: block,
match ty::get(rhs_t).sty {
ty::ty_estr(ty::vstore_uniq) => {
let scratch_lhs = alloca(cx, val_ty(lhs));
let scratch_lhs = alloca(cx, val_ty(lhs), "__lhs");
Store(cx, lhs, scratch_lhs);
let scratch_rhs = alloca(cx, val_ty(rhs));
let scratch_rhs = alloca(cx, val_ty(rhs), "__rhs");
Store(cx, rhs, scratch_rhs);
let did = cx.tcx().lang_items.uniq_str_eq_fn();
let result = callee::trans_lang_call(cx, did, [scratch_lhs, scratch_rhs], None);
@ -1138,18 +1138,11 @@ fn store_non_ref_bindings(bcx: block,
let mut bcx = bcx;
for bindings_map.each_value |&binding_info| {
match binding_info.trmode {
TrByValue(is_move, lldest) => {
TrByValue(lldest) => {
let llval = Load(bcx, binding_info.llmatch); // get a T*
let datum = Datum {val: llval, ty: binding_info.ty,
mode: ByRef(ZeroMem)};
bcx = {
if is_move {
datum.move_to(bcx, INIT, lldest)
} else {
datum.copy_to(bcx, INIT, lldest)
}
};
bcx = datum.store_to(bcx, INIT, lldest);
do opt_temp_cleanups.mutate |temp_cleanups| {
add_clean_temp_mem(bcx, lldest, binding_info.ty);
temp_cleanups.push(lldest);
@ -1181,7 +1174,7 @@ fn insert_lllocals(bcx: block,
let llval = match binding_info.trmode {
// By value bindings: use the stack slot that we
// copied/moved the value into
TrByValue(_, lldest) => {
TrByValue(lldest) => {
if add_cleans {
add_clean(bcx, lldest, binding_info.ty);
}
@ -1245,7 +1238,7 @@ pub fn compile_guard(bcx: block,
let mut bcx = bcx;
for data.bindings_map.each_value |&binding_info| {
match binding_info.trmode {
TrByValue(_, llval) => {
TrByValue(llval) => {
bcx = glue::drop_ty(bcx, llval, binding_info.ty);
}
TrByRef => {}
@ -1636,12 +1629,12 @@ fn create_bindings_map(bcx: block, pat: @ast::pat) -> BindingsMap {
// in this case, the final type of the variable will be T,
// but during matching we need to store a *T as explained
// above
let is_move = ccx.maps.moves_map.contains(&p_id);
llmatch = alloca(bcx, llvariable_ty.ptr_to());
trmode = TrByValue(is_move, alloca(bcx, llvariable_ty));
llmatch = alloca(bcx, llvariable_ty.ptr_to(), "__llmatch");
trmode = TrByValue(alloca(bcx, llvariable_ty,
bcx.ident(ident)));
}
ast::bind_by_ref(_) => {
llmatch = alloca(bcx, llvariable_ty);
llmatch = alloca(bcx, llvariable_ty, bcx.ident(ident));
trmode = TrByRef;
}
};
@ -1737,53 +1730,205 @@ pub enum IrrefutablePatternBindingMode {
BindArgument
}
// Not match-related, but similar to the pattern-munging code above
pub fn bind_irrefutable_pat(bcx: block,
pat: @ast::pat,
val: ValueRef,
make_copy: bool,
binding_mode: IrrefutablePatternBindingMode)
-> block {
let _icx = push_ctxt("match::bind_irrefutable_pat");
let ccx = bcx.fcx.ccx;
pub fn store_local(bcx: block,
pat: @ast::pat,
opt_init_expr: Option<@ast::expr>)
-> block {
/*!
* Generates code for a local variable declaration like
* `let <pat>;` or `let <pat> = <opt_init_expr>`.
*/
let _icx = push_ctxt("match::store_local");
let mut bcx = bcx;
// Necessary since bind_irrefutable_pat is called outside trans_match
match pat.node {
ast::pat_ident(_, _, ref inner) => {
if pat_is_variant_or_struct(bcx.tcx().def_map, pat) {
return bcx;
return match opt_init_expr {
Some(init_expr) => {
// Optimize the "let x = expr" case. This just writes
// the result of evaluating `expr` directly into the alloca
// for `x`. Often the general path results in similar or the
// same code post-optimization, but not always. In particular,
// in unsafe code, you can have expressions like
//
// let x = intrinsics::uninit();
//
// In such cases, the more general path is unsafe, because
// it assumes it is matching against a valid value.
match simple_identifier(pat) {
Some(path) => {
return mk_binding_alloca(
bcx, pat.id, path, BindLocal,
|bcx, _, llval| expr::trans_into(bcx, init_expr,
expr::SaveIn(llval)));
}
None => {}
}
if make_copy {
let binding_ty = node_id_type(bcx, pat.id);
let datum = Datum {val: val, ty: binding_ty,
mode: ByRef(RevokeClean)};
let scratch = scratch_datum(bcx, binding_ty, false);
datum.copy_to_datum(bcx, INIT, scratch);
match binding_mode {
BindLocal => {
bcx.fcx.lllocals.insert(pat.id, scratch.val);
}
BindArgument => {
bcx.fcx.llargs.insert(pat.id, scratch.val);
}
}
add_clean(bcx, scratch.val, binding_ty);
// General path.
let init_datum =
unpack_datum!(
bcx,
expr::trans_to_datum(bcx, init_expr));
if ty::type_is_bot(expr_ty(bcx, init_expr)) {
create_dummy_locals(bcx, pat)
} else {
match binding_mode {
BindLocal => {
bcx.fcx.lllocals.insert(pat.id, val);
}
BindArgument => {
bcx.fcx.llargs.insert(pat.id, val);
}
if bcx.sess().asm_comments() {
add_comment(bcx, "creating zeroable ref llval");
}
let llptr = init_datum.to_zeroable_ref_llval(bcx);
return bind_irrefutable_pat(bcx, pat, llptr, BindLocal);
}
}
None => {
create_dummy_locals(bcx, pat)
}
};
fn create_dummy_locals(mut bcx: block, pat: @ast::pat) -> block {
// create dummy memory for the variables if we have no
// value to store into them immediately
let tcx = bcx.tcx();
do pat_bindings(tcx.def_map, pat) |_, p_id, _, path| {
bcx = mk_binding_alloca(
bcx, p_id, path, BindLocal,
|bcx, var_ty, llval| { zero_mem(bcx, llval, var_ty); bcx });
}
bcx
}
}
pub fn store_arg(mut bcx: block,
pat: @ast::pat,
llval: ValueRef)
-> block {
/*!
* Generates code for argument patterns like `fn foo(<pat>: T)`.
* Creates entries in the `llargs` map for each of the bindings
* in `pat`.
*
* # Arguments
*
* - `pat` is the argument pattern
* - `llval` is a pointer to the argument value (in other words,
* if the argument type is `T`, then `llval` is a `T*`). In some
* cases, this code may zero out the memory `llval` points at.
*/
let _icx = push_ctxt("match::store_arg");
// We always need to cleanup the argument as we exit the fn scope.
// Note that we cannot do it before for fear of a fn like
// fn getaddr(~ref x: ~uint) -> *uint {....}
// (From test `run-pass/func-arg-ref-pattern.rs`)
let arg_ty = node_id_type(bcx, pat.id);
add_clean(bcx, llval, arg_ty);
match simple_identifier(pat) {
Some(_) => {
// Optimized path for `x: T` case. This just adopts
// `llval` wholesale as the pointer for `x`, avoiding the
// general logic which may copy out of `llval`.
bcx.fcx.llargs.insert(pat.id, llval);
}
None => {
// General path. Copy out the values that are used in the
// pattern.
bcx = bind_irrefutable_pat(bcx, pat, llval, BindArgument);
}
}
return bcx;
}
fn mk_binding_alloca(mut bcx: block,
p_id: ast::node_id,
path: &ast::Path,
binding_mode: IrrefutablePatternBindingMode,
populate: &fn(block, ty::t, ValueRef) -> block) -> block {
let var_ty = node_id_type(bcx, p_id);
let ident = ast_util::path_to_ident(path);
let llval = alloc_ty(bcx, var_ty, bcx.ident(ident));
bcx = populate(bcx, var_ty, llval);
let llmap = match binding_mode {
BindLocal => bcx.fcx.lllocals,
BindArgument => bcx.fcx.llargs
};
llmap.insert(p_id, llval);
add_clean(bcx, llval, var_ty);
return bcx;
}
fn bind_irrefutable_pat(bcx: block,
pat: @ast::pat,
val: ValueRef,
binding_mode: IrrefutablePatternBindingMode)
-> block {
/*!
* A simple version of the pattern matching code that only handles
* irrefutable patterns. This is used in let/argument patterns,
* not in match statements. Unifying this code with the code above
* sounds nice, but in practice it produces very inefficient code,
* since the match code is so much more general. In most cases,
* LLVM is able to optimize the code, but it causes longer compile
* times and makes the generated code nigh impossible to read.
*
* # Arguments
* - bcx: starting basic block context
* - pat: the irrefutable pattern being matched.
* - val: a pointer to the value being matched. If pat matches a value
* of type T, then this is a T*. If the value is moved from `pat`,
* then `*pat` will be zeroed; otherwise, it's existing cleanup
* applies.
* - binding_mode: is this for an argument or a local variable?
*/
debug!("bind_irrefutable_pat(bcx=%s, pat=%s, binding_mode=%?)",
bcx.to_str(),
pat_to_str(pat, bcx.sess().intr()),
binding_mode);
if bcx.sess().asm_comments() {
add_comment(bcx, fmt!("bind_irrefutable_pat(pat=%s)",
pat_to_str(pat, bcx.sess().intr())));
}
let _indenter = indenter();
let _icx = push_ctxt("alt::bind_irrefutable_pat");
let mut bcx = bcx;
let tcx = bcx.tcx();
let ccx = bcx.ccx();
match pat.node {
ast::pat_ident(pat_binding_mode, ref path, inner) => {
if pat_is_binding(tcx.def_map, pat) {
// Allocate the stack slot where the value of this
// binding will live and place it into the appropriate
// map.
bcx = mk_binding_alloca(
bcx, pat.id, path, binding_mode,
|bcx, variable_ty, llvariable_val| {
match pat_binding_mode {
ast::bind_infer => {
// By value binding: move the value that `val`
// points at into the binding's stack slot.
let datum = Datum {val: val,
ty: variable_ty,
mode: ByRef(ZeroMem)};
datum.store_to(bcx, INIT, llvariable_val)
}
ast::bind_by_ref(_) => {
// By ref binding: the value of the variable
// is the pointer `val` itself.
Store(bcx, val, llvariable_val);
bcx
}
}
});
}
for inner.iter().advance |inner_pat| {
bcx = bind_irrefutable_pat(
bcx, *inner_pat, val, true, binding_mode);
for inner.iter().advance |&inner_pat| {
bcx = bind_irrefutable_pat(bcx, inner_pat, val, binding_mode);
}
}
ast::pat_enum(_, ref sub_pats) => {
@ -1799,11 +1944,8 @@ pub fn bind_irrefutable_pat(bcx: block,
val);
for sub_pats.iter().advance |sub_pat| {
for args.vals.iter().enumerate().advance |(i, argval)| {
bcx = bind_irrefutable_pat(bcx,
sub_pat[i],
*argval,
make_copy,
binding_mode);
bcx = bind_irrefutable_pat(bcx, sub_pat[i],
*argval, binding_mode);
}
}
}
@ -1818,19 +1960,14 @@ pub fn bind_irrefutable_pat(bcx: block,
let repr = adt::represent_node(bcx, pat.id);
for elems.iter().enumerate().advance |(i, elem)| {
let fldptr = adt::trans_field_ptr(bcx, repr,
val, 0, i);
bcx = bind_irrefutable_pat(bcx,
*elem,
fldptr,
make_copy,
binding_mode);
val, 0, i);
bcx = bind_irrefutable_pat(bcx, *elem,
fldptr, binding_mode);
}
}
}
}
Some(&ast::def_static(_, false)) => {
bcx = bind_irrefutable_pat(bcx, pat, val, make_copy,
binding_mode);
}
_ => {
// Nothing to do here.
@ -1845,12 +1982,8 @@ pub fn bind_irrefutable_pat(bcx: block,
for fields.iter().advance |f| {
let ix = ty::field_idx_strict(tcx, f.ident, field_tys);
let fldptr = adt::trans_field_ptr(bcx, pat_repr, val,
discr, ix);
bcx = bind_irrefutable_pat(bcx,
f.pat,
fldptr,
make_copy,
binding_mode);
discr, ix);
bcx = bind_irrefutable_pat(bcx, f.pat, fldptr, binding_mode);
}
}
}
@ -1858,11 +1991,7 @@ pub fn bind_irrefutable_pat(bcx: block,
let repr = adt::represent_node(bcx, pat.id);
for elems.iter().enumerate().advance |(i, elem)| {
let fldptr = adt::trans_field_ptr(bcx, repr, val, 0, i);
bcx = bind_irrefutable_pat(bcx,
*elem,
fldptr,
make_copy,
binding_mode);
bcx = bind_irrefutable_pat(bcx, *elem, fldptr, binding_mode);
}
}
ast::pat_box(inner) | ast::pat_uniq(inner) => {
@ -1872,22 +2001,30 @@ pub fn bind_irrefutable_pat(bcx: block,
ty::ty_uniq(*) if !ty::type_contents(bcx.tcx(), pat_ty).contains_managed() => llbox,
_ => GEPi(bcx, llbox, [0u, abi::box_field_body])
};
bcx = bind_irrefutable_pat(bcx,
inner,
unboxed,
true,
binding_mode);
bcx = bind_irrefutable_pat(bcx, inner, unboxed, binding_mode);
}
ast::pat_region(inner) => {
let loaded_val = Load(bcx, val);
bcx = bind_irrefutable_pat(bcx,
inner,
loaded_val,
true,
binding_mode);
bcx = bind_irrefutable_pat(bcx, inner, loaded_val, binding_mode);
}
ast::pat_wild | ast::pat_lit(_) | ast::pat_range(_, _) |
ast::pat_vec(*) => ()
ast::pat_vec(*) => {
bcx.tcx().sess.span_bug(
pat.span,
fmt!("vector patterns are never irrefutable!"));
}
ast::pat_wild | ast::pat_lit(_) | ast::pat_range(_, _) => ()
}
return bcx;
}
fn simple_identifier<'a>(pat: &'a ast::pat) -> Option<&'a ast::Path> {
match pat.node {
ast::pat_ident(ast::bind_infer, ref path, None) => {
Some(path)
}
_ => {
None
}
}
}

View File

@ -59,6 +59,7 @@ use middle::trans::type_of::*;
use middle::ty;
use util::common::indenter;
use util::ppaux::{Repr, ty_to_str};
use middle::pat_util;
use middle::trans::type_::Type;
@ -75,7 +76,7 @@ use extra::time;
use extra::sort;
use syntax::ast::ident;
use syntax::ast_map::{path, path_elt_to_str, path_name};
use syntax::ast_util::{local_def, path_to_ident};
use syntax::ast_util::{local_def};
use syntax::attr;
use syntax::codemap::span;
use syntax::parse::token;
@ -111,8 +112,8 @@ impl Drop for _InsnCtxt {
fn drop(&self) {
unsafe {
do local_data::local_data_modify(task_local_insn_key) |c| {
do c.map_consume |@ctx| {
let mut ctx = ctx;
do c.map_consume |ctx| {
let mut ctx = copy *ctx;
ctx.pop();
@ctx
}
@ -125,8 +126,8 @@ pub fn push_ctxt(s: &'static str) -> _InsnCtxt {
debug!("new InsnCtxt: %s", s);
unsafe {
do local_data::local_data_modify(task_local_insn_key) |c| {
do c.map_consume |@ctx| {
let mut ctx = ctx;
do c.map_consume |ctx| {
let mut ctx = copy *ctx;
ctx.push(s);
@ctx
}
@ -1012,7 +1013,7 @@ pub fn get_landing_pad(bcx: block) -> BasicBlockRef {
match bcx.fcx.personality {
Some(addr) => Store(pad_bcx, llretval, addr),
None => {
let addr = alloca(pad_bcx, val_ty(llretval));
let addr = alloca(pad_bcx, val_ty(llretval), "");
bcx.fcx.personality = Some(addr);
Store(pad_bcx, llretval, addr);
}
@ -1056,7 +1057,7 @@ pub fn do_spill(bcx: block, v: ValueRef, t: ty::t) -> ValueRef {
if ty::type_is_bot(t) {
return C_null(Type::i8p());
}
let llptr = alloc_ty(bcx, t);
let llptr = alloc_ty(bcx, t, "");
Store(bcx, v, llptr);
return llptr;
}
@ -1064,7 +1065,7 @@ pub fn do_spill(bcx: block, v: ValueRef, t: ty::t) -> ValueRef {
// Since this function does *not* root, it is the caller's responsibility to
// ensure that the referent is pointed to by a root.
pub fn do_spill_noroot(cx: block, v: ValueRef) -> ValueRef {
let llptr = alloca(cx, val_ty(v));
let llptr = alloca(cx, val_ty(v), "");
Store(cx, v, llptr);
return llptr;
}
@ -1121,9 +1122,6 @@ pub fn init_local(bcx: block, local: &ast::local) -> block {
let _indenter = indenter();
let _icx = push_ctxt("init_local");
let ty = node_id_type(bcx, local.node.id);
debug!("ty=%s", bcx.ty_to_str(ty));
if ignore_lhs(bcx, local) {
// Handle let _ = e; just like e;
@ -1135,36 +1133,7 @@ pub fn init_local(bcx: block, local: &ast::local) -> block {
}
}
let llptr = match bcx.fcx.lllocals.find_copy(&local.node.id) {
Some(v) => v,
_ => {
bcx.tcx().sess.span_bug(local.span,
"init_local: Someone forgot to document why it's\
safe to assume local.node.init must be local_mem!");
}
};
let mut bcx = bcx;
match local.node.init {
Some(init) => {
bcx = expr::trans_into(bcx, init, expr::SaveIn(llptr));
}
_ => {
zero_mem(bcx, llptr, ty);
}
}
// Make a note to drop this slot on the way out.
debug!("adding clean for %?/%s to bcx=%s",
local.node.id, bcx.ty_to_str(ty),
bcx.to_str());
add_clean(bcx, llptr, ty);
return _match::bind_irrefutable_pat(bcx,
local.node.pat,
llptr,
false,
_match::BindLocal);
_match::store_local(bcx, local.node.pat, local.node.init)
}
pub fn trans_stmt(cx: block, s: &ast::stmt) -> block {
@ -1469,28 +1438,6 @@ pub fn block_locals(b: &ast::blk, it: &fn(@ast::local)) {
}
}
pub fn alloc_local(cx: block, local: &ast::local) -> block {
let _icx = push_ctxt("alloc_local");
let t = node_id_type(cx, local.node.id);
let simple_name = match local.node.pat.node {
ast::pat_ident(_, ref pth, None) => Some(path_to_ident(pth)),
_ => None
};
let val = alloc_ty(cx, t);
if cx.sess().opts.debuginfo {
for simple_name.iter().advance |name| {
str::as_c_str(cx.ccx().sess.str_of(*name), |buf| {
unsafe {
llvm::LLVMSetValueName(val, buf)
}
});
}
}
cx.fcx.lllocals.insert(local.node.id, val);
cx
}
pub fn with_cond(bcx: block, val: ValueRef, f: &fn(block) -> block) -> block {
let _icx = push_ctxt("with_cond");
let next_cx = base::sub_block(bcx, "next");
@ -1561,20 +1508,20 @@ pub fn memzero(cx: block, llptr: ValueRef, ty: Type) {
Call(cx, llintrinsicfn, [llptr, llzeroval, size, align, volatile]);
}
pub fn alloc_ty(bcx: block, t: ty::t) -> ValueRef {
pub fn alloc_ty(bcx: block, t: ty::t, name: &str) -> ValueRef {
let _icx = push_ctxt("alloc_ty");
let ccx = bcx.ccx();
let ty = type_of::type_of(ccx, t);
assert!(!ty::type_has_params(t), "Type has params: %s", ty_to_str(ccx.tcx, t));
let val = alloca(bcx, ty);
let val = alloca(bcx, ty, name);
return val;
}
pub fn alloca(cx: block, ty: Type) -> ValueRef {
alloca_maybe_zeroed(cx, ty, false)
pub fn alloca(cx: block, ty: Type, name: &str) -> ValueRef {
alloca_maybe_zeroed(cx, ty, name, false)
}
pub fn alloca_maybe_zeroed(cx: block, ty: Type, zero: bool) -> ValueRef {
pub fn alloca_maybe_zeroed(cx: block, ty: Type, name: &str, zero: bool) -> ValueRef {
let _icx = push_ctxt("alloca");
if cx.unreachable {
unsafe {
@ -1582,7 +1529,7 @@ pub fn alloca_maybe_zeroed(cx: block, ty: Type, zero: bool) -> ValueRef {
}
}
let initcx = base::raw_block(cx.fcx, false, cx.fcx.llstaticallocas);
let p = Alloca(initcx, ty);
let p = Alloca(initcx, ty, name);
if zero { memzero(initcx, p, ty); }
p
}
@ -1623,7 +1570,8 @@ pub fn make_return_pointer(fcx: fn_ctxt, output_type: ty::t) -> ValueRef {
llvm::LLVMGetParam(fcx.llfn, 0)
} else {
let lloutputtype = type_of::type_of(fcx.ccx, output_type);
alloca(raw_block(fcx, false, fcx.llstaticallocas), lloutputtype)
alloca(raw_block(fcx, false, fcx.llstaticallocas), lloutputtype,
"__make_return_pointer")
}
}
}
@ -1738,6 +1686,7 @@ pub fn create_llargs_for_fn_args(cx: fn_ctxt,
let arg = &args[i];
let llarg = llvm::LLVMGetParam(cx.llfn, arg_n as c_uint);
// FIXME #7260: aliasing should be determined by monomorphized ty::t
match arg.ty.node {
// `~` pointers never alias other parameters, because ownership was transferred
ast::ty_uniq(_) => {
@ -1766,7 +1715,7 @@ pub fn copy_args_to_allocas(fcx: fn_ctxt,
let self_val = if slf.is_copy
&& datum::appropriate_mode(bcx.tcx(), slf.t).is_by_value() {
let tmp = BitCast(bcx, slf.v, type_of(bcx.ccx(), slf.t));
let alloc = alloc_ty(bcx, slf.t);
let alloc = alloc_ty(bcx, slf.t, "__self");
Store(bcx, tmp, alloc);
alloc
} else {
@ -1782,7 +1731,6 @@ pub fn copy_args_to_allocas(fcx: fn_ctxt,
for uint::range(0, arg_tys.len()) |arg_n| {
let arg_ty = arg_tys[arg_n];
let raw_llarg = raw_llargs[arg_n];
let arg_id = args[arg_n].id;
// For certain mode/type combinations, the raw llarg values are passed
// by value. However, within the fn body itself, we want to always
@ -1793,22 +1741,13 @@ pub fn copy_args_to_allocas(fcx: fn_ctxt,
// the event it's not truly needed.
// only by value if immediate:
let llarg = if datum::appropriate_mode(bcx.tcx(), arg_ty).is_by_value() {
let alloc = alloc_ty(bcx, arg_ty);
let alloc = alloc_ty(bcx, arg_ty, "__arg");
Store(bcx, raw_llarg, alloc);
alloc
} else {
raw_llarg
};
add_clean(bcx, llarg, arg_ty);
bcx = _match::bind_irrefutable_pat(bcx,
args[arg_n].pat,
llarg,
false,
_match::BindArgument);
fcx.llargs.insert(arg_id, llarg);
bcx = _match::store_arg(bcx, args[arg_n].pat, llarg);
if fcx.ccx.sess.opts.extra_debuginfo && fcx_has_nonzero_span(fcx) {
debuginfo::create_arg(bcx, &args[arg_n], args[arg_n].ty.span);
@ -1967,81 +1906,51 @@ pub fn trans_fn(ccx: @mut CrateContext,
|_bcx| { });
}
fn insert_synthetic_type_entries(bcx: block,
fn_args: &[ast::arg],
arg_tys: &[ty::t])
{
/*!
* For tuple-like structs and enum-variants, we generate
* synthetic AST nodes for the arguments. These have no types
* in the type table and no entries in the moves table,
* so the code in `copy_args_to_allocas` and `bind_irrefutable_pat`
* gets upset. This hack of a function bridges the gap by inserting types.
*
* This feels horrible. I think we should just have a special path
* for these functions and not try to use the generic code, but
* that's not the problem I'm trying to solve right now. - nmatsakis
*/
let tcx = bcx.tcx();
for uint::range(0, fn_args.len()) |i| {
debug!("setting type of argument %u (pat node %d) to %s",
i, fn_args[i].pat.id, bcx.ty_to_str(arg_tys[i]));
let pat_id = fn_args[i].pat.id;
let arg_ty = arg_tys[i];
tcx.node_types.insert(pat_id as uint, arg_ty);
}
}
pub fn trans_enum_variant(ccx: @mut CrateContext,
enum_id: ast::node_id,
_enum_id: ast::node_id,
variant: &ast::variant,
args: &[ast::variant_arg],
disr: int,
param_substs: Option<@param_substs>,
llfndecl: ValueRef) {
let _icx = push_ctxt("trans_enum_variant");
// Translate variant arguments to function arguments.
let fn_args = do args.map |varg| {
ast::arg {
is_mutbl: false,
ty: copy varg.ty,
pat: ast_util::ident_to_pat(
ccx.tcx.sess.next_node_id(),
codemap::dummy_sp(),
special_idents::arg),
id: varg.id,
}
};
let ty_param_substs = match param_substs {
Some(ref substs) => { copy substs.tys }
None => ~[]
};
let enum_ty = ty::subst_tps(ccx.tcx,
ty_param_substs,
None,
ty::node_id_to_type(ccx.tcx, enum_id));
let fcx = new_fn_ctxt_w_id(ccx,
~[],
llfndecl,
variant.node.id,
enum_ty,
param_substs,
None);
let raw_llargs = create_llargs_for_fn_args(fcx, no_self, fn_args);
let bcx = top_scope_block(fcx, None);
let lltop = bcx.llbb;
let arg_tys = ty::ty_fn_args(node_id_type(bcx, variant.node.id));
let bcx = copy_args_to_allocas(fcx, bcx, fn_args, raw_llargs, arg_tys);
// XXX is there a better way to reconstruct the ty::t?
let repr = adt::represent_type(ccx, enum_ty);
debug!("trans_enum_variant: name=%s tps=%s repr=%? enum_ty=%s",
unsafe { str::raw::from_c_str(llvm::LLVMGetValueName(llfndecl)) },
~"[" + ty_param_substs.map(|&t| ty_to_str(ccx.tcx, t)).connect(", ") + "]",
repr, ty_to_str(ccx.tcx, enum_ty));
adt::trans_start_init(bcx, repr, fcx.llretptr.get(), disr);
for args.iter().enumerate().advance |(i, va)| {
let lldestptr = adt::trans_field_ptr(bcx,
repr,
fcx.llretptr.get(),
disr,
i);
// If this argument to this function is a enum, it'll have come in to
// this function as an opaque blob due to the way that type_of()
// works. So we have to cast to the destination's view of the type.
let llarg = match fcx.llargs.find(&va.id) {
Some(&x) => x,
_ => fail!("trans_enum_variant: how do we know this works?"),
};
let arg_ty = arg_tys[i];
memcpy_ty(bcx, lldestptr, llarg, arg_ty);
}
build_return(bcx);
finish_fn(fcx, lltop);
trans_enum_variant_or_tuple_like_struct(
ccx,
variant.node.id,
args,
disr,
param_substs,
llfndecl);
}
// NB: In theory this should be merged with the function above. But the AST
// structures are completely different, so very little code would be shared.
pub fn trans_tuple_struct(ccx: @mut CrateContext,
fields: &[@ast::struct_field],
ctor_id: ast::node_id,
@ -2049,37 +1958,72 @@ pub fn trans_tuple_struct(ccx: @mut CrateContext,
llfndecl: ValueRef) {
let _icx = push_ctxt("trans_tuple_struct");
// Translate struct fields to function arguments.
let fn_args = do fields.map |field| {
trans_enum_variant_or_tuple_like_struct(
ccx,
ctor_id,
fields,
0,
param_substs,
llfndecl);
}
trait IdAndTy {
fn id(&self) -> ast::node_id;
fn ty<'a>(&'a self) -> &'a ast::Ty;
}
impl IdAndTy for ast::variant_arg {
fn id(&self) -> ast::node_id { self.id }
fn ty<'a>(&'a self) -> &'a ast::Ty { &self.ty }
}
impl IdAndTy for @ast::struct_field {
fn id(&self) -> ast::node_id { self.node.id }
fn ty<'a>(&'a self) -> &'a ast::Ty { &self.node.ty }
}
pub fn trans_enum_variant_or_tuple_like_struct<A:IdAndTy>(
ccx: @mut CrateContext,
ctor_id: ast::node_id,
args: &[A],
disr: int,
param_substs: Option<@param_substs>,
llfndecl: ValueRef)
{
// Translate variant arguments to function arguments.
let fn_args = do args.map |varg| {
ast::arg {
is_mutbl: false,
ty: copy field.node.ty,
pat: ast_util::ident_to_pat(ccx.tcx.sess.next_node_id(),
codemap::dummy_sp(),
special_idents::arg),
id: field.node.id
ty: copy *varg.ty(),
pat: ast_util::ident_to_pat(
ccx.tcx.sess.next_node_id(),
codemap::dummy_sp(),
special_idents::arg),
id: varg.id(),
}
};
// XXX is there a better way to reconstruct the ty::t?
let ty_param_substs = match param_substs {
Some(ref substs) => { copy substs.tys }
None => ~[]
};
let ctor_ty = ty::subst_tps(ccx.tcx, ty_param_substs, None,
ty::node_id_to_type(ccx.tcx, ctor_id));
let tup_ty = match ty::get(ctor_ty).sty {
let result_ty = match ty::get(ctor_ty).sty {
ty::ty_bare_fn(ref bft) => bft.sig.output,
_ => ccx.sess.bug(fmt!("trans_tuple_struct: unexpected ctor \
return type %s",
ty_to_str(ccx.tcx, ctor_ty)))
_ => ccx.sess.bug(
fmt!("trans_enum_variant_or_tuple_like_struct: \
unexpected ctor return type %s",
ty_to_str(ccx.tcx, ctor_ty)))
};
let fcx = new_fn_ctxt_w_id(ccx,
~[],
llfndecl,
ctor_id,
tup_ty,
result_ty,
param_substs,
None);
@ -2087,23 +2031,23 @@ pub fn trans_tuple_struct(ccx: @mut CrateContext,
let bcx = top_scope_block(fcx, None);
let lltop = bcx.llbb;
let arg_tys = ty::ty_fn_args(node_id_type(bcx, ctor_id));
let arg_tys = ty::ty_fn_args(ctor_ty);
insert_synthetic_type_entries(bcx, fn_args, arg_tys);
let bcx = copy_args_to_allocas(fcx, bcx, fn_args, raw_llargs, arg_tys);
let repr = adt::represent_type(ccx, tup_ty);
adt::trans_start_init(bcx, repr, fcx.llretptr.get(), 0);
for fields.iter().enumerate().advance |(i, field)| {
let repr = adt::represent_type(ccx, result_ty);
adt::trans_start_init(bcx, repr, fcx.llretptr.get(), disr);
for fn_args.iter().enumerate().advance |(i, fn_arg)| {
let lldestptr = adt::trans_field_ptr(bcx,
repr,
fcx.llretptr.get(),
0,
disr,
i);
let llarg = fcx.llargs.get_copy(&field.node.id);
let llarg = fcx.llargs.get_copy(&fn_arg.pat.id);
let arg_ty = arg_tys[i];
memcpy_ty(bcx, lldestptr, llarg, arg_ty);
}
build_return(bcx);
finish_fn(fcx, lltop);
}
@ -3033,13 +2977,17 @@ pub fn trans_crate(sess: session::Session,
do sort::quick_sort(ccx.stats.fn_stats) |&(_, _, insns_a), &(_, _, insns_b)| {
insns_a > insns_b
}
for ccx.stats.fn_stats.iter().advance |&(name, ms, insns)| {
io::println(fmt!("%u insns, %u ms, %s", insns, ms, name));
for ccx.stats.fn_stats.iter().advance |tuple| {
match *tuple {
(ref name, ms, insns) => {
io::println(fmt!("%u insns, %u ms, %s", insns, ms, *name));
}
}
}
}
if ccx.sess.count_llvm_insns() {
for ccx.stats.llvm_insns.iter().advance |(&k, &v)| {
io::println(fmt!("%-7u %s", v, k));
for ccx.stats.llvm_insns.iter().advance |(k, v)| {
io::println(fmt!("%-7u %s", *v, *k));
}
}

View File

@ -505,11 +505,17 @@ pub fn ArrayMalloc(cx: block, Ty: Type, Val: ValueRef) -> ValueRef {
}
}
pub fn Alloca(cx: block, Ty: Type) -> ValueRef {
pub fn Alloca(cx: block, Ty: Type, name: &str) -> ValueRef {
unsafe {
if cx.unreachable { return llvm::LLVMGetUndef(Ty.ptr_to().to_ref()); }
count_insn(cx, "alloca");
return llvm::LLVMBuildAlloca(B(cx), Ty.to_ref(), noname());
if name.is_empty() {
llvm::LLVMBuildAlloca(B(cx), Ty.to_ref(), noname())
} else {
str::as_c_str(
name,
|c| llvm::LLVMBuildAlloca(B(cx), Ty.to_ref(), c))
}
}
}

View File

@ -130,10 +130,10 @@ impl FnType {
j = 1u;
get_param(llwrapfn, 0u)
} else if self.ret_ty.cast {
let retptr = alloca(bcx, self.ret_ty.ty);
let retptr = alloca(bcx, self.ret_ty.ty, "");
BitCast(bcx, retptr, ret_ty.ptr_to())
} else {
alloca(bcx, ret_ty)
alloca(bcx, ret_ty, "")
};
let mut i = 0u;

View File

@ -600,7 +600,7 @@ pub fn trans_call_inner(in_cx: block,
let mut bcx = callee.bcx;
let ccx = cx.ccx();
let ret_flag = if ret_in_loop {
let flag = alloca(bcx, Type::bool());
let flag = alloca(bcx, Type::bool(), "__ret_flag");
Store(bcx, C_bool(false), flag);
Some(flag)
} else {
@ -675,7 +675,7 @@ pub fn trans_call_inner(in_cx: block,
unsafe {
if ty::type_needs_drop(bcx.tcx(), ret_ty) {
if ty::type_is_immediate(bcx.tcx(), ret_ty) {
let llscratchptr = alloc_ty(bcx, ret_ty);
let llscratchptr = alloc_ty(bcx, ret_ty, "__ret");
Store(bcx, llresult, llscratchptr);
bcx = glue::drop_ty(bcx, llscratchptr, ret_ty);
} else {
@ -733,7 +733,7 @@ pub fn trans_ret_slot(bcx: block, fn_ty: ty::t, dest: Option<expr::Dest>)
llvm::LLVMGetUndef(Type::nil().ptr_to().to_ref())
}
} else {
alloc_ty(bcx, retty)
alloc_ty(bcx, retty, "__trans_ret_slot")
}
}
}
@ -823,7 +823,7 @@ pub fn trans_arg_expr(bcx: block,
_
}) => {
let scratch_ty = expr_ty(bcx, arg_expr);
let scratch = alloc_ty(bcx, scratch_ty);
let scratch = alloc_ty(bcx, scratch_ty, "__ret_flag");
let arg_ty = expr_ty(bcx, arg_expr);
let sigil = ty::ty_closure_sigil(arg_ty);
let bcx = closure::trans_expr_fn(
@ -860,8 +860,6 @@ pub fn trans_arg_expr(bcx: block,
// FIXME(#3548) use the adjustments table
match autoref_arg {
DoAutorefArg => {
assert!(!
bcx.ccx().maps.moves_map.contains(&arg_expr.id));
val = arg_datum.to_ref_llval(bcx);
}
DontAutorefArg => {
@ -875,10 +873,10 @@ pub fn trans_arg_expr(bcx: block,
// &arg_expr.id);
debug!("by ref arg with type %s, storing to scratch",
bcx.ty_to_str(arg_datum.ty));
let scratch = scratch_datum(bcx, arg_datum.ty, false);
let scratch = scratch_datum(bcx, arg_datum.ty,
"__self", false);
arg_datum.store_to_datum(bcx,
arg_expr.id,
INIT,
scratch);
@ -895,10 +893,10 @@ pub fn trans_arg_expr(bcx: block,
arg_datum.appropriate_mode(bcx.tcx()).is_by_ref() {
debug!("by copy arg with type %s, storing to scratch",
bcx.ty_to_str(arg_datum.ty));
let scratch = scratch_datum(bcx, arg_datum.ty, false);
let scratch = scratch_datum(bcx, arg_datum.ty,
"__arg", false);
arg_datum.store_to_datum(bcx,
arg_expr.id,
INIT,
scratch);

View File

@ -193,7 +193,7 @@ pub fn allocate_cbox(bcx: block, sigil: ast::Sigil, cdata_ty: ty::t)
}
ast::BorrowedSigil => {
let cbox_ty = tuplify_box_ty(tcx, cdata_ty);
let llbox = alloc_ty(bcx, cbox_ty);
let llbox = alloc_ty(bcx, cbox_ty, "__closure");
nuke_ref_count(bcx, llbox);
rslt(bcx, llbox)
}

View File

@ -608,6 +608,10 @@ impl block_ {
pub fn tcx(&self) -> ty::ctxt { self.fcx.ccx.tcx }
pub fn sess(&self) -> Session { self.fcx.ccx.sess }
pub fn ident(&self, ident: ident) -> @str {
token::ident_to_str(&ident)
}
pub fn node_id_to_str(&self, id: ast::node_id) -> ~str {
ast_map::node_id_to_str(self.tcx().items, id, self.sess().intr())
}

View File

@ -96,7 +96,7 @@ pub struct CrateContext {
all_llvm_symbols: HashSet<@str>,
tcx: ty::ctxt,
maps: astencode::Maps,
stats: Stats,
stats: @mut Stats,
upcalls: @upcall::Upcalls,
tydesc_type: Type,
int_type: Type,
@ -201,7 +201,7 @@ impl CrateContext {
all_llvm_symbols: HashSet::new(),
tcx: tcx,
maps: maps,
stats: Stats {
stats: @mut Stats {
n_static_tydescs: 0u,
n_glues_created: 0u,
n_null_glues: 0u,

View File

@ -35,9 +35,6 @@ use syntax::codemap::span;
pub fn trans_block(bcx: block, b: &ast::blk, dest: expr::Dest) -> block {
let _icx = push_ctxt("trans_block");
let mut bcx = bcx;
do block_locals(b) |local| {
bcx = alloc_local(bcx, local);
};
for b.node.stmts.iter().advance |s| {
debuginfo::update_source_pos(bcx, b.span);
bcx = trans_stmt(bcx, *s);

View File

@ -70,8 +70,8 @@
* This is a "shallow" clone. After `move_to()`, the current datum
* is invalid and should no longer be used.
*
* - `store_to()` either performs a copy or a move by consulting the
* moves_map computed by `middle::moves`.
* - `store_to()` either performs a copy or a move depending on the
* Rust type of the datum.
*
* # Scratch datum
*
@ -173,19 +173,19 @@ pub fn immediate_rvalue_bcx(bcx: block,
return DatumBlock {bcx: bcx, datum: immediate_rvalue(val, ty)};
}
pub fn scratch_datum(bcx: block, ty: ty::t, zero: bool) -> Datum {
pub fn scratch_datum(bcx: block, ty: ty::t, name: &str, zero: bool) -> Datum {
/*!
*
* Allocates temporary space on the stack using alloca() and
* returns a by-ref Datum pointing to it. If `zero` is true, the
* space will be zeroed when it is allocated; this is normally not
* necessary, but in the case of automatic rooting in match
* statements it is possible to have temporaries that may not get
* initialized if a certain arm is not taken, so we must zero
* them. You must arrange any cleanups etc yourself! */
* them. You must arrange any cleanups etc yourself!
*/
let llty = type_of::type_of(bcx.ccx(), ty);
let scratch = alloca_maybe_zeroed(bcx, llty, zero);
let scratch = alloca_maybe_zeroed(bcx, llty, name, zero);
Datum { val: scratch, ty: ty, mode: ByRef(RevokeClean) }
}
@ -208,7 +208,6 @@ pub fn appropriate_mode(tcx: ty::ctxt, ty: ty::t) -> DatumMode {
impl Datum {
pub fn store_to(&self,
bcx: block,
id: ast::node_id,
action: CopyAction,
dst: ValueRef)
-> block {
@ -218,7 +217,7 @@ impl Datum {
* `id` is located in the move table, but copies otherwise.
*/
if bcx.ccx().maps.moves_map.contains(&id) {
if ty::type_moves_by_default(bcx.tcx(), self.ty) {
self.move_to(bcx, action, dst)
} else {
self.copy_to(bcx, action, dst)
@ -227,7 +226,6 @@ impl Datum {
pub fn store_to_dest(&self,
bcx: block,
id: ast::node_id,
dest: expr::Dest)
-> block {
match dest {
@ -235,21 +233,20 @@ impl Datum {
return bcx;
}
expr::SaveIn(addr) => {
return self.store_to(bcx, id, INIT, addr);
return self.store_to(bcx, INIT, addr);
}
}
}
pub fn store_to_datum(&self,
bcx: block,
id: ast::node_id,
action: CopyAction,
datum: Datum)
-> block {
debug!("store_to_datum(self=%s, action=%?, datum=%s)",
self.to_str(bcx.ccx()), action, datum.to_str(bcx.ccx()));
assert!(datum.mode.is_by_ref());
self.store_to(bcx, id, action, datum.val)
self.store_to(bcx, action, datum.val)
}
pub fn move_to_datum(&self, bcx: block, action: CopyAction, datum: Datum)
@ -476,7 +473,7 @@ impl Datum {
if ty::type_is_nil(self.ty) || ty::type_is_bot(self.ty) {
C_null(type_of::type_of(bcx.ccx(), self.ty).ptr_to())
} else {
let slot = alloc_ty(bcx, self.ty);
let slot = alloc_ty(bcx, self.ty, "");
Store(bcx, self.val, slot);
slot
}
@ -828,11 +825,10 @@ impl DatumBlock {
}
pub fn store_to(&self,
id: ast::node_id,
action: CopyAction,
dst: ValueRef)
-> block {
self.datum.store_to(self.bcx, id, action, dst)
self.datum.store_to(self.bcx, action, dst)
}
pub fn copy_to(&self, action: CopyAction, dst: ValueRef) -> block {

View File

@ -23,7 +23,8 @@ This will generate code that evaluates `expr`, storing the result into
`Dest`, which must either be the special flag ignore (throw the result
away) or be a pointer to memory of the same type/size as the
expression. It returns the resulting basic block. This form will
handle all automatic adjustments and moves for you.
handle all automatic adjustments for you. The value will be moved if
its type is linear and copied otherwise.
## Translation to a datum
@ -42,18 +43,18 @@ This function generates code to evaluate the expression and return a
tries to return its result in the most efficient way possible, without
introducing extra copies or sacrificing information. Therefore, for
lvalue expressions, you always get a by-ref `Datum` in return that
points at the memory for this lvalue (almost, see [1]). For rvalue
expressions, we will return a by-value `Datum` whenever possible, but
it is often necessary to allocate a stack slot, store the result of
the rvalue in there, and then return a pointer to the slot (see the
discussion later on about the different kinds of rvalues).
points at the memory for this lvalue. For rvalue expressions, we will
return a by-value `Datum` whenever possible, but it is often necessary
to allocate a stack slot, store the result of the rvalue in there, and
then return a pointer to the slot (see the discussion later on about
the different kinds of rvalues).
NB: The `trans_to_datum()` function does perform adjustments, but
since it returns a pointer to the value "in place" it does not handle
any moves that may be relevant. If you are transing an expression
whose result should be moved, you should either use the Datum methods
`move_to()` (for unconditional moves) or `store_to()` (for moves
conditioned on the type of the expression) at some point.
moves. If you wish to copy/move the value returned into a new
location, you should use the Datum method `store_to()` (move or copy
depending on type). You can also use `move_to()` (force move) or
`copy_to()` (force copy) for special situations.
## Translating local variables
@ -110,13 +111,6 @@ generate phi nodes).
Finally, statement rvalues are rvalues that always produce a nil
return type, such as `while` loops or assignments (`a = b`).
## Caveats
[1] Actually, some lvalues are only stored by value and not by
reference. An example (as of this writing) would be immutable
arguments or pattern bindings of immediate type. However, mutable
lvalues are *never* stored by value.
*/
@ -274,7 +268,7 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
ty::mt { ty: unit_ty, mutbl: ast::m_imm },
ty::vstore_slice(ty::re_static));
let scratch = scratch_datum(bcx, slice_ty, false);
let scratch = scratch_datum(bcx, slice_ty, "__adjust", false);
Store(bcx, base, GEPi(bcx, scratch.val, [0u, abi::slice_elt_base]));
Store(bcx, len, GEPi(bcx, scratch.val, [0u, abi::slice_elt_len]));
DatumBlock {bcx: bcx, datum: scratch}
@ -290,7 +284,7 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
let tcx = bcx.tcx();
let closure_ty = expr_ty_adjusted(bcx, expr);
debug!("add_env(closure_ty=%s)", closure_ty.repr(tcx));
let scratch = scratch_datum(bcx, closure_ty, false);
let scratch = scratch_datum(bcx, closure_ty, "__adjust", false);
let llfn = GEPi(bcx, scratch.val, [0u, abi::fn_field_code]);
assert_eq!(datum.appropriate_mode(tcx), ByValue);
Store(bcx, datum.to_appropriate_llval(bcx), llfn);
@ -315,7 +309,7 @@ pub fn trans_into(bcx: block, expr: @ast::expr, dest: Dest) -> block {
let datumblock = trans_to_datum(bcx, expr);
return match dest {
Ignore => datumblock.bcx,
SaveIn(lldest) => datumblock.store_to(expr.id, INIT, lldest)
SaveIn(lldest) => datumblock.store_to(INIT, lldest)
};
}
@ -343,7 +337,7 @@ pub fn trans_into(bcx: block, expr: @ast::expr, dest: Dest) -> block {
let datumblock = trans_lvalue_unadjusted(bcx, expr);
match dest {
Ignore => datumblock.bcx,
SaveIn(lldest) => datumblock.store_to(expr.id, INIT, lldest)
SaveIn(lldest) => datumblock.store_to(INIT, lldest)
}
}
ty::RvalueDatumExpr => {
@ -351,8 +345,9 @@ pub fn trans_into(bcx: block, expr: @ast::expr, dest: Dest) -> block {
match dest {
Ignore => datumblock.drop_val(),
// NB: We always do `move_to()` regardless of the
// moves_map because we're processing an rvalue
// When processing an rvalue, the value will be newly
// allocated, so we always `move_to` so as not to
// unnecessarily inc ref counts and so forth:
SaveIn(lldest) => datumblock.move_to(INIT, lldest)
}
}
@ -386,11 +381,11 @@ fn trans_lvalue(bcx: block, expr: @ast::expr) -> DatumBlock {
fn trans_to_datum_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
/*!
*
* Translates an expression into a datum. If this expression
* is an rvalue, this will result in a temporary value being
* created. If you already know where the result should be stored,
* you should use `trans_into()` instead. */
* created. If you plan to store the value somewhere else,
* you should prefer `trans_into()` instead.
*/
let mut bcx = bcx;
@ -423,7 +418,7 @@ fn trans_to_datum_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
bcx = trans_rvalue_dps_unadjusted(bcx, expr, Ignore);
return nil(bcx, ty);
} else {
let scratch = scratch_datum(bcx, ty, false);
let scratch = scratch_datum(bcx, ty, "", false);
bcx = trans_rvalue_dps_unadjusted(
bcx, expr, SaveIn(scratch.val));
@ -535,7 +530,7 @@ fn trans_rvalue_stmt_unadjusted(bcx: block, expr: @ast::expr) -> block {
let dst_datum = unpack_datum!(
bcx, trans_lvalue(bcx, dst));
return src_datum.store_to_datum(
bcx, src.id, DROP_EXISTING, dst_datum);
bcx, DROP_EXISTING, dst_datum);
}
ast::expr_assign_op(callee_id, op, dst, src) => {
return trans_assign_op(bcx, expr, callee_id, op, dst, src);
@ -638,7 +633,15 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
return trans_into(bcx, blk, dest);
}
ast::expr_copy(a) => {
return trans_into(bcx, a, dest);
// If we just called `trans_into(bcx, a, dest)`, then this
// might *move* the value into `dest` if the value is
// non-copyable. So first get a datum and then do an
// explicit copy.
let datumblk = trans_to_datum(bcx, a);
return match dest {
Ignore => datumblk.bcx,
SaveIn(llval) => datumblk.copy_to(INIT, llval)
};
}
ast::expr_call(f, ref args, _) => {
return callee::trans_call(
@ -1221,6 +1224,7 @@ fn trans_adt(bcx: block, repr: &adt::Repr, discr: int,
bcx = trans_into(bcx, e, Ignore);
}
for optbase.iter().advance |sbi| {
// FIXME #7261: this moves entire base, not just certain fields
bcx = trans_into(bcx, sbi.expr, Ignore);
}
return bcx;
@ -1245,7 +1249,7 @@ fn trans_adt(bcx: block, repr: &adt::Repr, discr: int,
adt::trans_field_ptr(bcx, repr, srcval, discr, i)
};
let dest = adt::trans_field_ptr(bcx, repr, addr, discr, i);
bcx = datum.store_to(bcx, base.expr.id, INIT, dest);
bcx = datum.store_to(bcx, INIT, dest);
}
}
@ -1687,7 +1691,7 @@ fn trans_assign_op(bcx: block,
// A user-defined operator method
if bcx.ccx().maps.method_map.find(&expr.id).is_some() {
// FIXME(#2528) evaluates the receiver twice!!
let scratch = scratch_datum(bcx, dst_datum.ty, false);
let scratch = scratch_datum(bcx, dst_datum.ty, "__assign_op", false);
let bcx = trans_overloaded_op(bcx,
expr,
callee_id,

View File

@ -195,14 +195,15 @@ fn build_wrap_fn_(ccx: @mut CrateContext,
if needs_c_return && !ty::type_is_immediate(ccx.tcx, tys.fn_sig.output) {
let lloutputtype = type_of::type_of(fcx.ccx, tys.fn_sig.output);
fcx.llretptr = Some(alloca(raw_block(fcx, false, fcx.llstaticallocas),
lloutputtype));
lloutputtype,
""));
}
let bcx = top_scope_block(fcx, None);
let lltop = bcx.llbb;
// Allocate the struct and write the arguments into it.
let llargbundle = alloca(bcx, tys.bundle_ty);
let llargbundle = alloca(bcx, tys.bundle_ty, "__llargbundle");
arg_builder(bcx, tys, llwrapfn, llargbundle);
// Create call itself.
@ -732,7 +733,7 @@ pub fn trans_intrinsic(ccx: @mut CrateContext,
let llsrcval = get_param(decl, first_real_arg);
let llsrcptr = if ty::type_is_immediate(ccx.tcx, in_type) {
let llsrcptr = alloca(bcx, llintype);
let llsrcptr = alloca(bcx, llintype, "__llsrcptr");
Store(bcx, llsrcval, llsrcptr);
llsrcptr
} else {

View File

@ -132,7 +132,7 @@ pub fn free_ty_immediate(bcx: block, v: ValueRef, t: ty::t) -> block {
ty::ty_evec(_, ty::vstore_box) |
ty::ty_estr(ty::vstore_box) |
ty::ty_opaque_closure_ptr(_) => {
let vp = alloca(bcx, type_of(bcx.ccx(), t));
let vp = alloca(bcx, type_of(bcx.ccx(), t), "");
Store(bcx, v, vp);
free_ty(bcx, vp, t)
}

View File

@ -614,7 +614,8 @@ pub fn trans_trait_callee_from_llval(bcx: block,
}
llself = PointerCast(bcx, llself, Type::opaque_box(ccx).ptr_to());
let scratch = scratch_datum(bcx, ty::mk_opaque_box(bcx.tcx()), false);
let scratch = scratch_datum(bcx, ty::mk_opaque_box(bcx.tcx()),
"__trait_callee", false);
Store(bcx, llself, scratch.val);
scratch.add_clean(bcx);

View File

@ -57,7 +57,7 @@ impl Reflector {
let bcx = self.bcx;
let str_vstore = ty::vstore_slice(ty::re_static);
let str_ty = ty::mk_estr(bcx.tcx(), str_vstore);
let scratch = scratch_datum(bcx, str_ty, false);
let scratch = scratch_datum(bcx, str_ty, "", false);
let len = C_uint(bcx.ccx(), s.len() + 1);
let c_str = PointerCast(bcx, C_cstr(bcx.ccx(), s), Type::i8p());
Store(bcx, c_str, GEPi(bcx, scratch.val, [ 0, 0 ]));

View File

@ -332,7 +332,7 @@ pub fn trans_uniq_or_managed_vstore(bcx: block, heap: heap, vstore_expr: @ast::e
let llptrval = PointerCast(bcx, llptrval, Type::i8p());
let llsizeval = C_uint(bcx.ccx(), s.len());
let typ = ty::mk_estr(bcx.tcx(), ty::vstore_uniq);
let lldestval = scratch_datum(bcx, typ, false);
let lldestval = scratch_datum(bcx, typ, "", false);
let bcx = callee::trans_lang_call(
bcx,
bcx.tcx().lang_items.strdup_uniq_fn(),
@ -454,7 +454,7 @@ pub fn write_content(bcx: block,
let loop_counter = {
// i = 0
let i = alloca(loop_bcx, bcx.ccx().int_type);
let i = alloca(loop_bcx, bcx.ccx().int_type, "__i");
Store(loop_bcx, C_uint(bcx.ccx(), 0), i);
Br(loop_bcx, cond_bcx.llbb);

View File

@ -120,7 +120,7 @@ fn root(datum: &Datum,
// First, root the datum. Note that we must zero this value,
// because sometimes we root on one path but not another.
// See e.g. #4904.
let scratch = scratch_datum(bcx, datum.ty, true);
let scratch = scratch_datum(bcx, datum.ty, "__write_guard", true);
datum.copy_to_datum(bcx, INIT, scratch);
let cleanup_bcx = find_bcx_for_scope(bcx, root_info.scope);
add_clean_temp_mem_in_scope(cleanup_bcx, root_info.scope, scratch.val, scratch.ty);
@ -135,7 +135,8 @@ fn root(datum: &Datum,
// scratch.val will be NULL should the cleanup get
// called without the freezing actually occurring, and
// return_to_mut checks for this condition.
let scratch_bits = scratch_datum(bcx, ty::mk_uint(), false);
let scratch_bits = scratch_datum(bcx, ty::mk_uint(),
"__write_guard_bits", false);
let freeze_did = match freeze_kind {
DynaImm => bcx.tcx().lang_items.borrow_as_imm_fn(),

View File

@ -158,9 +158,9 @@ pub fn check_pat_variant(pcx: &pat_ctxt, pat: @ast::pat, path: &ast::Path,
None => {
fcx.infcx().type_error_message_str_with_expected(pat.span,
|expected, actual| {
expected.map_default(~"", |&e| {
expected.map_default(~"", |e| {
fmt!("mismatched types: expected `%s` but found %s",
e, actual)})},
*e, actual)})},
Some(expected), ~"a structure pattern",
None);
fcx.write_error(pat.id);
@ -200,9 +200,9 @@ pub fn check_pat_variant(pcx: &pat_ctxt, pat: @ast::pat, path: &ast::Path,
_ => {
fcx.infcx().type_error_message_str_with_expected(pat.span,
|expected, actual| {
expected.map_default(~"", |&e| {
expected.map_default(~"", |e| {
fmt!("mismatched types: expected `%s` but found %s",
e, actual)})},
*e, actual)})},
Some(expected), ~"an enum or structure pattern",
None);
fcx.write_error(pat.id);
@ -534,9 +534,9 @@ pub fn check_pat(pcx: &pat_ctxt, pat: @ast::pat, expected: ty::t) {
_ => ty::terr_mismatch
};
fcx.infcx().type_error_message_str_with_expected(pat.span, |expected, actual| {
expected.map_default(~"", |&e| {
expected.map_default(~"", |e| {
fmt!("mismatched types: expected `%s` but found %s",
e, actual)})}, Some(expected), ~"tuple", Some(&type_error));
*e, actual)})}, Some(expected), ~"tuple", Some(&type_error));
fcx.write_error(pat.id);
}
}
@ -583,9 +583,9 @@ pub fn check_pat(pcx: &pat_ctxt, pat: @ast::pat, expected: ty::t) {
fcx.infcx().type_error_message_str_with_expected(
pat.span,
|expected, actual| {
expected.map_default(~"", |&e| {
expected.map_default(~"", |e| {
fmt!("mismatched types: expected `%s` but found %s",
e, actual)})},
*e, actual)})},
Some(expected),
~"a vector pattern",
None);
@ -641,9 +641,9 @@ pub fn check_pointer_pat(pcx: &pat_ctxt,
fcx.infcx().type_error_message_str_with_expected(
span,
|expected, actual| {
expected.map_default(~"", |&e| {
expected.map_default(~"", |e| {
fmt!("mismatched types: expected `%s` but found %s",
e, actual)})},
*e, actual)})},
Some(expected),
fmt!("%s pattern", match pointer_kind {
Managed => "an @-box",

View File

@ -350,10 +350,7 @@ pub fn resolve_type_vars_in_fn(fcx: @mut FnCtxt,
self_info.self_id);
}
for decl.inputs.iter().advance |arg| {
do pat_util::pat_bindings(fcx.tcx().def_map, arg.pat)
|_bm, pat_id, span, _path| {
resolve_type_vars_for_node(wbcx, span, pat_id);
}
(visit.visit_pat)(arg.pat, (wbcx, visit));
// Privacy needs the type for the whole pattern, not just each binding
if !pat_util::pat_is_binding(fcx.tcx().def_map, arg.pat) {
resolve_type_vars_for_node(wbcx, arg.pat.span, arg.pat.id);

View File

@ -209,7 +209,7 @@ impl CoherenceChecker {
match item.node {
item_impl(_, ref opt_trait, _, _) => {
let opt_trait : ~[trait_ref] = opt_trait.iter()
.transform(|&x| x)
.transform(|x| copy *x)
.collect();
self.check_implementation(item, opt_trait);
}
@ -270,7 +270,7 @@ impl CoherenceChecker {
// We only want to generate one Impl structure. When we generate one,
// we store it here so that we don't recreate it.
let mut implementation_opt = None;
for associated_traits.iter().advance |&associated_trait| {
for associated_traits.iter().advance |associated_trait| {
let trait_ref =
ty::node_id_to_trait_ref(
self.crate_context.tcx,

View File

@ -202,7 +202,7 @@ impl PkgSrc {
crates: &[Crate],
cfgs: &[~str],
what: OutputType) {
for crates.iter().advance |&crate| {
for crates.iter().advance |crate| {
let path = &src_dir.push_rel(&crate.file).normalize();
note(fmt!("build_crates: compiling %s", path.to_str()));
note(fmt!("build_crates: destination dir is %s", dst_dir.to_str()));

View File

@ -71,8 +71,8 @@ pub fn make_dir_rwx(p: &Path) -> bool { os::make_dir(p, U_RWX) }
pub fn workspace_contains_package_id(pkgid: &PkgId, workspace: &Path) -> bool {
let src_dir = workspace.push("src");
let dirs = os::list_dir(&src_dir);
for dirs.iter().advance |&p| {
let p = Path(p);
for dirs.iter().advance |p| {
let p = Path(copy *p);
debug!("=> p = %s", p.to_str());
if !os::path_is_dir(&src_dir.push_rel(&p)) {
loop;
@ -84,8 +84,8 @@ pub fn workspace_contains_package_id(pkgid: &PkgId, workspace: &Path) -> bool {
}
else {
let pf = p.filename();
for pf.iter().advance |&pf| {
let f_ = copy pf;
for pf.iter().advance |pf| {
let f_ = copy *pf;
let g = f_.to_str();
match split_version_general(g, '-') {
Some((ref might_match, ref vers)) => {
@ -217,10 +217,10 @@ pub fn library_in_workspace(path: &LocalPath, short_name: &str, where: Target,
debug!("lib_prefix = %s and lib_filetype = %s", lib_prefix, lib_filetype);
let mut result_filename = None;
for dir_contents.iter().advance |&p| {
for dir_contents.iter().advance |p| {
let mut which = 0;
let mut hash = None;
let p_path = Path(p);
let p_path = Path(copy *p);
let extension = p_path.filetype();
debug!("p = %s, p's extension is %?", p.to_str(), extension);
match extension {

View File

@ -194,7 +194,7 @@ pub fn compile_input(ctxt: &Ctx,
Main => ~[]
}
+ flags
+ cfgs.flat_map(|&c| { ~[~"--cfg", c] }),
+ cfgs.flat_map(|c| { ~[~"--cfg", copy *c] }),
driver::optgroups()).get();
let options = @session::options {
crate_type: crate_type,
@ -311,8 +311,8 @@ pub fn compile_crate(ctxt: &Ctx, pkg_id: &PkgId,
what: OutputType) -> bool {
debug!("compile_crate: crate=%s, dir=%s", crate.to_str(), dir.to_str());
debug!("compile_crate: short_name = %s, flags =...", pkg_id.to_str());
for flags.iter().advance |&fl| {
debug!("+++ %s", fl);
for flags.iter().advance |fl| {
debug!("+++ %s", *fl);
}
compile_input(ctxt, pkg_id, crate, dir, flags, cfgs, opt, what)
}

View File

@ -142,6 +142,30 @@ pub unsafe fn set_memory<T>(dst: *mut T, c: u8, count: uint) {
memset64(dst, c, count as u64);
}
/**
* Zeroes out `count * size_of::<T>` bytes of memory at `dst`
*/
#[inline]
#[cfg(not(stage0))]
pub unsafe fn zero_memory<T>(dst: *mut T, count: uint) {
set_memory(dst, 0, count);
}
/**
* Zeroes out `count * size_of::<T>` bytes of memory at `dst`
*/
#[inline]
#[cfg(stage0)]
pub unsafe fn zero_memory<T>(dst: *mut T, count: uint) {
let mut count = count * sys::size_of::<T>();
let mut dst = dst as *mut u8;
while count > 0 {
*dst = 0;
dst = mut_offset(dst, 1);
count -= 1;
}
}
/**
* Swap the values at two mutable locations of the same type, without
* deinitialising or copying either one.
@ -172,6 +196,32 @@ pub unsafe fn replace_ptr<T>(dest: *mut T, mut src: T) -> T {
src
}
/**
* Reads the value from `*src` and returns it. Does not copy `*src`.
*/
#[inline(always)]
pub unsafe fn read_ptr<T>(src: *mut T) -> T {
let mut tmp: T = intrinsics::uninit();
let t: *mut T = &mut tmp;
copy_memory(t, src, 1);
tmp
}
/**
* Reads the value from `*src` and nulls it out.
* This currently prevents destructors from executing.
*/
#[inline(always)]
pub unsafe fn read_and_zero_ptr<T>(dest: *mut T) -> T {
// Copy the data out from `dest`:
let tmp = read_ptr(dest);
// Now zero out `dest`:
zero_memory(dest, 1);
tmp
}
/// Transform a region pointer - &T - to an unsafe pointer - *T.
#[inline]
pub fn to_unsafe_ptr<T>(thing: &T) -> *T {

View File

@ -715,10 +715,16 @@ fn with_envp<T>(env: Option<&[(~str, ~str)]>, cb: &fn(*c_void) -> T) -> T {
let mut tmps = ~[];
let mut ptrs = ~[];
for es.iter().advance |&(k, v)| {
let kv = @fmt!("%s=%s", k, v);
tmps.push(kv);
ptrs.push(str::as_c_str(*kv, |b| b));
for es.iter().advance |pair| {
// Use of match here is just to workaround limitations
// in the stage0 irrefutable pattern impl.
match pair {
&(ref k, ref v) => {
let kv = @fmt!("%s=%s", *k, *v);
tmps.push(kv);
ptrs.push(str::as_c_str(*kv, |b| b));
}
}
}
ptrs.push(ptr::null());
@ -738,8 +744,8 @@ fn with_envp<T>(env: Option<&[(~str, ~str)]>, cb: &fn(*mut c_void) -> T) -> T {
match env {
Some(es) => {
let mut blk = ~[];
for es.iter().advance |&(k, v)| {
let kv = fmt!("%s=%s", k, v);
for es.iter().advance |pair| {
let kv = fmt!("%s=%s", pair.first(), pair.second());
blk.push_all(kv.as_bytes_with_null_consume());
}
blk.push(0);
@ -1294,9 +1300,9 @@ mod tests {
let output = str::from_bytes(prog.finish_with_output().output);
let r = os::env();
for r.iter().advance |&(k, v)| {
for r.iter().advance |&(ref k, ref v)| {
// don't check windows magical empty-named variables
assert!(k.is_empty() || output.contains(fmt!("%s=%s", k, v)));
assert!(k.is_empty() || output.contains(fmt!("%s=%s", *k, *v)));
}
}
#[test]

View File

@ -281,16 +281,16 @@ pub trait VectorVector<T> {
impl<'self, T:Copy> VectorVector<T> for &'self [~[T]] {
/// Flattens a vector of slices of T into a single vector of T.
pub fn concat_vec(&self) -> ~[T] {
self.flat_map(|&inner| inner)
self.flat_map(|inner| copy *inner)
}
/// Concatenate a vector of vectors, placing a given separator between each.
pub fn connect_vec(&self, sep: &T) -> ~[T] {
let mut r = ~[];
let mut first = true;
for self.iter().advance |&inner| {
for self.iter().advance |inner| {
if first { first = false; } else { r.push(copy *sep); }
r.push_all(inner);
r.push_all(copy *inner);
}
r
}
@ -1256,16 +1256,15 @@ impl<T> OwnedVector<T> for ~[T] {
/// ~~~
#[inline]
fn push_all_move(&mut self, mut rhs: ~[T]) {
let new_len = self.len() + rhs.len();
let self_len = self.len();
let rhs_len = rhs.len();
let new_len = self_len + rhs_len;
self.reserve(new_len);
unsafe {
do rhs.as_mut_buf |p, len| {
for uint::range(0, len) |i| {
let x = ptr::replace_ptr(ptr::mut_offset(p, i),
intrinsics::uninit());
self.push(x);
}
}
unsafe { // Note: infallible.
let self_p = vec::raw::to_mut_ptr(*self);
let rhs_p = vec::raw::to_ptr(rhs);
ptr::copy_memory(ptr::mut_offset(self_p, self_len), rhs_p, rhs_len);
raw::set_len(self, new_len);
raw::set_len(&mut rhs, 0);
}
}
@ -1277,9 +1276,8 @@ impl<T> OwnedVector<T> for ~[T] {
ln => {
let valptr = ptr::to_mut_unsafe_ptr(&mut self[ln - 1u]);
unsafe {
let val = ptr::replace_ptr(valptr, intrinsics::init());
raw::set_len(self, ln - 1u);
Some(val)
Some(ptr::read_ptr(valptr))
}
}
}
@ -1410,7 +1408,7 @@ impl<T> OwnedVector<T> for ~[T] {
unsafe {
// This loop is optimized out for non-drop types.
for uint::range(newlen, oldlen) |i| {
ptr::replace_ptr(ptr::mut_offset(p, i), intrinsics::uninit());
ptr::read_and_zero_ptr(ptr::mut_offset(p, i));
}
}
}
@ -1555,37 +1553,93 @@ pub trait OwnedEqVector<T:Eq> {
impl<T:Eq> OwnedEqVector<T> for ~[T] {
/**
* Remove consecutive repeated elements from a vector; if the vector is
* sorted, this removes all duplicates.
*/
* Remove consecutive repeated elements from a vector; if the vector is
* sorted, this removes all duplicates.
*/
pub fn dedup(&mut self) {
unsafe {
if self.len() == 0 { return; }
let mut last_written = 0;
let mut next_to_read = 1;
do self.as_mut_buf |p, ln| {
// last_written < next_to_read <= ln
while next_to_read < ln {
// last_written < next_to_read < ln
if *ptr::mut_offset(p, next_to_read) ==
*ptr::mut_offset(p, last_written) {
ptr::replace_ptr(ptr::mut_offset(p, next_to_read),
intrinsics::uninit());
} else {
last_written += 1;
// last_written <= next_to_read < ln
if next_to_read != last_written {
ptr::swap_ptr(ptr::mut_offset(p, last_written),
ptr::mut_offset(p, next_to_read));
}
// Although we have a mutable reference to `self`, we cannot make
// *arbitrary* changes. There exists the possibility that this
// vector is contained with an `@mut` box and hence is still
// readable by the outside world during the `Eq` comparisons.
// Moreover, those comparisons could fail, so we must ensure
// that the vector is in a valid state at all time.
//
// The way that we handle this is by using swaps; we iterate
// over all the elements, swapping as we go so that at the end
// the elements we wish to keep are in the front, and those we
// wish to reject are at the back. We can then truncate the
// vector. This operation is still O(n).
//
// Example: We start in this state, where `r` represents "next
// read" and `w` represents "next_write`.
//
// r
// +---+---+---+---+---+---+
// | 0 | 1 | 1 | 2 | 3 | 3 |
// +---+---+---+---+---+---+
// w
//
// Comparing self[r] against self[w-1], tis is not a duplicate, so
// we swap self[r] and self[w] (no effect as r==w) and then increment both
// r and w, leaving us with:
//
// r
// +---+---+---+---+---+---+
// | 0 | 1 | 1 | 2 | 3 | 3 |
// +---+---+---+---+---+---+
// w
//
// Comparing self[r] against self[w-1], this value is a duplicate,
// so we increment `r` but leave everything else unchanged:
//
// r
// +---+---+---+---+---+---+
// | 0 | 1 | 1 | 2 | 3 | 3 |
// +---+---+---+---+---+---+
// w
//
// Comparing self[r] against self[w-1], this is not a duplicate,
// so swap self[r] and self[w] and advance r and w:
//
// r
// +---+---+---+---+---+---+
// | 0 | 1 | 2 | 1 | 3 | 3 |
// +---+---+---+---+---+---+
// w
//
// Not a duplicate, repeat:
//
// r
// +---+---+---+---+---+---+
// | 0 | 1 | 2 | 3 | 1 | 3 |
// +---+---+---+---+---+---+
// w
//
// Duplicate, advance r. End of vec. Truncate to w.
let ln = self.len();
if ln < 1 { return; }
// Avoid bounds checks by using unsafe pointers.
let p = vec::raw::to_mut_ptr(*self);
let mut r = 1;
let mut w = 1;
while r < ln {
let p_r = ptr::mut_offset(p, r);
let p_wm1 = ptr::mut_offset(p, w - 1);
if *p_r != *p_wm1 {
if r != w {
let p_w = ptr::mut_offset(p_wm1, 1);
util::swap(&mut *p_r, &mut *p_w);
}
// last_written <= next_to_read < ln
next_to_read += 1;
// last_written < next_to_read <= ln
w += 1;
}
r += 1;
}
// last_written < next_to_read == ln
raw::set_len(self, last_written + 1);
self.truncate(w);
}
}
}

View File

@ -519,8 +519,8 @@ impl<'self> MethodDef<'self> {
// create the generics that aren't for Self
let fn_generics = self.generics.to_generics(cx, span, type_ident, generics);
let args = do arg_types.map |&(id, ty)| {
cx.arg(span, id, ty)
let args = do arg_types.map |pair| {
cx.arg(span, pair.first(), pair.second())
};
let ret_type = self.get_ret_ty(cx, span, generics, type_ident);
@ -589,7 +589,7 @@ impl<'self> MethodDef<'self> {
// transpose raw_fields
let fields = match raw_fields {
[self_arg, .. rest] => {
[ref self_arg, .. rest] => {
do self_arg.iter().enumerate().transform |(i, &(opt_id, field))| {
let other_fields = do rest.map |l| {
match &l[i] {
@ -738,16 +738,20 @@ impl<'self> MethodDef<'self> {
let mut enum_matching_fields = vec::from_elem(self_vec.len(), ~[]);
for matches_so_far.tail().iter().advance |&(_, _, other_fields)| {
for other_fields.iter().enumerate().advance |(i, &(_, other_field))| {
enum_matching_fields[i].push(other_field);
for matches_so_far.tail().iter().advance |triple| {
match triple {
&(_, _, ref other_fields) => {
for other_fields.iter().enumerate().advance |(i, pair)| {
enum_matching_fields[i].push(pair.second());
}
}
}
}
let field_tuples =
do self_vec.iter()
.zip(enum_matching_fields.iter())
.transform |(&(id, self_f), &other)| {
(id, self_f, other)
.transform |(&(id, self_f), other)| {
(id, self_f, copy *other)
}.collect();
substructure = EnumMatching(variant_index, variant, field_tuples);
}
@ -892,8 +896,8 @@ pub fn create_subpatterns(cx: @ExtCtxt,
field_paths: ~[ast::Path],
mutbl: ast::mutability)
-> ~[@ast::pat] {
do field_paths.map |&path| {
cx.pat(span, ast::pat_ident(ast::bind_by_ref(mutbl), path, None))
do field_paths.map |path| {
cx.pat(span, ast::pat_ident(ast::bind_by_ref(mutbl), copy *path, None))
}
}
@ -1015,7 +1019,8 @@ left-to-right (`true`) or right-to-left (`false`).
pub fn cs_fold(use_foldl: bool,
f: &fn(@ExtCtxt, span,
old: @expr,
self_f: @expr, other_fs: &[@expr]) -> @expr,
self_f: @expr,
other_fs: &[@expr]) -> @expr,
base: @expr,
enum_nonmatch_f: EnumNonMatchFunc,
cx: @ExtCtxt, span: span,
@ -1023,11 +1028,13 @@ pub fn cs_fold(use_foldl: bool,
match *substructure.fields {
EnumMatching(_, _, ref all_fields) | Struct(ref all_fields) => {
if use_foldl {
do all_fields.iter().fold(base) |old, &(_, self_f, other_fs)| {
do all_fields.iter().fold(base) |old, triple| {
let (_, self_f, other_fs) = copy *triple;
f(cx, span, old, self_f, other_fs)
}
} else {
do all_fields.rev_iter().fold(base) |old, &(_, self_f, other_fs)| {
do all_fields.rev_iter().fold(base) |old, triple| {
let (_, self_f, other_fs) = copy *triple;
f(cx, span, old, self_f, other_fs)
}
}
@ -1059,7 +1066,8 @@ pub fn cs_same_method(f: &fn(@ExtCtxt, span, ~[@expr]) -> @expr,
match *substructure.fields {
EnumMatching(_, _, ref all_fields) | Struct(ref all_fields) => {
// call self_n.method(other_1_n, other_2_n, ...)
let called = do all_fields.map |&(_, self_field, other_fields)| {
let called = do all_fields.map |triple| {
let (_, self_field, other_fields) = copy *triple;
cx.expr_method_call(span,
self_field,
substructure.method_ident,

View File

@ -137,7 +137,7 @@ impl gen_send for message {
let arg_names = vec::from_fn(tys.len(), |i| "x_" + i.to_str());
let args_ast: ~[ast::arg] = arg_names.iter().zip(tys.iter())
.transform(|(&n, t)| cx.arg(span, cx.ident_of(n), copy *t)).collect();
.transform(|(n, t)| cx.arg(span, cx.ident_of(*n), copy *t)).collect();
let args_ast = vec::append(
~[cx.arg(span,

View File

@ -215,8 +215,8 @@ pub fn visit<Tproto, Tstate, Tmessage, V: visitor<Tproto, Tstate, Tmessage>>(
// the copy keywords prevent recursive use of dvec
let states: ~[Tstate] = do (copy proto.states).iter().transform |&s| {
let messages: ~[Tmessage] = do (copy s.messages).iter().transform |&m| {
let message(name, span, tys, this, next) = m;
let messages: ~[Tmessage] = do (copy s.messages).iter().transform |m| {
let message(name, span, tys, this, next) = copy *m;
visitor.visit_message(name, span, tys, this, next)
}.collect();
visitor.visit_state(s, messages)

View File

@ -3914,7 +3914,7 @@ impl Parser {
};
let full_path = full_path.normalize();
let maybe_i = do self.sess.included_mod_stack.iter().position |&p| { p == full_path };
let maybe_i = do self.sess.included_mod_stack.iter().position |p| { *p == full_path };
match maybe_i {
Some(i) => {
let stack = &self.sess.included_mod_stack;

View File

@ -147,7 +147,7 @@ pub fn ty_to_str(ty: &ast::Ty, intr: @ident_interner) -> ~str {
}
pub fn pat_to_str(pat: &ast::pat, intr: @ident_interner) -> ~str {
to_str(pat, print_irrefutable_pat, intr)
to_str(pat, print_pat, intr)
}
pub fn expr_to_str(e: &ast::expr, intr: @ident_interner) -> ~str {
@ -1240,7 +1240,7 @@ pub fn print_expr(s: @ps, expr: &ast::expr) {
if first {
first = false;
} else { space(s.s); word_space(s, "|"); }
print_refutable_pat(s, *p);
print_pat(s, *p);
}
space(s.s);
match arm.guard {
@ -1434,7 +1434,7 @@ pub fn print_expr(s: @ps, expr: &ast::expr) {
}
pub fn print_local_decl(s: @ps, loc: &ast::local) {
print_irrefutable_pat(s, loc.node.pat);
print_pat(s, loc.node.pat);
match loc.node.ty.node {
ast::ty_infer => (),
_ => { word_space(s, ":"); print_type(s, &loc.node.ty); }
@ -1526,15 +1526,7 @@ pub fn print_bounded_path(s: @ps, path: &ast::Path,
print_path_(s, path, false, bounds)
}
pub fn print_irrefutable_pat(s: @ps, pat: &ast::pat) {
print_pat(s, pat, false)
}
pub fn print_refutable_pat(s: @ps, pat: &ast::pat) {
print_pat(s, pat, true)
}
pub fn print_pat(s: @ps, pat: &ast::pat, refutable: bool) {
pub fn print_pat(s: @ps, pat: &ast::pat) {
maybe_print_comment(s, pat.span.lo);
let ann_node = node_pat(s, pat);
(s.ann.pre)(ann_node);
@ -1543,20 +1535,18 @@ pub fn print_pat(s: @ps, pat: &ast::pat, refutable: bool) {
match pat.node {
ast::pat_wild => word(s.s, "_"),
ast::pat_ident(binding_mode, ref path, sub) => {
if refutable {
match binding_mode {
ast::bind_by_ref(mutbl) => {
word_nbsp(s, "ref");
print_mutability(s, mutbl);
}
ast::bind_infer => {}
match binding_mode {
ast::bind_by_ref(mutbl) => {
word_nbsp(s, "ref");
print_mutability(s, mutbl);
}
ast::bind_infer => {}
}
print_path(s, path, true);
match sub {
Some(p) => {
word(s.s, "@");
print_pat(s, p, refutable);
print_pat(s, p);
}
None => ()
}
@ -1569,7 +1559,7 @@ pub fn print_pat(s: @ps, pat: &ast::pat, refutable: bool) {
if !args.is_empty() {
popen(s);
commasep(s, inconsistent, *args,
|s, &p| print_pat(s, p, refutable));
|s, &p| print_pat(s, p));
pclose(s);
} else { }
}
@ -1578,16 +1568,16 @@ pub fn print_pat(s: @ps, pat: &ast::pat, refutable: bool) {
ast::pat_struct(ref path, ref fields, etc) => {
print_path(s, path, true);
word(s.s, "{");
fn print_field(s: @ps, f: &ast::field_pat, refutable: bool) {
fn print_field(s: @ps, f: &ast::field_pat) {
cbox(s, indent_unit);
print_ident(s, f.ident);
word_space(s, ":");
print_pat(s, f.pat, refutable);
print_pat(s, f.pat);
end(s);
}
fn get_span(f: &ast::field_pat) -> codemap::span { return f.pat.span; }
commasep_cmnt(s, consistent, *fields,
|s, f| print_field(s,f,refutable),
|s, f| print_field(s,f),
get_span);
if etc {
if fields.len() != 0u { word_space(s, ","); }
@ -1597,7 +1587,7 @@ pub fn print_pat(s: @ps, pat: &ast::pat, refutable: bool) {
}
ast::pat_tup(ref elts) => {
popen(s);
commasep(s, inconsistent, *elts, |s, &p| print_pat(s, p, refutable));
commasep(s, inconsistent, *elts, |s, &p| print_pat(s, p));
if elts.len() == 1 {
word(s.s, ",");
}
@ -1605,15 +1595,15 @@ pub fn print_pat(s: @ps, pat: &ast::pat, refutable: bool) {
}
ast::pat_box(inner) => {
word(s.s, "@");
print_pat(s, inner, refutable);
print_pat(s, inner);
}
ast::pat_uniq(inner) => {
word(s.s, "~");
print_pat(s, inner, refutable);
print_pat(s, inner);
}
ast::pat_region(inner) => {
word(s.s, "&");
print_pat(s, inner, refutable);
print_pat(s, inner);
}
ast::pat_lit(e) => print_expr(s, e),
ast::pat_range(begin, end) => {
@ -1625,16 +1615,16 @@ pub fn print_pat(s: @ps, pat: &ast::pat, refutable: bool) {
ast::pat_vec(ref before, slice, ref after) => {
word(s.s, "[");
do commasep(s, inconsistent, *before) |s, &p| {
print_pat(s, p, refutable);
print_pat(s, p);
}
for slice.iter().advance |&p| {
if !before.is_empty() { word_space(s, ","); }
word(s.s, "..");
print_pat(s, p, refutable);
print_pat(s, p);
if !after.is_empty() { word_space(s, ","); }
}
do commasep(s, inconsistent, *after) |s, &p| {
print_pat(s, p, refutable);
print_pat(s, p);
}
word(s.s, "]");
}
@ -1888,7 +1878,7 @@ pub fn print_arg(s: @ps, input: &ast::arg) {
word_space(s, "mut");
}
match input.ty.node {
ast::ty_infer => print_irrefutable_pat(s, input.pat),
ast::ty_infer => print_pat(s, input.pat),
_ => {
match input.pat.node {
ast::pat_ident(_, ref path, _) if
@ -1897,7 +1887,7 @@ pub fn print_arg(s: @ps, input: &ast::arg) {
// Do nothing.
}
_ => {
print_irrefutable_pat(s, input.pat);
print_pat(s, input.pat);
word(s.s, ":");
space(s.s);
}

View File

@ -39,10 +39,6 @@ rust_opaque_box *boxed_region::malloc(type_desc *td, size_t body_size) {
rust_opaque_box *boxed_region::realloc(rust_opaque_box *box,
size_t new_size) {
// We also get called on the unique-vec-in-managed-heap path.
assert(box->ref_count == 1 ||
box->ref_count == (size_t)(-2));
size_t total_size = new_size + sizeof(rust_opaque_box);
rust_opaque_box *new_box =
(rust_opaque_box*)backing_region->realloc(box, total_size);

View File

@ -56,8 +56,8 @@ fn sort_and_fmt(mm: &HashMap<~[u8], uint>, total: uint) -> ~str {
let mut pairs = ~[];
// map -> [(k,%)]
for mm.iter().advance |(&key, &val)| {
pairs.push((key, pct(val, total)));
for mm.iter().advance |(key, &val)| {
pairs.push((copy *key, pct(val, total)));
}
let pairs_sorted = sortKV(pairs);

View File

@ -0,0 +1,16 @@
fn with(f: &fn(&~str)) {}
fn arg_item(&_x: &~str) {}
//~^ ERROR cannot move out of dereference of & pointer
fn arg_closure() {
with(|&_x| ())
//~^ ERROR cannot move out of dereference of & pointer
}
fn let_pat() {
let &_x = &~"hi";
//~^ ERROR cannot move out of dereference of & pointer
}
pub fn main() {}

View File

@ -0,0 +1,22 @@
struct S {f:~str}
impl Drop for S {
fn drop(&self) { println(self.f); }
}
fn move_in_match() {
match S {f:~"foo"} {
S {f:_s} => {}
//~^ ERROR cannot move out of type `S`, which defines the `Drop` trait
}
}
fn move_in_let() {
let S {f:_s} = S {f:~"foo"};
//~^ ERROR cannot move out of type `S`, which defines the `Drop` trait
}
fn move_in_fn_arg(S {f:_s}: S) {
//~^ ERROR cannot move out of type `S`, which defines the `Drop` trait
}
fn main() {}

View File

@ -0,0 +1,22 @@
struct S(~str);
impl Drop for S {
fn drop(&self) { println(**self); }
}
fn move_in_match() {
match S(~"foo") {
S(_s) => {}
//~^ ERROR cannot move out of type `S`, which defines the `Drop` trait
}
}
fn move_in_let() {
let S(_s) = S(~"foo");
//~^ ERROR cannot move out of type `S`, which defines the `Drop` trait
}
fn move_in_fn_arg(S(_s): S) {
//~^ ERROR cannot move out of type `S`, which defines the `Drop` trait
}
fn main() {}

View File

@ -11,7 +11,7 @@ pub fn main() {
Foo { string: ~"baz" }
];
match x {
[first, ..tail] => {
[_, ..tail] => {
match tail {
[Foo { string: a }, Foo { string: b }] => {
//~^ ERROR cannot move out of dereference of & pointer

View File

@ -17,4 +17,41 @@ fn b() {
}
}
fn c() {
let mut vec = [~1, ~2, ~3];
match vec {
[_a, .._b] => {
//~^ ERROR cannot move out
// Note: `_a` is *moved* here, but `b` is borrowing,
// hence illegal.
//
// See comment in middle/borrowck/gather_loans/mod.rs
// in the case covering these sorts of vectors.
}
_ => {}
}
let a = vec[0]; //~ ERROR use of partially moved value: `vec`
}
fn d() {
let mut vec = [~1, ~2, ~3];
match vec {
[.._a, _b] => {
//~^ ERROR cannot move out
}
_ => {}
}
let a = vec[0]; //~ ERROR use of partially moved value: `vec`
}
fn e() {
let mut vec = [~1, ~2, ~3];
match vec {
[_a, _b, _c] => {}
_ => {}
}
let a = vec[0]; //~ ERROR use of partially moved value: `vec`
}
fn main() {}

View File

@ -0,0 +1,11 @@
fn arg_item(~ref x: ~int) -> &'static int {
x //~^ ERROR borrowed value does not live long enough
}
fn with<R>(f: &fn(~int) -> R) -> R { f(~3) }
fn arg_closure() -> &'static int {
with(|~ref x| x) //~ ERROR borrowed value does not live long enough
}
fn main() {}

View File

@ -68,7 +68,7 @@ fn main() {
check_pp(ext_cx, *stmt, pprust::print_stmt, ~"let x = 20;");
let pat = quote_pat!(Some(_));
check_pp(ext_cx, pat, pprust::print_refutable_pat, ~"Some(_)");
check_pp(ext_cx, pat, pprust::print_pat, ~"Some(_)");
}

View File

@ -1,32 +0,0 @@
// Copyright 2012 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.
struct foo {bar: baz}
struct baz_ {baz: int}
type baz = @mut baz_;
trait frob {
fn frob(&self);
}
impl frob for foo {
fn frob(&self) {
really_impure(self.bar);
}
}
// Override default mode so that we are passing by value
fn really_impure(bar: baz) {
bar.baz = 3;
}
pub fn main() {}

View File

@ -28,12 +28,12 @@
fn main() {
let a = @mut [3i];
let a = @mut 3i;
let b = @mut [a];
let c = @mut b;
let c = @mut [3];
// this should freeze `a` only
let _x: &mut [int] = c[0];
let _x: &mut int = a;
// hence these writes should not fail:
b[0] = b[0];

View File

@ -0,0 +1,20 @@
// Test that we do not leak when the arg pattern must drop part of the
// argument (in this case, the `y` field).
struct Foo {
x: ~uint,
y: ~uint,
}
fn foo(Foo {x, _}: Foo) -> *uint {
let addr: *uint = &*x;
addr
}
fn main() {
let obj = ~1;
let objptr: *uint = &*obj;
let f = Foo {x: obj, y: ~2};
let xptr = foo(f);
assert_eq!(objptr, xptr);
}

View File

@ -0,0 +1,24 @@
// exec-env:RUST_POISON_ON_FREE=1
// Test argument patterns where we create refs to the inside of `~`
// boxes. Make sure that we don't free the box as we match the
// pattern.
fn getaddr(~ref x: ~uint) -> *uint {
let addr: *uint = &*x;
addr
}
fn checkval(~ref x: ~uint) -> uint {
*x
}
fn main() {
let obj = ~1;
let objptr: *uint = &*obj;
let xptr = getaddr(obj);
assert_eq!(objptr, xptr);
let obj = ~22;
assert_eq!(checkval(obj), 22);
}

View File

@ -0,0 +1,10 @@
// Test that we can compile code that uses a `_` in function argument
// patterns.
fn foo((x, _): (int, int)) -> int {
x
}
fn main() {
assert_eq!(foo((22, 23)), 22);
}

View File

@ -0,0 +1,5 @@
fn main() {
let x = ~"hello";
let ref y = x;
assert_eq!(x.slice(0, x.len()), y.slice(0, y.len()));
}

View File

@ -0,0 +1,27 @@
// Tests a tricky scenario involving string matching,
// copying, and moving to ensure that we don't segfault
// or double-free, as we were wont to do in the past.
use std::io;
use std::os;
fn parse_args() -> ~str {
let args = os::args();
let mut n = 0;
while n < args.len() {
match copy args[n] {
~"-v" => (),
s => {
return s;
}
}
n += 1;
}
return ~""
}
fn main() {
io::println(parse_args());
}

View File

@ -14,8 +14,11 @@
enum t { make_t(@int), clam, }
fn foo(s: @int) {
debug!(::std::sys::refcount(s));
let count = ::std::sys::refcount(s);
let x: t = make_t(s); // ref up
assert_eq!(::std::sys::refcount(s), count + 1u);
debug!(::std::sys::refcount(s));
match x {
make_t(y) => {
@ -38,6 +41,5 @@ pub fn main() {
debug!("%u", ::std::sys::refcount(s));
let count2 = ::std::sys::refcount(s);
let _ = ::std::sys::refcount(s); // don't get bitten by last-use.
assert_eq!(count, count2);
}

View File

@ -163,8 +163,8 @@ pub fn main() {
visit_ty::<i16>(vv);
visit_ty::<~[int]>(vv);
for v.types.iter().advance |&s| {
println(fmt!("type: %s", s));
for v.types.iter().advance |s| {
println(fmt!("type: %s", copy *s));
}
assert_eq!((*v.types).clone(), ~[~"bool", ~"int", ~"i8", ~"i16", ~"[", ~"int", ~"]"]);
}

View File

@ -9,7 +9,7 @@ pub fn main() {
Foo { string: ~"baz" }
];
match x {
[first, ..tail] => {
[ref first, ..tail] => {
assert!(first.string == ~"foo");
assert_eq!(tail.len(), 2);
assert!(tail[0].string == ~"bar");