diff --git a/src/doc/guide-container.md b/src/doc/guide-container.md index 1c79be1eb8247..ebad650a5340b 100644 --- a/src/doc/guide-container.md +++ b/src/doc/guide-container.md @@ -181,19 +181,25 @@ never call its underlying iterator again once `None` has been returned: ~~~ let xs = [1,2,3,4,5]; let mut calls = 0; -let it = xs.iter().scan((), |_, x| { - calls += 1; - if *x < 3 { Some(x) } else { None }}); -// the iterator will only yield 1 and 2 before returning None -// If we were to call it 5 times, calls would end up as 5, despite only 2 values -// being yielded (and therefore 3 unique calls being made). The fuse() adaptor -// can fix this. -let mut it = it.fuse(); -it.next(); -it.next(); -it.next(); -it.next(); -it.next(); + +{ + let it = xs.iter().scan((), |_, x| { + calls += 1; + if *x < 3 { Some(x) } else { None }}); + + // the iterator will only yield 1 and 2 before returning None + // If we were to call it 5 times, calls would end up as 5, despite + // only 2 values being yielded (and therefore 3 unique calls being + // made). The fuse() adaptor can fix this. + + let mut it = it.fuse(); + it.next(); + it.next(); + it.next(); + it.next(); + it.next(); +} + assert_eq!(calls, 3); ~~~ diff --git a/src/libgetopts/lib.rs b/src/libgetopts/lib.rs index e5e7c50d2cecd..34e09ac1913db 100644 --- a/src/libgetopts/lib.rs +++ b/src/libgetopts/lib.rs @@ -775,14 +775,13 @@ fn each_split_within<'a>(ss: &'a str, lim: uint, it: |&'a str| -> bool) let mut lim = lim; let mut cont = true; - let slice: || = || { cont = it(ss.slice(slice_start, last_end)) }; // if the limit is larger than the string, lower it to save cycles if lim >= fake_i { lim = fake_i; } - let machine: |(uint, char)| -> bool = |(i, c)| { + let machine: |&mut bool, (uint, char)| -> bool = |cont, (i, c)| { let whitespace = if ::std::char::is_whitespace(c) { Ws } else { Cr }; let limit = if (i - slice_start + 1) <= lim { UnderLim } else { OverLim }; @@ -794,24 +793,49 @@ fn each_split_within<'a>(ss: &'a str, lim: uint, it: |&'a str| -> bool) (B, Cr, OverLim) if (i - last_start + 1) > lim => fail!("word starting with {} longer than limit!", ss.slice(last_start, i + 1)), - (B, Cr, OverLim) => { slice(); slice_start = last_start; B } - (B, Ws, UnderLim) => { last_end = i; C } - (B, Ws, OverLim) => { last_end = i; slice(); A } - - (C, Cr, UnderLim) => { last_start = i; B } - (C, Cr, OverLim) => { slice(); slice_start = i; last_start = i; last_end = i; B } - (C, Ws, OverLim) => { slice(); A } - (C, Ws, UnderLim) => { C } + (B, Cr, OverLim) => { + *cont = it(ss.slice(slice_start, last_end)); + slice_start = last_start; + B + } + (B, Ws, UnderLim) => { + last_end = i; + C + } + (B, Ws, OverLim) => { + last_end = i; + *cont = it(ss.slice(slice_start, last_end)); + A + } + + (C, Cr, UnderLim) => { + last_start = i; + B + } + (C, Cr, OverLim) => { + *cont = it(ss.slice(slice_start, last_end)); + slice_start = i; + last_start = i; + last_end = i; + B + } + (C, Ws, OverLim) => { + *cont = it(ss.slice(slice_start, last_end)); + A + } + (C, Ws, UnderLim) => { + C + } }; - cont + *cont }; - ss.char_indices().advance(|x| machine(x)); + ss.char_indices().advance(|x| machine(&mut cont, x)); // Let the automaton 'run out' by supplying trailing whitespace while cont && match state { B | C => true, A => false } { - machine((fake_i, ' ')); + machine(&mut cont, (fake_i, ' ')); fake_i += 1; } return cont; diff --git a/src/libglob/lib.rs b/src/libglob/lib.rs index c6ecb7697da4e..25634b1808de2 100644 --- a/src/libglob/lib.rs +++ b/src/libglob/lib.rs @@ -28,6 +28,7 @@ #[crate_type = "dylib"]; #[license = "MIT/ASL2"]; +use std::cell::Cell; use std::{os, path}; use std::io::fs; use std::path::is_sep; @@ -342,22 +343,24 @@ impl Pattern { } fn matches_from(&self, - mut prev_char: Option, + prev_char: Option, mut file: &str, i: uint, options: MatchOptions) -> MatchResult { + let prev_char = Cell::new(prev_char); + let require_literal = |c| { (options.require_literal_separator && is_sep(c)) || (options.require_literal_leading_dot && c == '.' - && is_sep(prev_char.unwrap_or('/'))) + && is_sep(prev_char.get().unwrap_or('/'))) }; for (ti, token) in self.tokens.slice_from(i).iter().enumerate() { match *token { AnySequence => { loop { - match self.matches_from(prev_char, file, i + ti + 1, options) { + match self.matches_from(prev_char.get(), file, i + ti + 1, options) { SubPatternDoesntMatch => (), // keep trying m => return m, } @@ -370,7 +373,7 @@ impl Pattern { if require_literal(c) { return SubPatternDoesntMatch; } - prev_char = Some(c); + prev_char.set(Some(c)); file = next; } } @@ -400,7 +403,7 @@ impl Pattern { if !matches { return SubPatternDoesntMatch; } - prev_char = Some(c); + prev_char.set(Some(c)); file = next; } } diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index d6abc7b6954d3..099f376aded6c 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -361,21 +361,23 @@ pub mod write { let mut llvm_c_strs = ~[]; let mut llvm_args = ~[]; - let add = |arg: &str| { - let s = arg.to_c_str(); - llvm_args.push(s.with_ref(|p| p)); - llvm_c_strs.push(s); - }; - add("rustc"); // fake program name - add("-arm-enable-ehabi"); - add("-arm-enable-ehabi-descriptors"); - if vectorize_loop { add("-vectorize-loops"); } - if vectorize_slp { add("-vectorize-slp"); } - if sess.time_llvm_passes() { add("-time-passes"); } - if sess.print_llvm_passes() { add("-debug-pass=Structure"); } - - for arg in sess.opts.cg.llvm_args.iter() { - add(*arg); + { + let add = |arg: &str| { + let s = arg.to_c_str(); + llvm_args.push(s.with_ref(|p| p)); + llvm_c_strs.push(s); + }; + add("rustc"); // fake program name + add("-arm-enable-ehabi"); + add("-arm-enable-ehabi-descriptors"); + if vectorize_loop { add("-vectorize-loops"); } + if vectorize_slp { add("-vectorize-slp"); } + if sess.time_llvm_passes() { add("-time-passes"); } + if sess.print_llvm_passes() { add("-debug-pass=Structure"); } + + for arg in sess.opts.cg.llvm_args.iter() { + add(*arg); + } } INIT.doit(|| { @@ -631,7 +633,7 @@ pub fn mangle(sess: Session, ss: ast_map::Path, let mut n = ~"_ZN"; // _Z == Begin name-sequence, N == nested - let push = |s: &str| { + let push = |n: &mut ~str, s: &str| { let sani = sanitize(s); n.push_str(format!("{}{}", sani.len(), sani)); }; @@ -640,7 +642,7 @@ pub fn mangle(sess: Session, ss: ast_map::Path, for s in ss.iter() { match *s { PathName(s) | PathMod(s) | PathPrettyName(s, _) => { - push(sess.str_of(s)) + push(&mut n, sess.str_of(s)) } } } @@ -665,10 +667,10 @@ pub fn mangle(sess: Session, ss: ast_map::Path, } } if hash.len() > 0 { - push(hash); + push(&mut n, hash); } match vers { - Some(s) => push(s), + Some(s) => push(&mut n, s), None => {} } diff --git a/src/librustc/front/config.rs b/src/librustc/front/config.rs index f87c05ddab8a9..4fd72a4bbfc69 100644 --- a/src/librustc/front/config.rs +++ b/src/librustc/front/config.rs @@ -58,8 +58,10 @@ fn filter_view_item<'r>(cx: &Context, view_item: &'r ast::ViewItem) } fn fold_mod(cx: &mut Context, m: &ast::Mod) -> ast::Mod { - let filtered_items = m.items.iter() + let filtered_items: ~[&@ast::Item] = m.items.iter() .filter(|&a| item_in_cfg(cx, *a)) + .collect(); + let flattened_items = filtered_items.move_iter() .flat_map(|&x| cx.fold_item(x).move_iter()) .collect(); let filtered_view_items = m.view_items.iter().filter_map(|a| { @@ -67,7 +69,7 @@ fn fold_mod(cx: &mut Context, m: &ast::Mod) -> ast::Mod { }).collect(); ast::Mod { view_items: filtered_view_items, - items: filtered_items + items: flattened_items } } @@ -113,23 +115,26 @@ fn fold_item_underscore(cx: &mut Context, item: &ast::Item_) -> ast::Item_ { ast::ItemStruct(fold_struct(cx, def), generics.clone()) } ast::ItemEnum(ref def, ref generics) => { - let mut variants = def.variants.iter().map(|c| c.clone()).filter(|m| { - (cx.in_cfg)(m.node.attrs) - }).map(|v| { - match v.node.kind { - ast::TupleVariantKind(..) => v, - ast::StructVariantKind(def) => { - let def = fold_struct(cx, def); - @codemap::Spanned { - node: ast::Variant_ { - kind: ast::StructVariantKind(def), - ..v.node.clone() - }, - ..*v - } + let mut variants = def.variants.iter().map(|c| c.clone()). + filter_map(|v| { + if !(cx.in_cfg)(v.node.attrs) { + None + } else { + Some(match v.node.kind { + ast::TupleVariantKind(..) => v, + ast::StructVariantKind(def) => { + let def = fold_struct(cx, def); + @codemap::Spanned { + node: ast::Variant_ { + kind: ast::StructVariantKind(def), + ..v.node.clone() + }, + ..*v + } + } + }) } - } - }); + }); ast::ItemEnum(ast::EnumDef { variants: variants.collect(), }, generics.clone()) @@ -165,10 +170,11 @@ fn retain_stmt(cx: &Context, stmt: @ast::Stmt) -> bool { } fn fold_block(cx: &mut Context, b: ast::P) -> ast::P { - let resulting_stmts = b.stmts.iter() - .filter(|&a| retain_stmt(cx, *a)) - .flat_map(|&stmt| cx.fold_stmt(stmt).move_iter()) - .collect(); + let resulting_stmts: ~[&@ast::Stmt] = + b.stmts.iter().filter(|&a| retain_stmt(cx, *a)).collect(); + let resulting_stmts = resulting_stmts.move_iter() + .flat_map(|&stmt| cx.fold_stmt(stmt).move_iter()) + .collect(); let filtered_view_items = b.view_items.iter().filter_map(|a| { filter_view_item(cx, a).map(|x| cx.fold_view_item(x)) }).collect(); diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 4abfedb87228e..ea4d6d3b252f3 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -924,7 +924,6 @@ fn encode_info_for_item(ecx: &EncodeContext, pos: ebml_w.writer.tell().unwrap(), }); } - let add_to_index: || = || add_to_index(item, ebml_w, index); debug!("encoding info for item at {}", ecx.tcx.sess.codemap.span_to_str(item.span)); @@ -932,7 +931,7 @@ fn encode_info_for_item(ecx: &EncodeContext, let def_id = local_def(item.id); match item.node { ItemStatic(_, m, _) => { - add_to_index(); + add_to_index(item, ebml_w, index); ebml_w.start_tag(tag_items_data_item); encode_def_id(ebml_w, def_id); if m == ast::MutMutable { @@ -959,7 +958,7 @@ fn encode_info_for_item(ecx: &EncodeContext, ebml_w.end_tag(); } ItemFn(_, purity, _, ref generics, _) => { - add_to_index(); + add_to_index(item, ebml_w, index); ebml_w.start_tag(tag_items_data_item); encode_def_id(ebml_w, def_id); encode_family(ebml_w, purity_fn_family(purity)); @@ -977,7 +976,7 @@ fn encode_info_for_item(ecx: &EncodeContext, ebml_w.end_tag(); } ItemMod(ref m) => { - add_to_index(); + add_to_index(item, ebml_w, index); encode_info_for_mod(ecx, ebml_w, m, @@ -987,7 +986,7 @@ fn encode_info_for_item(ecx: &EncodeContext, item.vis); } ItemForeignMod(ref fm) => { - add_to_index(); + add_to_index(item, ebml_w, index); ebml_w.start_tag(tag_items_data_item); encode_def_id(ebml_w, def_id); encode_family(ebml_w, 'n'); @@ -1004,7 +1003,7 @@ fn encode_info_for_item(ecx: &EncodeContext, ebml_w.end_tag(); } ItemTy(..) => { - add_to_index(); + add_to_index(item, ebml_w, index); ebml_w.start_tag(tag_items_data_item); encode_def_id(ebml_w, def_id); encode_family(ebml_w, 'y'); @@ -1015,7 +1014,7 @@ fn encode_info_for_item(ecx: &EncodeContext, ebml_w.end_tag(); } ItemEnum(ref enum_definition, ref generics) => { - add_to_index(); + add_to_index(item, ebml_w, index); ebml_w.start_tag(tag_items_data_item); encode_def_id(ebml_w, def_id); @@ -1053,7 +1052,7 @@ fn encode_info_for_item(ecx: &EncodeContext, struct_def.fields, index); /* Index the class*/ - add_to_index(); + add_to_index(item, ebml_w, index); /* Now, make an item for the class itself */ ebml_w.start_tag(tag_items_data_item); @@ -1106,7 +1105,7 @@ fn encode_info_for_item(ecx: &EncodeContext, let impls = tcx.impls.borrow(); let imp = impls.get().get(&def_id); - add_to_index(); + add_to_index(item, ebml_w, index); ebml_w.start_tag(tag_items_data_item); encode_def_id(ebml_w, def_id); encode_family(ebml_w, 'i'); @@ -1170,7 +1169,7 @@ fn encode_info_for_item(ecx: &EncodeContext, } } ItemTrait(_, ref super_traits, ref ms) => { - add_to_index(); + add_to_index(item, ebml_w, index); ebml_w.start_tag(tag_items_data_item); encode_def_id(ebml_w, def_id); encode_family(ebml_w, 'I'); diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index 42e4d986837be..00e189cdc792d 100644 --- a/src/librustc/metadata/tydecode.rs +++ b/src/librustc/metadata/tydecode.rs @@ -96,8 +96,9 @@ pub fn parse_ident(st: &mut PState, last: char) -> ast::Ident { } fn parse_ident_(st: &mut PState, is_last: |char| -> bool) -> ast::Ident { + let tcx = st.tcx; scan(st, is_last, |bytes| { - st.tcx.sess.ident_of(str::from_utf8(bytes).unwrap()) + tcx.sess.ident_of(str::from_utf8(bytes).unwrap()) }) } diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs index cfd2c0719b09b..cb1a803c35a0f 100644 --- a/src/librustc/middle/borrowck/check_loans.rs +++ b/src/librustc/middle/borrowck/check_loans.rs @@ -52,9 +52,11 @@ impl<'a> Visitor<()> for CheckLoanCtxt<'a> { fn visit_pat(&mut self, p: &ast::Pat, _: ()) { check_loans_in_pat(self, p); } - fn visit_fn(&mut self, fk: &visit::FnKind, fd: &ast::FnDecl, - b: &ast::Block, s: Span, n: ast::NodeId, _: ()) { - check_loans_in_fn(self, fk, fd, b, s, n); + fn visit_fn(&mut self, _fk: &visit::FnKind, _fd: &ast::FnDecl, + _b: &ast::Block, _s: Span, _n: ast::NodeId, _: ()) { + // Don't process nested items or closures here, + // the outer loop will take care of it. + return; } // FIXME(#10894) should continue recursing @@ -218,9 +220,19 @@ impl<'a> CheckLoanCtxt<'a> { loan2.repr(self.tcx())); // Restrictions that would cause the new loan to be illegal: - let illegal_if = match loan2.mutbl { - MutableMutability => RESTR_FREEZE | RESTR_CLAIM, - ImmutableMutability => RESTR_FREEZE, + let illegal_if = match loan2.kind { + // Look for restrictions against mutation. These are + // generated by all other borrows. + ty::MutBorrow => RESTR_MUTATE, + + // Look for restrictions against freezing (immutable borrows). + // These are generated by `&mut` borrows. + ty::ImmBorrow => RESTR_FREEZE, + + // No matter how the data is borrowed (as `&`, as `&mut`, + // or as `&unique imm`) it will always generate a + // restriction against mutating the data. So look for those. + ty::UniqueImmBorrow => RESTR_MUTATE, }; debug!("illegal_if={:?}", illegal_if); @@ -228,47 +240,107 @@ impl<'a> CheckLoanCtxt<'a> { if !restr.set.intersects(illegal_if) { continue; } if restr.loan_path != loan2.loan_path { continue; } - match (new_loan.mutbl, old_loan.mutbl) { - (_, MutableMutability) => { - let var = self.bccx.loan_path_to_str(new_loan.loan_path); + let old_pronoun = if new_loan.loan_path == old_loan.loan_path { + ~"it" + } else { + format!("`{}`", + self.bccx.loan_path_to_str(old_loan.loan_path)) + }; + + match (new_loan.kind, old_loan.kind) { + (ty::MutBorrow, ty::MutBorrow) => { self.bccx.span_err( new_loan.span, - format!("cannot borrow `{}` because it is already \ - borrowed as mutable", var)); - self.bccx.span_note( - old_loan.span, - format!("previous borrow of `{0}` as mutable occurs \ - here; the mutable borrow prevents subsequent \ - moves, borrows, or modification of `{0}` \ - until the borrow ends", var)); + format!("cannot borrow `{}` as mutable \ + more than once at a time", + self.bccx.loan_path_to_str(new_loan.loan_path))); + } + + (ty::UniqueImmBorrow, _) => { + self.bccx.span_err( + new_loan.span, + format!("closure requires unique access to `{}` \ + but {} is already borrowed", + self.bccx.loan_path_to_str(new_loan.loan_path), + old_pronoun)); } - (_, mutability) => { + (_, ty::UniqueImmBorrow) => { self.bccx.span_err( new_loan.span, format!("cannot borrow `{}` as {} because \ - it is already borrowed as {}", - self.bccx.loan_path_to_str(new_loan.loan_path), - self.bccx.mut_to_str(new_loan.mutbl), - self.bccx.mut_to_str(old_loan.mutbl))); - - let var = self.bccx.loan_path_to_str(new_loan.loan_path); - let mut note = format!("previous borrow of `{}` occurs \ - here", var); - if mutability == ImmutableMutability { - note.push_str(format!("; the immutable borrow prevents \ - subsequent moves or mutable - borrows of `{}` until the - borrow ends", var)); - } - self.bccx.span_note(old_loan.span, note); + previous closure requires unique access", + self.bccx.loan_path_to_str(new_loan.loan_path), + new_loan.kind.to_user_str())); + } + + (_, _) => { + self.bccx.span_err( + new_loan.span, + format!("cannot borrow `{}` as {} because \ + {} is also borrowed as {}", + self.bccx.loan_path_to_str(new_loan.loan_path), + new_loan.kind.to_user_str(), + old_pronoun, + old_loan.kind.to_user_str())); } } + match new_loan.cause { + ClosureCapture(span) => { + self.bccx.span_note( + span, + format!("borrow occurs due to use of `{}` in closure", + self.bccx.loan_path_to_str(new_loan.loan_path))); + } + _ => { } + } + + let rule_summary = match old_loan.kind { + ty::MutBorrow => { + format!("the mutable borrow prevents subsequent \ + moves, borrows, or modification of `{0}` \ + until the borrow ends", + self.bccx.loan_path_to_str(old_loan.loan_path)) + } + + ty::ImmBorrow => { + format!("the immutable borrow prevents subsequent \ + moves or mutable borrows of `{0}` \ + until the borrow ends", + self.bccx.loan_path_to_str(old_loan.loan_path)) + } + + ty::UniqueImmBorrow => { + format!("the unique capture prevents subsequent \ + moves or borrows of `{0}` \ + until the borrow ends", + self.bccx.loan_path_to_str(old_loan.loan_path)) + } + }; + + let borrow_summary = match old_loan.cause { + ClosureCapture(_) => { + format!("previous borrow of `{}` occurs here due to \ + use in closure", + self.bccx.loan_path_to_str(old_loan.loan_path)) + } + + AddrOf | AutoRef | RefBinding => { + format!("previous borrow of `{}` occurs here", + self.bccx.loan_path_to_str(old_loan.loan_path)) + } + }; + + self.bccx.span_note( + old_loan.span, + format!("{}; {}", borrow_summary, rule_summary)); + let old_loan_span = ast_map::node_span(self.tcx().items, old_loan.kill_scope); self.bccx.span_end_note(old_loan_span, "previous borrow ends here"); + return false; } @@ -349,11 +421,23 @@ impl<'a> CheckLoanCtxt<'a> { } // Otherwise, just a plain error. - self.bccx.span_err( - expr.span, - format!("cannot assign to {} {}", - cmt.mutbl.to_user_str(), - self.bccx.cmt_to_str(cmt))); + match opt_loan_path(cmt) { + Some(lp) => { + self.bccx.span_err( + expr.span, + format!("cannot assign to {} {} `{}`", + cmt.mutbl.to_user_str(), + self.bccx.cmt_to_str(cmt), + self.bccx.loan_path_to_str(lp))); + } + None => { + self.bccx.span_err( + expr.span, + format!("cannot assign to {} {}", + cmt.mutbl.to_user_str(), + self.bccx.cmt_to_str(cmt))); + } + } return; fn mark_variable_as_used_mut(this: &CheckLoanCtxt, @@ -377,11 +461,11 @@ impl<'a> CheckLoanCtxt<'a> { return; } - mc::cat_stack_upvar(b) => { - cmt = b; + mc::cat_upvar(..) => { + return; } - mc::cat_deref(_, _, mc::gc_ptr) => { + mc::cat_deref(_, _, mc::GcPtr) => { assert_eq!(cmt.mutbl, mc::McImmutable); return; } @@ -389,25 +473,22 @@ impl<'a> CheckLoanCtxt<'a> { mc::cat_rvalue(..) | mc::cat_static_item | mc::cat_copied_upvar(..) | - mc::cat_deref(_, _, mc::unsafe_ptr(..)) | - mc::cat_deref(_, _, mc::region_ptr(..)) => { + mc::cat_deref(_, _, mc::UnsafePtr(..)) | + mc::cat_deref(_, _, mc::BorrowedPtr(..)) => { assert_eq!(cmt.mutbl, mc::McDeclared); return; } mc::cat_discr(b, _) | - mc::cat_deref(b, _, mc::uniq_ptr) => { + mc::cat_deref(b, _, mc::OwnedPtr) => { assert_eq!(cmt.mutbl, mc::McInherited); cmt = b; } mc::cat_downcast(b) | mc::cat_interior(b, _) => { - if cmt.mutbl == mc::McInherited { - cmt = b; - } else { - return; // field declared as mutable or some such - } + assert_eq!(cmt.mutbl, mc::McInherited); + cmt = b; } } } @@ -422,7 +503,7 @@ impl<'a> CheckLoanCtxt<'a> { debug!("check_for_aliasable_mutable_writes(cmt={}, guarantor={})", cmt.repr(this.tcx()), guarantor.repr(this.tcx())); match guarantor.cat { - mc::cat_deref(b, _, mc::region_ptr(ast::MutMutable, _)) => { + mc::cat_deref(b, _, mc::BorrowedPtr(ty::MutBorrow, _)) => { // Statically prohibit writes to `&mut` when aliasable check_for_aliasability_violation(this, expr, b); @@ -557,7 +638,7 @@ impl<'a> CheckLoanCtxt<'a> { // with inherited mutability and with `&mut` // pointers. LpExtend(lp_base, mc::McInherited, _) | - LpExtend(lp_base, _, LpDeref(mc::region_ptr(ast::MutMutable, _))) => { + LpExtend(lp_base, _, LpDeref(mc::BorrowedPtr(ty::MutBorrow, _))) => { loan_path = lp_base; } @@ -572,9 +653,7 @@ impl<'a> CheckLoanCtxt<'a> { // Check for a non-const loan of `loan_path` let cont = this.each_in_scope_loan(expr.id, |loan| { if loan.loan_path == loan_path { - this.report_illegal_mutation(expr, - full_loan_path, - loan); + this.report_illegal_mutation(expr, full_loan_path, loan); false } else { true @@ -603,9 +682,10 @@ impl<'a> CheckLoanCtxt<'a> { fn check_move_out_from_expr(&self, expr: &ast::Expr) { match expr.node { ast::ExprFnBlock(..) | ast::ExprProc(..) => { - // moves due to capture clauses are checked - // in `check_loans_in_fn`, so that we can - // give a better error message + // Moves due to captures are checked in + // check_captured_variables() because it allows + // us to give a more precise error message with + // a more precise span. } _ => { self.check_move_out_from_id(expr.id, expr.span) @@ -621,18 +701,59 @@ impl<'a> CheckLoanCtxt<'a> { self.bccx.span_err( span, format!("cannot move out of `{}` \ - because it is borrowed", + because it is borrowed", self.bccx.loan_path_to_str(move_path))); self.bccx.span_note( loan_span, format!("borrow of `{}` occurs here", - self.bccx.loan_path_to_str(loan_path))); + self.bccx.loan_path_to_str(loan_path))); } } true }); } + fn check_captured_variables(&self, + closure_id: ast::NodeId, + span: Span) { + let capture_map = self.bccx.capture_map.borrow(); + let cap_vars = capture_map.get().get(&closure_id); + for cap_var in cap_vars.borrow().iter() { + let var_id = ast_util::def_id_of_def(cap_var.def).node; + let var_path = @LpVar(var_id); + self.check_if_path_is_moved(closure_id, span, + MovedInCapture, var_path); + match cap_var.mode { + moves::CapRef | moves::CapCopy => {} + moves::CapMove => { + check_by_move_capture(self, closure_id, cap_var, var_path); + } + } + } + return; + + fn check_by_move_capture(this: &CheckLoanCtxt, + closure_id: ast::NodeId, + cap_var: &moves::CaptureVar, + move_path: @LoanPath) { + let move_err = this.analyze_move_out_from(closure_id, move_path); + match move_err { + MoveOk => {} + MoveWhileBorrowed(loan_path, loan_span) => { + this.bccx.span_err( + cap_var.span, + format!("cannot move `{}` into closure \ + because it is borrowed", + this.bccx.loan_path_to_str(move_path))); + this.bccx.span_note( + loan_span, + format!("borrow of `{}` occurs here", + this.bccx.loan_path_to_str(loan_path))); + } + } + } + } + pub fn analyze_move_out_from(&self, expr_id: ast::NodeId, mut move_path: @LoanPath) @@ -681,67 +802,6 @@ impl<'a> CheckLoanCtxt<'a> { } } -fn check_loans_in_fn<'a>(this: &mut CheckLoanCtxt<'a>, - fk: &visit::FnKind, - decl: &ast::FnDecl, - body: &ast::Block, - sp: Span, - id: ast::NodeId) { - match *fk { - visit::FkItemFn(..) | visit::FkMethod(..) => { - // Don't process nested items. - return; - } - - visit::FkFnBlock(..) => { - check_captured_variables(this, id, sp); - } - } - - visit::walk_fn(this, fk, decl, body, sp, id, ()); - - fn check_captured_variables(this: &CheckLoanCtxt, - closure_id: ast::NodeId, - span: Span) { - let capture_map = this.bccx.capture_map.borrow(); - let cap_vars = capture_map.get().get(&closure_id); - for cap_var in cap_vars.borrow().iter() { - let var_id = ast_util::def_id_of_def(cap_var.def).node; - let var_path = @LpVar(var_id); - this.check_if_path_is_moved(closure_id, span, - MovedInCapture, var_path); - match cap_var.mode { - moves::CapRef | moves::CapCopy => {} - moves::CapMove => { - check_by_move_capture(this, closure_id, cap_var, var_path); - } - } - } - return; - - fn check_by_move_capture(this: &CheckLoanCtxt, - closure_id: ast::NodeId, - cap_var: &moves::CaptureVar, - move_path: @LoanPath) { - let move_err = this.analyze_move_out_from(closure_id, move_path); - match move_err { - MoveOk => {} - MoveWhileBorrowed(loan_path, loan_span) => { - this.bccx.span_err( - cap_var.span, - format!("cannot move `{}` into closure \ - because it is borrowed", - this.bccx.loan_path_to_str(move_path))); - this.bccx.span_note( - loan_span, - format!("borrow of `{}` occurs here", - this.bccx.loan_path_to_str(loan_path))); - } - } - } - } -} - fn check_loans_in_local<'a>(this: &mut CheckLoanCtxt<'a>, local: &ast::Local) { visit::walk_local(this, local, ()); @@ -769,6 +829,9 @@ fn check_loans_in_expr<'a>(this: &mut CheckLoanCtxt<'a>, } } } + ast::ExprFnBlock(..) | ast::ExprProc(..) => { + this.check_captured_variables(expr.id, expr.span) + } ast::ExprAssign(dest, _) | ast::ExprAssignOp(_, _, dest, _) => { this.check_assignment(dest); diff --git a/src/librustc/middle/borrowck/gather_loans/gather_moves.rs b/src/librustc/middle/borrowck/gather_loans/gather_moves.rs index 0d9b4b0b171cc..49b12a6db1fb5 100644 --- a/src/librustc/middle/borrowck/gather_loans/gather_moves.rs +++ b/src/librustc/middle/borrowck/gather_loans/gather_moves.rs @@ -18,9 +18,8 @@ use middle::borrowck::move_data::*; use middle::moves; use middle::ty; use syntax::ast; -use syntax::ast_util; use syntax::codemap::Span; -use util::ppaux::{UserString}; +use util::ppaux::{Repr, UserString}; pub fn gather_decl(bccx: &BorrowckCtxt, move_data: &MoveData, @@ -35,33 +34,14 @@ pub fn gather_move_from_expr(bccx: &BorrowckCtxt, move_data: &MoveData, move_expr: &ast::Expr, cmt: mc::cmt) { - gather_move_from_expr_or_pat(bccx, move_data, move_expr.id, MoveExpr, cmt); + gather_move(bccx, move_data, move_expr.id, MoveExpr, cmt); } pub fn gather_move_from_pat(bccx: &BorrowckCtxt, move_data: &MoveData, move_pat: &ast::Pat, cmt: mc::cmt) { - gather_move_from_expr_or_pat(bccx, move_data, move_pat.id, MovePat, cmt); -} - -fn gather_move_from_expr_or_pat(bccx: &BorrowckCtxt, - move_data: &MoveData, - move_id: ast::NodeId, - move_kind: MoveKind, - cmt: mc::cmt) { - if !check_is_legal_to_move_from(bccx, cmt, cmt) { - return; - } - - match opt_loan_path(cmt) { - Some(loan_path) => { - move_data.add_move(bccx.tcx, loan_path, move_id, move_kind); - } - None => { - // move from rvalue or unsafe pointer, hence ok - } - } + gather_move(bccx, move_data, move_pat.id, MovePat, cmt); } pub fn gather_captures(bccx: &BorrowckCtxt, @@ -72,16 +52,38 @@ pub fn gather_captures(bccx: &BorrowckCtxt, for captured_var in captured_vars.borrow().iter() { match captured_var.mode { moves::CapMove => { - let fvar_id = ast_util::def_id_of_def(captured_var.def).node; - let loan_path = @LpVar(fvar_id); - move_data.add_move(bccx.tcx, loan_path, closure_expr.id, - Captured); + let cmt = bccx.cat_captured_var(closure_expr.id, + closure_expr.span, + captured_var); + gather_move(bccx, move_data, closure_expr.id, Captured, cmt); } moves::CapCopy | moves::CapRef => {} } } } +fn gather_move(bccx: &BorrowckCtxt, + move_data: &MoveData, + move_id: ast::NodeId, + move_kind: MoveKind, + cmt: mc::cmt) { + debug!("gather_move(move_id={}, cmt={})", + move_id, cmt.repr(bccx.tcx)); + + if !check_is_legal_to_move_from(bccx, cmt, cmt) { + return; + } + + match opt_loan_path(cmt) { + Some(loan_path) => { + move_data.add_move(bccx.tcx, loan_path, move_id, move_kind); + } + None => { + // move from rvalue or unsafe pointer, hence ok + } + } +} + pub fn gather_assignment(bccx: &BorrowckCtxt, move_data: &MoveData, assignment_id: ast::NodeId, @@ -99,15 +101,15 @@ fn check_is_legal_to_move_from(bccx: &BorrowckCtxt, cmt0: mc::cmt, cmt: mc::cmt) -> bool { match cmt.cat { - mc::cat_deref(_, _, mc::region_ptr(..)) | - mc::cat_deref(_, _, mc::gc_ptr) | - mc::cat_deref(_, _, mc::unsafe_ptr(..)) | - mc::cat_stack_upvar(..) | + mc::cat_deref(_, _, mc::BorrowedPtr(..)) | + mc::cat_deref(_, _, mc::GcPtr) | + mc::cat_deref(_, _, mc::UnsafePtr(..)) | + mc::cat_upvar(..) | mc::cat_copied_upvar(mc::CopiedUpvar { onceness: ast::Many, .. }) => { bccx.span_err( cmt0.span, format!("cannot move out of {}", - bccx.cmt_to_str(cmt))); + bccx.cmt_to_str(cmt))); false } @@ -158,7 +160,7 @@ fn check_is_legal_to_move_from(bccx: &BorrowckCtxt, } } - mc::cat_deref(b, _, mc::uniq_ptr) | + mc::cat_deref(b, _, mc::OwnedPtr) | mc::cat_discr(b, _) => { check_is_legal_to_move_from(bccx, cmt0, b) } diff --git a/src/librustc/middle/borrowck/gather_loans/lifetime.rs b/src/librustc/middle/borrowck/gather_loans/lifetime.rs index e151f39845860..c47affac683f4 100644 --- a/src/librustc/middle/borrowck/gather_loans/lifetime.rs +++ b/src/librustc/middle/borrowck/gather_loans/lifetime.rs @@ -26,16 +26,19 @@ pub fn guarantee_lifetime(bccx: &BorrowckCtxt, item_scope_id: ast::NodeId, root_scope_id: ast::NodeId, span: Span, + cause: LoanCause, cmt: mc::cmt, loan_region: ty::Region, - loan_mutbl: LoanMutability) -> R { + loan_kind: ty::BorrowKind) + -> Result<(),()> { debug!("guarantee_lifetime(cmt={}, loan_region={})", cmt.repr(bccx.tcx), loan_region.repr(bccx.tcx)); let ctxt = GuaranteeLifetimeContext {bccx: bccx, item_scope_id: item_scope_id, span: span, + cause: cause, loan_region: loan_region, - loan_mutbl: loan_mutbl, + loan_kind: loan_kind, cmt_original: cmt, root_scope_id: root_scope_id}; ctxt.check(cmt, None) @@ -55,8 +58,9 @@ struct GuaranteeLifetimeContext<'a> { root_scope_id: ast::NodeId, span: Span, + cause: LoanCause, loan_region: ty::Region, - loan_mutbl: LoanMutability, + loan_kind: ty::BorrowKind, cmt_original: mc::cmt } @@ -76,21 +80,18 @@ impl<'a> GuaranteeLifetimeContext<'a> { mc::cat_copied_upvar(..) | // L-Local mc::cat_local(..) | // L-Local mc::cat_arg(..) | // L-Local - mc::cat_deref(_, _, mc::region_ptr(..)) | // L-Deref-Borrowed - mc::cat_deref(_, _, mc::unsafe_ptr(..)) => { + mc::cat_upvar(..) | + mc::cat_deref(_, _, mc::BorrowedPtr(..)) | // L-Deref-Borrowed + mc::cat_deref(_, _, mc::UnsafePtr(..)) => { let scope = self.scope(cmt); self.check_scope(scope) } - mc::cat_stack_upvar(cmt) => { - self.check(cmt, discr_scope) - } - mc::cat_static_item => { Ok(()) } - mc::cat_deref(base, derefs, mc::gc_ptr) => { + mc::cat_deref(base, derefs, mc::GcPtr) => { let base_scope = self.scope(base); // L-Deref-Managed-Imm-User-Root @@ -112,7 +113,7 @@ impl<'a> GuaranteeLifetimeContext<'a> { } mc::cat_downcast(base) | - mc::cat_deref(base, _, mc::uniq_ptr) | // L-Deref-Send + mc::cat_deref(base, _, mc::OwnedPtr) | // L-Deref-Send mc::cat_interior(base, _) => { // L-Field self.check(base, discr_scope) } @@ -269,12 +270,12 @@ impl<'a> GuaranteeLifetimeContext<'a> { mc::cat_rvalue(..) | mc::cat_static_item | mc::cat_copied_upvar(..) | - mc::cat_deref(..) => { + mc::cat_deref(..) | + mc::cat_upvar(..) => { false } r @ mc::cat_downcast(..) | r @ mc::cat_interior(..) | - r @ mc::cat_stack_upvar(..) | r @ mc::cat_discr(..) => { self.tcx().sess.span_bug( cmt.span, @@ -294,6 +295,7 @@ impl<'a> GuaranteeLifetimeContext<'a> { mc::cat_rvalue(temp_scope) => { temp_scope } + mc::cat_upvar(..) | mc::cat_copied_upvar(_) => { ty::ReScope(self.item_scope_id) } @@ -304,17 +306,16 @@ impl<'a> GuaranteeLifetimeContext<'a> { mc::cat_arg(local_id) => { ty::ReScope(self.bccx.tcx.region_maps.var_scope(local_id)) } - mc::cat_deref(_, _, mc::unsafe_ptr(..)) => { + mc::cat_deref(_, _, mc::UnsafePtr(..)) => { ty::ReStatic } - mc::cat_deref(_, _, mc::region_ptr(_, r)) => { + mc::cat_deref(_, _, mc::BorrowedPtr(_, r)) => { r } mc::cat_downcast(cmt) | - mc::cat_deref(cmt, _, mc::uniq_ptr) | - mc::cat_deref(cmt, _, mc::gc_ptr) | + mc::cat_deref(cmt, _, mc::OwnedPtr) | + mc::cat_deref(cmt, _, mc::GcPtr) | mc::cat_interior(cmt, _) | - mc::cat_stack_upvar(cmt) | mc::cat_discr(cmt, _) => { self.scope(cmt) } @@ -322,10 +323,9 @@ impl<'a> GuaranteeLifetimeContext<'a> { } fn report_error(&self, code: bckerr_code) { - self.bccx.report(BckError { - cmt: self.cmt_original, - span: self.span, - code: code - }); + self.bccx.report(BckError { cmt: self.cmt_original, + span: self.span, + cause: self.cause, + code: code }); } } diff --git a/src/librustc/middle/borrowck/gather_loans/mod.rs b/src/librustc/middle/borrowck/gather_loans/mod.rs index 957073ac392bd..c6a77988bced9 100644 --- a/src/librustc/middle/borrowck/gather_loans/mod.rs +++ b/src/librustc/middle/borrowck/gather_loans/mod.rs @@ -16,10 +16,10 @@ // their associated scopes. In phase two, checking loans, we will then make // sure that all of these loans are honored. - use middle::borrowck::*; use middle::borrowck::move_data::MoveData; use mc = middle::mem_categorization; +use middle::moves; use middle::pat_util; use middle::ty::{ty_region}; use middle::ty; @@ -28,6 +28,7 @@ use util::ppaux::{Repr}; use std::cell::RefCell; use syntax::ast; +use syntax::ast_util; use syntax::ast_util::IdRange; use syntax::codemap::Span; use syntax::print::pprust; @@ -127,22 +128,15 @@ fn add_pat_to_id_range(this: &mut GatherLoanCtxt, visit::walk_pat(this, p, ()); } -fn gather_loans_in_fn(this: &mut GatherLoanCtxt, fk: &FnKind, - decl: &ast::FnDecl, body: &ast::Block, - sp: Span, id: ast::NodeId) { - match fk { - &visit::FkItemFn(..) | &visit::FkMethod(..) => { - fail!("cannot occur, due to visit_item override"); - } - - // Visit closures as part of the containing item. - &visit::FkFnBlock(..) => { - this.push_repeating_id(body.id); - visit::walk_fn(this, fk, decl, body, sp, id, ()); - this.pop_repeating_id(body.id); - this.gather_fn_arg_patterns(decl, body); - } - } +fn gather_loans_in_fn(_v: &mut GatherLoanCtxt, + _fk: &FnKind, + _decl: &ast::FnDecl, + _body: &ast::Block, + _sp: Span, + _id: ast::NodeId) { + // Do not visit closures or fn items here, the outer loop in + // borrowck/mod will visit them for us in turn. + return; } fn gather_loans_in_block(this: &mut GatherLoanCtxt, @@ -232,8 +226,9 @@ fn gather_loans_in_expr(this: &mut GatherLoanCtxt, this.guarantee_valid(ex.id, ex.span, base_cmt, - LoanMutability::from_ast_mutability(mutbl), - scope_r); + mutbl, + scope_r, + AddrOf); } visit::walk_expr(this, ex, ()); } @@ -278,8 +273,9 @@ fn gather_loans_in_expr(this: &mut GatherLoanCtxt, this.guarantee_valid(arg.id, arg.span, arg_cmt, - ImmutableMutability, - scope_r); + ast::MutImmutable, + scope_r, + AutoRef); visit::walk_expr(this, ex, ()); } @@ -305,6 +301,7 @@ fn gather_loans_in_expr(this: &mut GatherLoanCtxt, ast::ExprFnBlock(..) | ast::ExprProc(..) => { gather_moves::gather_captures(this.bccx, &this.move_data, ex); + this.guarantee_captures(ex); visit::walk_expr(this, ex, ()); } @@ -367,49 +364,48 @@ impl<'a> GatherLoanCtxt<'a> { ty::AutoDerefRef { autoref: Some(ref autoref), autoderefs: autoderefs}) => { - let mcx = &mc::mem_categorization_ctxt { - tcx: self.tcx(), - method_map: self.bccx.method_map}; - let cmt = mcx.cat_expr_autoderefd(expr, autoderefs); + let mut mc = self.bccx.mc(); + let cmt = match mc.cat_expr_autoderefd(expr, autoderefs) { + Ok(v) => v, + Err(()) => self.tcx().sess.span_bug(expr.span, "Err from mc") + }; debug!("after autoderef, cmt={}", cmt.repr(self.tcx())); match *autoref { ty::AutoPtr(r, m) => { - let loan_mutability = - LoanMutability::from_ast_mutability(m); self.guarantee_valid(expr.id, expr.span, cmt, - loan_mutability, - r) + m, + r, + AutoRef) } ty::AutoBorrowVec(r, m) | ty::AutoBorrowVecRef(r, m) => { - let cmt_index = mcx.cat_index(expr, cmt, autoderefs+1); - let loan_mutability = - LoanMutability::from_ast_mutability(m); + let cmt_index = mc.cat_index(expr, cmt, autoderefs+1); self.guarantee_valid(expr.id, expr.span, cmt_index, - loan_mutability, - r) + m, + r, + AutoRef) } ty::AutoBorrowFn(r) => { - let cmt_deref = mcx.cat_deref_fn_or_obj(expr, cmt, 0); + let cmt_deref = mc.cat_deref_fn_or_obj(expr, cmt, 0); self.guarantee_valid(expr.id, expr.span, cmt_deref, - ImmutableMutability, - r) + ast::MutImmutable, + r, + AutoRef) } ty::AutoBorrowObj(r, m) => { - let cmt_deref = mcx.cat_deref_fn_or_obj(expr, cmt, 0); - let loan_mutability = - LoanMutability::from_ast_mutability(m); + let cmt_deref = mc.cat_deref_fn_or_obj(expr, cmt, 0); self.guarantee_valid(expr.id, expr.span, cmt_deref, - loan_mutability, - r) + m, + r, + AutoRef) } ty::AutoUnsafe(_) => {} } @@ -421,22 +417,71 @@ impl<'a> GatherLoanCtxt<'a> { } } - // Guarantees that addr_of(cmt) will be valid for the duration of - // `static_scope_r`, or reports an error. This may entail taking - // out loans, which will be added to the `req_loan_map`. This can - // also entail "rooting" GC'd pointers, which means ensuring - // dynamically that they are not freed. + fn guarantee_captures(&mut self, + closure_expr: &ast::Expr) { + let capture_map = self.bccx.capture_map.borrow(); + let captured_vars = capture_map.get().get(&closure_expr.id); + for captured_var in captured_vars.borrow().iter() { + match captured_var.mode { + moves::CapCopy | moves::CapMove => { continue; } + moves::CapRef => { } + } + + let var_id = ast_util::def_id_of_def(captured_var.def).node; + let var_cmt = self.bccx.cat_captured_var(closure_expr.id, + closure_expr.span, + captured_var); + + // Lookup the kind of borrow the callee requires + let upvar_id = ty::UpvarId { var_id: var_id, + closure_expr_id: closure_expr.id }; + let upvar_borrow_map = self.tcx().upvar_borrow_map.borrow(); + let upvar_borrow = upvar_borrow_map.get().get_copy(&upvar_id); + + self.guarantee_valid_kind(closure_expr.id, + closure_expr.span, + var_cmt, + upvar_borrow.kind, + upvar_borrow.region, + ClosureCapture(captured_var.span)); + } + } + pub fn guarantee_valid(&mut self, borrow_id: ast::NodeId, borrow_span: Span, cmt: mc::cmt, - req_mutbl: LoanMutability, - loan_region: ty::Region) { + req_mutbl: ast::Mutability, + loan_region: ty::Region, + cause: LoanCause) { + self.guarantee_valid_kind(borrow_id, + borrow_span, + cmt, + ty::BorrowKind::from_mutbl(req_mutbl), + loan_region, + cause); + } + + fn guarantee_valid_kind(&mut self, + borrow_id: ast::NodeId, + borrow_span: Span, + cmt: mc::cmt, + req_kind: ty::BorrowKind, + loan_region: ty::Region, + cause: LoanCause) { + /*! + * Guarantees that `addr_of(cmt)` will be valid for the duration of + * `static_scope_r`, or reports an error. This may entail taking + * out loans, which will be added to the `req_loan_map`. This can + * also entail "rooting" GC'd pointers, which means ensuring + * dynamically that they are not freed. + */ + debug!("guarantee_valid(borrow_id={:?}, cmt={}, \ req_mutbl={:?}, loan_region={:?})", borrow_id, cmt.repr(self.tcx()), - req_mutbl, + req_kind, loan_region); // a loan for the empty region can never be dereferenced, so @@ -450,26 +495,28 @@ impl<'a> GatherLoanCtxt<'a> { // Check that the lifetime of the borrow does not exceed // the lifetime of the data being borrowed. if lifetime::guarantee_lifetime(self.bccx, self.item_ub, root_ub, - borrow_span, cmt, loan_region, - req_mutbl).is_err() { + borrow_span, cause, cmt, loan_region, + req_kind).is_err() { return; // reported an error, no sense in reporting more. } // Check that we don't allow mutable borrows of non-mutable data. - if check_mutability(self.bccx, borrow_span, cmt, req_mutbl).is_err() { + if check_mutability(self.bccx, borrow_span, cause, + cmt, req_kind).is_err() { return; // reported an error, no sense in reporting more. } // Check that we don't allow mutable borrows of aliasable data. - if check_aliasability(self.bccx, borrow_span, cmt, req_mutbl).is_err() { + if check_aliasability(self.bccx, borrow_span, cause, + cmt, req_kind).is_err() { return; // reported an error, no sense in reporting more. } // Compute the restrictions that are required to enforce the // loan is safe. let restr = restrictions::compute_restrictions( - self.bccx, borrow_span, - cmt, loan_region, self.restriction_set(req_mutbl)); + self.bccx, borrow_span, cause, + cmt, loan_region, self.restriction_set(req_kind)); // Create the loan record (if needed). let loan = match restr { @@ -512,7 +559,7 @@ impl<'a> GatherLoanCtxt<'a> { let kill_scope = self.compute_kill_scope(loan_scope, loan_path); debug!("kill_scope = {:?}", kill_scope); - if req_mutbl == MutableMutability { + if req_kind == ty::MutBorrow { self.mark_loan_path_as_mutated(loan_path); } @@ -521,11 +568,12 @@ impl<'a> GatherLoanCtxt<'a> { index: all_loans.get().len(), loan_path: loan_path, cmt: cmt, - mutbl: req_mutbl, + kind: req_kind, gen_scope: gen_scope, kill_scope: kill_scope, span: borrow_span, - restrictions: restrictions + restrictions: restrictions, + cause: cause, } } }; @@ -568,23 +616,33 @@ impl<'a> GatherLoanCtxt<'a> { fn check_mutability(bccx: &BorrowckCtxt, borrow_span: Span, + cause: LoanCause, cmt: mc::cmt, - req_mutbl: LoanMutability) -> Result<(),()> { + req_kind: ty::BorrowKind) + -> Result<(),()> { //! Implements the M-* rules in doc.rs. - match req_mutbl { - ImmutableMutability => { - // both imm and mut data can be lent as imm; - // for mutable data, this is a freeze - Ok(()) + match req_kind { + ty::UniqueImmBorrow | ty::ImmBorrow => { + match cmt.mutbl { + // I am intentionally leaving this here to help + // refactoring if, in the future, we should add new + // kinds of mutability. + mc::McImmutable | mc::McDeclared | mc::McInherited => { + // both imm and mut data can be lent as imm; + // for mutable data, this is a freeze + Ok(()) + } + } } - MutableMutability => { + ty::MutBorrow => { // Only mutable data can be lent as mutable. if !cmt.mutbl.is_mutable() { - Err(bccx.report(BckError {span: borrow_span, - cmt: cmt, - code: err_mutbl(req_mutbl)})) + Err(bccx.report(BckError { span: borrow_span, + cause: cause, + cmt: cmt, + code: err_mutbl })) } else { Ok(()) } @@ -594,18 +652,18 @@ impl<'a> GatherLoanCtxt<'a> { fn check_aliasability(bccx: &BorrowckCtxt, borrow_span: Span, + loan_cause: LoanCause, cmt: mc::cmt, - req_mutbl: LoanMutability) -> Result<(),()> { + req_kind: ty::BorrowKind) + -> Result<(),()> { //! Implements the A-* rules in doc.rs. - match req_mutbl { - ImmutableMutability => { - // both imm and mut data can be lent as imm; - // for mutable data, this is a freeze + match req_kind { + ty::ImmBorrow => { Ok(()) } - MutableMutability => { + ty::UniqueImmBorrow | ty::MutBorrow => { // Check for those cases where we cannot control // the aliasing and make sure that we are not // being asked to. @@ -620,11 +678,11 @@ impl<'a> GatherLoanCtxt<'a> { // unsafe. At your own peril and all that. Ok(()) } - Some(cause) => { + Some(alias_cause) => { bccx.report_aliasability_violation( borrow_span, - BorrowViolation, - cause); + BorrowViolation(loan_cause), + alias_cause); Err(()) } } @@ -633,11 +691,18 @@ impl<'a> GatherLoanCtxt<'a> { } } - pub fn restriction_set(&self, req_mutbl: LoanMutability) - -> RestrictionSet { - match req_mutbl { - ImmutableMutability => RESTR_MUTATE | RESTR_CLAIM, - MutableMutability => RESTR_MUTATE | RESTR_CLAIM | RESTR_FREEZE, + fn restriction_set(&self, req_kind: ty::BorrowKind) -> RestrictionSet { + match req_kind { + // If borrowing data as immutable, no mutation allowed: + ty::ImmBorrow => RESTR_MUTATE, + + // If borrowing data as mutable, no mutation nor other + // borrows allowed: + ty::MutBorrow => RESTR_MUTATE | RESTR_FREEZE, + + // If borrowing data as unique imm, no mutation nor other + // borrows allowed: + ty::UniqueImmBorrow => RESTR_MUTATE | RESTR_FREEZE, } } @@ -719,11 +784,11 @@ impl<'a> GatherLoanCtxt<'a> { * `gather_pat()`. */ - let mc_ctxt = self.bccx.mc_ctxt(); + let mut mc = self.bccx.mc(); for arg in decl.inputs.iter() { let arg_ty = ty::node_id_to_type(self.tcx(), arg.pat.id); - let arg_cmt = mc_ctxt.cat_rvalue( + let arg_cmt = mc.cat_rvalue( arg.id, arg.pat.span, ty::ReScope(body.id), // Args live only as long as the fn body. @@ -735,7 +800,7 @@ impl<'a> GatherLoanCtxt<'a> { fn gather_pat(&mut self, discr_cmt: mc::cmt, - root_pat: &ast::Pat, + root_pat: @ast::Pat, arm_match_ids: Option<(ast::NodeId, ast::NodeId)>) { /*! * Walks patterns, examining the bindings to determine if they @@ -774,13 +839,12 @@ impl<'a> GatherLoanCtxt<'a> { } } }; - let loan_mutability = - LoanMutability::from_ast_mutability(mutbl); self.guarantee_valid(pat.id, pat.span, cmt_discr, - loan_mutability, - scope_r); + mutbl, + scope_r, + RefBinding); } ast::BindByValue(_) => { // No borrows here, but there may be moves @@ -797,14 +861,15 @@ impl<'a> GatherLoanCtxt<'a> { // original vector. This is effectively a borrow of // the elements of the vector being matched. - let slice_ty = ty::node_id_to_type(self.tcx(), - slice_pat.id); - let (slice_mutbl, slice_r) = - self.vec_slice_info(slice_pat, slice_ty); - let mcx = self.bccx.mc_ctxt(); - let cmt_index = mcx.cat_index(slice_pat, cmt, 0); - let slice_loan_mutability = - LoanMutability::from_ast_mutability(slice_mutbl); + let (slice_cmt, slice_borrow_kind, slice_r) = { + match self.bccx.mc().cat_slice_pattern(cmt, slice_pat) { + Ok(v) => v, + Err(()) => { + self.tcx().sess.span_bug(slice_pat.span, + "Err from mc") + } + } + }; // Note: We declare here that the borrow occurs upon // entering the `[...]` pattern. This implies that @@ -823,11 +888,9 @@ impl<'a> GatherLoanCtxt<'a> { // 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_loan_mutability, - slice_r); + self.guarantee_valid(pat.id, pat.span, + slice_cmt, slice_borrow_kind, slice_r, + RefBinding); } _ => {} @@ -835,33 +898,6 @@ impl<'a> GatherLoanCtxt<'a> { }) } - pub fn vec_slice_info(&self, pat: &ast::Pat, slice_ty: ty::t) - -> (ast::Mutability, ty::Region) { - /*! - * - * In a pattern like [a, b, ..c], normally `c` has slice type, - * but if you have [a, b, ..ref c], then the type of `ref c` - * will be `&&[]`, so to extract the slice details we have - * to recurse through rptrs. - */ - - match ty::get(slice_ty).sty { - ty::ty_vec(slice_mt, ty::vstore_slice(slice_r)) => { - (slice_mt.mutbl, slice_r) - } - - ty::ty_rptr(_, ref mt) => { - self.vec_slice_info(pat, mt.ty) - } - - _ => { - self.tcx().sess.span_bug( - pat.span, - format!("type of slice pattern is not a slice")); - } - } - } - pub fn pat_is_binding(&self, pat: &ast::Pat) -> bool { pat_util::pat_is_binding(self.bccx.tcx.def_map, pat) } diff --git a/src/librustc/middle/borrowck/gather_loans/restrictions.rs b/src/librustc/middle/borrowck/gather_loans/restrictions.rs index 60e0baa3534ba..575119ba6904b 100644 --- a/src/librustc/middle/borrowck/gather_loans/restrictions.rs +++ b/src/librustc/middle/borrowck/gather_loans/restrictions.rs @@ -16,8 +16,8 @@ use std::vec; use middle::borrowck::*; use mc = middle::mem_categorization; use middle::ty; -use syntax::ast::{MutImmutable, MutMutable}; use syntax::codemap::Span; +use util::ppaux::Repr; pub enum RestrictionResult { Safe, @@ -26,12 +26,14 @@ pub enum RestrictionResult { pub fn compute_restrictions(bccx: &BorrowckCtxt, span: Span, + cause: LoanCause, cmt: mc::cmt, loan_region: ty::Region, restr: RestrictionSet) -> RestrictionResult { let ctxt = RestrictionsContext { bccx: bccx, span: span, + cause: cause, cmt_original: cmt, loan_region: loan_region, }; @@ -47,12 +49,17 @@ struct RestrictionsContext<'a> { span: Span, cmt_original: mc::cmt, loan_region: ty::Region, + cause: LoanCause, } impl<'a> RestrictionsContext<'a> { fn restrict(&self, cmt: mc::cmt, restrictions: RestrictionSet) -> RestrictionResult { + debug!("restrict(cmt={}, restrictions={})", + cmt.repr(self.bccx.tcx), + restrictions.repr(self.bccx.tcx)); + match cmt.cat { mc::cat_rvalue(..) => { // Effectively, rvalues are stored into a @@ -64,7 +71,8 @@ impl<'a> RestrictionsContext<'a> { } mc::cat_local(local_id) | - mc::cat_arg(local_id) => { + mc::cat_arg(local_id) | + mc::cat_upvar(ty::UpvarId {var_id: local_id, ..}, _) => { // R-Variable let lp = @LpVar(local_id); SafeIf(lp, ~[Restriction {loan_path: lp, @@ -77,7 +85,7 @@ impl<'a> RestrictionsContext<'a> { // could cause the type of the memory to change. self.restrict( cmt_base, - restrictions | RESTR_MUTATE | RESTR_CLAIM) + restrictions | RESTR_MUTATE) } mc::cat_interior(cmt_base, i) => { @@ -90,7 +98,7 @@ impl<'a> RestrictionsContext<'a> { self.extend(result, cmt.mutbl, LpInterior(i), restrictions) } - mc::cat_deref(cmt_base, _, pk @ mc::uniq_ptr) => { + mc::cat_deref(cmt_base, _, pk @ mc::OwnedPtr) => { // R-Deref-Send-Pointer // // When we borrow the interior of an owned pointer, we @@ -98,7 +106,7 @@ impl<'a> RestrictionsContext<'a> { // would cause the unique pointer to be freed. let result = self.restrict( cmt_base, - restrictions | RESTR_MUTATE | RESTR_CLAIM); + restrictions | RESTR_MUTATE); self.extend(result, cmt.mutbl, LpDeref(pk), restrictions) } @@ -107,12 +115,14 @@ impl<'a> RestrictionsContext<'a> { Safe } - mc::cat_deref(cmt_base, _, mc::region_ptr(MutImmutable, lt)) => { + mc::cat_deref(cmt_base, _, mc::BorrowedPtr(ty::ImmBorrow, lt)) | + mc::cat_deref(cmt_base, _, mc::BorrowedPtr(ty::UniqueImmBorrow, lt)) => { // R-Deref-Imm-Borrowed if !self.bccx.is_subregion_of(self.loan_region, lt) { self.bccx.report( BckError { span: self.span, + cause: self.cause, cmt: cmt_base, code: err_borrowed_pointer_too_short( self.loan_region, lt, restrictions)}); @@ -121,17 +131,18 @@ impl<'a> RestrictionsContext<'a> { Safe } - mc::cat_deref(_, _, mc::gc_ptr) => { + mc::cat_deref(_, _, mc::GcPtr) => { // R-Deref-Imm-Managed Safe } - mc::cat_deref(cmt_base, _, pk @ mc::region_ptr(MutMutable, lt)) => { + mc::cat_deref(cmt_base, _, pk @ mc::BorrowedPtr(ty::MutBorrow, lt)) => { // R-Deref-Mut-Borrowed if !self.bccx.is_subregion_of(self.loan_region, lt) { self.bccx.report( BckError { span: self.span, + cause: self.cause, cmt: cmt_base, code: err_borrowed_pointer_too_short( self.loan_region, lt, restrictions)}); @@ -142,12 +153,11 @@ impl<'a> RestrictionsContext<'a> { self.extend(result, cmt.mutbl, LpDeref(pk), restrictions) } - mc::cat_deref(_, _, mc::unsafe_ptr(..)) => { + mc::cat_deref(_, _, mc::UnsafePtr(..)) => { // We are very trusting when working with unsafe pointers. Safe } - mc::cat_stack_upvar(cmt_base) | mc::cat_discr(cmt_base, _) => { self.restrict(cmt_base, restrictions) } diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index 6a3e2fc63b08f..acc8ece85f8d8 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -120,41 +120,32 @@ fn borrowck_fn(this: &mut BorrowckCtxt, body: &ast::Block, sp: Span, id: ast::NodeId) { - match fk { - &visit::FkFnBlock(..) => { - // Closures are checked as part of their containing fn item. - } - - &visit::FkItemFn(..) | &visit::FkMethod(..) => { - debug!("borrowck_fn(id={:?})", id); - - // Check the body of fn items. - let (id_range, all_loans, move_data) = - gather_loans::gather_loans(this, decl, body); - - let all_loans = all_loans.borrow(); - let mut loan_dfcx = DataFlowContext::new(this.tcx, - this.method_map, - LoanDataFlowOperator, - id_range, - all_loans.get().len()); - for (loan_idx, loan) in all_loans.get().iter().enumerate() { - loan_dfcx.add_gen(loan.gen_scope, loan_idx); - loan_dfcx.add_kill(loan.kill_scope, loan_idx); - } - - loan_dfcx.propagate(body); - - let flowed_moves = move_data::FlowedMoveData::new(move_data, - this.tcx, - this.method_map, - id_range, - body); - - check_loans::check_loans(this, &loan_dfcx, flowed_moves, - *all_loans.get(), body); - } - } + debug!("borrowck_fn(id={})", id); + + // Check the body of fn items. + let (id_range, all_loans, move_data) = + gather_loans::gather_loans(this, decl, body); + let all_loans = all_loans.borrow(); + let mut loan_dfcx = + DataFlowContext::new(this.tcx, + this.method_map, + LoanDataFlowOperator, + id_range, + all_loans.get().len()); + for (loan_idx, loan) in all_loans.get().iter().enumerate() { + loan_dfcx.add_gen(loan.gen_scope, loan_idx); + loan_dfcx.add_kill(loan.kill_scope, loan_idx); + } + loan_dfcx.propagate(body); + + let flowed_moves = move_data::FlowedMoveData::new(move_data, + this.tcx, + this.method_map, + id_range, + body); + + check_loans::check_loans(this, &loan_dfcx, flowed_moves, + *all_loans.get(), body); visit::walk_fn(this, fk, decl, body, sp, id, ()); } @@ -211,41 +202,25 @@ pub enum PartialTotal { /////////////////////////////////////////////////////////////////////////// // Loans and loan paths -#[deriving(Clone, Eq)] -pub enum LoanMutability { - ImmutableMutability, - MutableMutability, -} - -impl LoanMutability { - pub fn from_ast_mutability(ast_mutability: ast::Mutability) - -> LoanMutability { - match ast_mutability { - ast::MutImmutable => ImmutableMutability, - ast::MutMutable => MutableMutability, - } - } -} - -impl ToStr for LoanMutability { - fn to_str(&self) -> ~str { - match *self { - ImmutableMutability => ~"immutable", - MutableMutability => ~"mutable", - } - } -} - /// Record of a loan that was issued. pub struct Loan { index: uint, loan_path: @LoanPath, cmt: mc::cmt, - mutbl: LoanMutability, + kind: ty::BorrowKind, restrictions: ~[Restriction], gen_scope: ast::NodeId, kill_scope: ast::NodeId, span: Span, + cause: LoanCause, +} + +#[deriving(Eq)] +pub enum LoanCause { + ClosureCapture(Span), + AddrOf, + AutoRef, + RefBinding, } #[deriving(Eq, IterBytes)] @@ -283,7 +258,9 @@ pub fn opt_loan_path(cmt: mc::cmt) -> Option<@LoanPath> { None } - mc::cat_local(id) | mc::cat_arg(id) => { + mc::cat_local(id) | + mc::cat_arg(id) | + mc::cat_upvar(ty::UpvarId {var_id: id, ..}, _) => { Some(@LpVar(id)) } @@ -300,7 +277,6 @@ pub fn opt_loan_path(cmt: mc::cmt) -> Option<@LoanPath> { } mc::cat_downcast(cmt_base) | - mc::cat_stack_upvar(cmt_base) | mc::cat_discr(cmt_base, _) => { opt_loan_path(cmt_base) } @@ -313,8 +289,7 @@ pub fn opt_loan_path(cmt: mc::cmt) -> Option<@LoanPath> { // Borrowing an lvalue often results in *restrictions* that limit what // can be done with this lvalue during the scope of the loan: // -// - `RESTR_MUTATE`: The lvalue may not be modified. -// - `RESTR_CLAIM`: `&mut` borrows of the lvalue are forbidden. +// - `RESTR_MUTATE`: The lvalue may not be modified or `&mut` borrowed. // - `RESTR_FREEZE`: `&` borrows of the lvalue are forbidden. // // In addition, no value which is restricted may be moved. Therefore, @@ -333,8 +308,7 @@ pub struct RestrictionSet { pub static RESTR_EMPTY: RestrictionSet = RestrictionSet {bits: 0b0000}; pub static RESTR_MUTATE: RestrictionSet = RestrictionSet {bits: 0b0001}; -pub static RESTR_CLAIM: RestrictionSet = RestrictionSet {bits: 0b0010}; -pub static RESTR_FREEZE: RestrictionSet = RestrictionSet {bits: 0b0100}; +pub static RESTR_FREEZE: RestrictionSet = RestrictionSet {bits: 0b0010}; impl RestrictionSet { pub fn intersects(&self, restr: RestrictionSet) -> bool { @@ -358,6 +332,12 @@ impl BitAnd for RestrictionSet { } } +impl Repr for RestrictionSet { + fn repr(&self, _tcx: ty::ctxt) -> ~str { + format!("RestrictionSet(0x{:x})", self.bits as uint) + } +} + /////////////////////////////////////////////////////////////////////////// // Rooting of managed boxes // @@ -393,10 +373,9 @@ pub fn root_map() -> root_map { // Errors that can occur #[deriving(Eq)] pub enum bckerr_code { - err_mutbl(LoanMutability), + err_mutbl, err_out_of_root_scope(ty::Region, ty::Region), // superscope, subscope err_out_of_scope(ty::Region, ty::Region), // superscope, subscope - err_freeze_aliasable_const, err_borrowed_pointer_too_short( ty::Region, ty::Region, RestrictionSet), // loan, ptr } @@ -406,13 +385,14 @@ pub enum bckerr_code { #[deriving(Eq)] pub struct BckError { span: Span, + cause: LoanCause, cmt: mc::cmt, code: bckerr_code } pub enum AliasableViolationKind { MutabilityViolation, - BorrowViolation + BorrowViolation(LoanCause) } pub enum MovedValueUseKind { @@ -439,29 +419,55 @@ impl BorrowckCtxt { moves_map.get().contains(&id) } + pub fn mc(&self) -> mc::MemCategorizationContext { + mc::MemCategorizationContext { + typer: TcxTyper { + tcx: self.tcx, + method_map: self.method_map + } + } + } + pub fn cat_expr(&self, expr: &ast::Expr) -> mc::cmt { - mc::cat_expr(self.tcx, self.method_map, expr) + match self.mc().cat_expr(expr) { + Ok(c) => c, + Err(()) => { + self.tcx.sess.span_bug(expr.span, "error in mem categorization"); + } + } } pub fn cat_expr_unadjusted(&self, expr: &ast::Expr) -> mc::cmt { - mc::cat_expr_unadjusted(self.tcx, self.method_map, expr) + match self.mc().cat_expr_unadjusted(expr) { + Ok(c) => c, + Err(()) => { + self.tcx.sess.span_bug(expr.span, "error in mem categorization"); + } + } } pub fn cat_expr_autoderefd(&self, expr: &ast::Expr, adj: &ty::AutoAdjustment) -> mc::cmt { - match *adj { + let r = match *adj { ty::AutoAddEnv(..) | ty::AutoObject(..) => { // no autoderefs - mc::cat_expr_unadjusted(self.tcx, self.method_map, expr) + self.mc().cat_expr_unadjusted(expr) } ty::AutoDerefRef( ty::AutoDerefRef { autoderefs: autoderefs, ..}) => { - mc::cat_expr_autoderefd(self.tcx, self.method_map, expr, - autoderefs) + self.mc().cat_expr_autoderefd(expr, autoderefs) + } + }; + + match r { + Ok(c) => c, + Err(()) => { + self.tcx.sess.span_bug(expr.span, + "error in mem categorization"); } } } @@ -472,7 +478,23 @@ impl BorrowckCtxt { ty: ty::t, def: ast::Def) -> mc::cmt { - mc::cat_def(self.tcx, self.method_map, id, span, ty, def) + match self.mc().cat_def(id, span, ty, def) { + Ok(c) => c, + Err(()) => { + self.tcx.sess.span_bug(span, "error in mem categorization"); + } + } + } + + pub fn cat_captured_var(&self, + id: ast::NodeId, + span: Span, + captured_var: &moves::CaptureVar) -> mc::cmt { + // Create the cmt for the variable being borrowed, from the + // caller's perspective + let var_id = ast_util::def_id_of_def(captured_var.def).node; + let var_ty = ty::node_id_to_type(self.tcx, var_id); + self.cat_def(id, span, var_ty, captured_var.def) } pub fn cat_discr(&self, cmt: mc::cmt, match_id: ast::NodeId) -> mc::cmt { @@ -481,17 +503,12 @@ impl BorrowckCtxt { ..*cmt} } - pub fn mc_ctxt(&self) -> mc::mem_categorization_ctxt { - mc::mem_categorization_ctxt {tcx: self.tcx, - method_map: self.method_map} - } - pub fn cat_pattern(&self, cmt: mc::cmt, - pat: &ast::Pat, + pat: @ast::Pat, op: |mc::cmt, &ast::Pat|) { - let mc = self.mc_ctxt(); - mc.cat_pattern(cmt, pat, op); + let r = self.mc().cat_pattern(cmt, pat, |_,x,y| op(x,y)); + assert!(r.is_ok()); } pub fn report(&self, err: BckError) { @@ -622,24 +639,35 @@ impl BorrowckCtxt { pub fn bckerr_to_str(&self, err: BckError) -> ~str { match err.code { - err_mutbl(lk) => { - format!("cannot borrow {} {} as {}", - err.cmt.mutbl.to_user_str(), - self.cmt_to_str(err.cmt), - self.mut_to_str(lk)) + err_mutbl => { + let descr = match opt_loan_path(err.cmt) { + None => format!("{} {}", + err.cmt.mutbl.to_user_str(), + self.cmt_to_str(err.cmt)), + Some(lp) => format!("{} {} `{}`", + err.cmt.mutbl.to_user_str(), + self.cmt_to_str(err.cmt), + self.loan_path_to_str(lp)), + }; + + match err.cause { + ClosureCapture(_) => { + format!("closure cannot assign to {}", descr) + } + AddrOf | RefBinding | AutoRef => { + format!("cannot borrow {} as mutable", descr) + } + } } err_out_of_root_scope(..) => { format!("cannot root managed value long enough") } err_out_of_scope(..) => { - format!("borrowed value does not live long enough") - } - err_freeze_aliasable_const => { - // Means that the user borrowed a ~T or enum value - // residing in &const or @const pointer. Terrible - // error message, but then &const and @const are - // supposed to be going away. - format!("unsafe borrow of aliasable, const value") + let msg = match opt_loan_path(err.cmt) { + None => format!("borrowed value"), + Some(lp) => format!("`{}`", self.loan_path_to_str(lp)), + }; + format!("{} does not live long enough", msg) } err_borrowed_pointer_too_short(..) => { let descr = match opt_loan_path(err.cmt) { @@ -659,8 +687,24 @@ impl BorrowckCtxt { kind: AliasableViolationKind, cause: mc::AliasableReason) { let prefix = match kind { - MutabilityViolation => "cannot assign to data", - BorrowViolation => "cannot borrow data mutably" + MutabilityViolation => { + "cannot assign to data" + } + BorrowViolation(ClosureCapture(_)) => { + // I don't think we can get aliasability violations + // with closure captures, so no need to come up with a + // good error message. The reason this cannot happen + // is because we only capture local variables in + // closures, and those are never aliasable. + self.tcx.sess.span_bug( + span, + "aliasability violation with closure"); + } + BorrowViolation(AddrOf) | + BorrowViolation(AutoRef) | + BorrowViolation(RefBinding) => { + "cannot borrow data mutably" + } }; match cause { @@ -680,7 +724,7 @@ impl BorrowckCtxt { span, format!("{} in a `@` pointer", prefix)); } - mc::AliasableBorrowed(_) => { + mc::AliasableBorrowed => { self.tcx.sess.span_err( span, format!("{} in a `&` reference", prefix)); @@ -691,7 +735,7 @@ impl BorrowckCtxt { pub fn note_and_explain_bckerr(&self, err: BckError) { let code = err.code; match code { - err_mutbl(..) | err_freeze_aliasable_const(..) => {} + err_mutbl(..) => { } err_out_of_root_scope(super_scope, sub_scope) => { note_and_explain_region( @@ -738,52 +782,16 @@ impl BorrowckCtxt { } } - pub fn append_loan_path_to_str_from_interior(&self, - loan_path: &LoanPath, - out: &mut ~str) { - match *loan_path { - LpExtend(_, _, LpDeref(_)) => { - out.push_char('('); - self.append_loan_path_to_str(loan_path, out); - out.push_char(')'); - } - LpExtend(_, _, LpInterior(_)) | - LpVar(_) => { - self.append_loan_path_to_str(loan_path, out); - } - } - } - pub fn append_loan_path_to_str(&self, loan_path: &LoanPath, out: &mut ~str) { match *loan_path { LpVar(id) => { - match self.tcx.items.find(id) { - Some(ast_map::NodeLocal(pat)) => { - match pat.node { - ast::PatIdent(_, ref path, _) => { - let ident = ast_util::path_to_ident(path); - let string = token::get_ident(ident.name); - out.push_str(string.get()); - } - _ => { - self.tcx.sess.bug( - format!("loan path LpVar({:?}) maps to {:?}, not local", - id, pat)); - } - } - } - r => { - self.tcx.sess.bug( - format!("loan path LpVar({:?}) maps to {:?}, not local", - id, r)); - } - } + out.push_str(ty::local_var_name_str(self.tcx, id).get()); } LpExtend(lp_base, _, LpInterior(mc::InteriorField(fname))) => { - self.append_loan_path_to_str_from_interior(lp_base, out); + self.append_autoderefd_loan_path_to_str(lp_base, out); match fname { mc::NamedField(ref fname) => { let string = token::get_ident(*fname); @@ -798,8 +806,8 @@ impl BorrowckCtxt { } LpExtend(lp_base, _, LpInterior(mc::InteriorElement(_))) => { - self.append_loan_path_to_str_from_interior(lp_base, out); - out.push_str("[]"); + self.append_autoderefd_loan_path_to_str(lp_base, out); + out.push_str("[..]"); } LpExtend(lp_base, _, LpDeref(_)) => { @@ -809,6 +817,23 @@ impl BorrowckCtxt { } } + pub fn append_autoderefd_loan_path_to_str(&self, + loan_path: &LoanPath, + out: &mut ~str) { + match *loan_path { + LpExtend(lp_base, _, LpDeref(_)) => { + // For a path like `(*x).f` or `(*x)[3]`, autoderef + // rules would normally allow users to omit the `*x`. + // So just serialize such paths to `x.f` or x[3]` respectively. + self.append_autoderefd_loan_path_to_str(lp_base, out) + } + + LpVar(..) | LpExtend(_, _, LpInterior(..)) => { + self.append_loan_path_to_str(loan_path, out) + } + } + } + pub fn loan_path_to_str(&self, loan_path: &LoanPath) -> ~str { let mut result = ~""; self.append_loan_path_to_str(loan_path, &mut result); @@ -816,13 +841,11 @@ impl BorrowckCtxt { } pub fn cmt_to_str(&self, cmt: mc::cmt) -> ~str { - let mc = &mc::mem_categorization_ctxt {tcx: self.tcx, - method_map: self.method_map}; - mc.cmt_to_str(cmt) + self.mc().cmt_to_str(cmt) } - pub fn mut_to_str(&self, mutbl: LoanMutability) -> ~str { - mutbl.to_str() + pub fn mut_to_str(&self, mutbl: ast::Mutability) -> ~str { + self.mc().mut_to_str(mutbl) } pub fn mut_to_keyword(&self, mutbl: ast::Mutability) -> &'static str { @@ -843,11 +866,6 @@ impl DataFlowOperator for LoanDataFlowOperator { fn join(&self, succ: uint, pred: uint) -> uint { succ | pred // loans from both preds are in scope } - - #[inline] - fn walk_closures(&self) -> bool { - true - } } impl Repr for Loan { @@ -855,7 +873,7 @@ impl Repr for Loan { format!("Loan_{:?}({}, {:?}, {:?}-{:?}, {})", self.index, self.loan_path.repr(tcx), - self.mutbl, + self.kind, self.gen_scope, self.kill_scope, self.restrictions.repr(tcx)) @@ -890,3 +908,39 @@ impl Repr for LoanPath { } } } + +/////////////////////////////////////////////////////////////////////////// + +struct TcxTyper { + tcx: ty::ctxt, + method_map: typeck::method_map, +} + +impl mc::Typer for TcxTyper { + fn tcx(&self) -> ty::ctxt { + self.tcx + } + + fn node_ty(&mut self, id: ast::NodeId) -> mc::McResult { + Ok(ty::node_id_to_type(self.tcx, id)) + } + + fn adjustment(&mut self, id: ast::NodeId) -> Option<@ty::AutoAdjustment> { + let adjustments = self.tcx.adjustments.borrow(); + adjustments.get().find_copy(&id) + } + + fn is_method_call(&mut self, id: ast::NodeId) -> bool { + let method_map = self.method_map.borrow(); + method_map.get().contains_key(&id) + } + + fn temporary_scope(&mut self, id: ast::NodeId) -> Option { + self.tcx.region_maps.temporary_scope(id) + } + + fn upvar_borrow(&mut self, id: ty::UpvarId) -> ty::UpvarBorrow { + let upvar_borrow_map = self.tcx.upvar_borrow_map.borrow(); + upvar_borrow_map.get().get_copy(&id) + } +} diff --git a/src/librustc/middle/borrowck/move_data.rs b/src/librustc/middle/borrowck/move_data.rs index 75549c5944df7..34efcacc44b06 100644 --- a/src/librustc/middle/borrowck/move_data.rs +++ b/src/librustc/middle/borrowck/move_data.rs @@ -722,11 +722,6 @@ impl DataFlowOperator for MoveDataFlowOperator { fn join(&self, succ: uint, pred: uint) -> uint { succ | pred // moves from both preds are in scope } - - #[inline] - fn walk_closures(&self) -> bool { - true - } } impl DataFlowOperator for AssignDataFlowOperator { @@ -739,9 +734,4 @@ impl DataFlowOperator for AssignDataFlowOperator { fn join(&self, succ: uint, pred: uint) -> uint { succ | pred // moves from both preds are in scope } - - #[inline] - fn walk_closures(&self) -> bool { - true - } } diff --git a/src/librustc/middle/dataflow.rs b/src/librustc/middle/dataflow.rs index e4b648dd43c23..5af5aa63e1de6 100644 --- a/src/librustc/middle/dataflow.rs +++ b/src/librustc/middle/dataflow.rs @@ -17,7 +17,6 @@ */ -use std::cast; use std::io; use std::uint; use std::vec; @@ -72,9 +71,6 @@ pub trait DataFlowOperator { /// Joins two predecessor bits together, typically either `|` or `&` fn join(&self, succ: uint, pred: uint) -> uint; - - /// True if we should propagate through closures - fn walk_closures(&self) -> bool; } struct PropagationContext<'a, O> { @@ -373,8 +369,8 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { blk: &ast::Block, in_out: &mut [uint], loop_scopes: &mut ~[LoopScope]) { - debug!("DataFlowContext::walk_block(blk.id={:?}, in_out={})", - blk.id, bits_to_str(reslice(in_out))); + debug!("DataFlowContext::walk_block(blk.id={}, in_out={})", + blk.id, bits_to_str(in_out)); self.merge_with_entry_set(blk.id, in_out); @@ -425,99 +421,12 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { in_out: &mut [uint], loop_scopes: &mut ~[LoopScope]) { debug!("DataFlowContext::walk_expr(expr={}, in_out={})", - expr.repr(self.dfcx.tcx), bits_to_str(reslice(in_out))); + expr.repr(self.dfcx.tcx), bits_to_str(in_out)); self.merge_with_entry_set(expr.id, in_out); match expr.node { - ast::ExprFnBlock(ref decl, body) | - ast::ExprProc(ref decl, body) => { - if self.dfcx.oper.walk_closures() { - // In the absence of once fns, we must assume that - // every function body will execute more than - // once. Thus we treat every function body like a - // loop. - // - // What is subtle and a bit tricky, also, is how - // to deal with the "output" bits---that is, what - // do we consider to be the successor of a - // function body, given that it could be called - // from any point within its lifetime? What we do - // is to add their effects immediately as of the - // point of creation. Of course we have to ensure - // that this is sound for the analyses which make - // use of dataflow. - // - // In the case of the initedness checker (which - // does not currently use dataflow, but I hope to - // convert at some point), we will simply not walk - // closures at all, so it's a moot point. - // - // In the case of the borrow checker, this means - // the loans which would be created by calling a - // function come into effect immediately when the - // function is created. This is guaranteed to be - // earlier than the point at which the loan - // actually comes into scope (which is the point - // at which the closure is *called*). Because - // loans persist until the scope of the loans is - // exited, it is always a safe approximation to - // have a loan begin earlier than it actually will - // at runtime, so this should be sound. - // - // We stil have to be careful in the region - // checker and borrow checker to treat function - // bodies like loops, which implies some - // limitations. For example, a closure cannot root - // a managed box for longer than its body. - // - // General control flow looks like this: - // - // +- (expr) <----------+ - // | | | - // | v | - // | (body) -----------+--> (exit) - // | | | - // | + (break/loop) -+ - // | | - // +--------------------+ - // - // This is a bit more conservative than a loop. - // Note that we must assume that even after a - // `break` occurs (e.g., in a `for` loop) that the - // closure may be reinvoked. - // - // One difference from other loops is that `loop` - // and `break` statements which target a closure - // both simply add to the `break_bits`. - - // func_bits represents the state when the function - // returns - let mut func_bits = reslice(in_out).to_owned(); - - loop_scopes.push(LoopScope { - loop_id: expr.id, - break_bits: reslice(in_out).to_owned() - }); - for input in decl.inputs.iter() { - self.walk_pat(input.pat, func_bits, loop_scopes); - } - self.walk_block(body, func_bits, loop_scopes); - - // add the bits from any early return via `break`, - // `continue`, or `return` into `func_bits` - let loop_scope = loop_scopes.pop().unwrap(); - join_bits(&self.dfcx.oper, loop_scope.break_bits, func_bits); - - // add `func_bits` to the entry bits for `expr`, - // since we must assume the function may be called - // more than once - self.add_to_entry_set(expr.id, reslice(func_bits)); - - // the final exit bits include whatever was present - // in the original, joined with the bits from the function - join_bits(&self.dfcx.oper, func_bits, in_out); - } + ast::ExprFnBlock(..) | ast::ExprProc(..) => { } ast::ExprIf(cond, then, els) => { @@ -536,7 +445,7 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { // self.walk_expr(cond, in_out, loop_scopes); - let mut then_bits = reslice(in_out).to_owned(); + let mut then_bits = in_out.to_owned(); self.walk_block(then, then_bits, loop_scopes); self.walk_opt_expr(els, in_out, loop_scopes); @@ -558,10 +467,10 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { self.walk_expr(cond, in_out, loop_scopes); - let mut body_bits = reslice(in_out).to_owned(); + let mut body_bits = in_out.to_owned(); loop_scopes.push(LoopScope { loop_id: expr.id, - break_bits: reslice(in_out).to_owned() + break_bits: in_out.to_owned() }); self.walk_block(blk, body_bits, loop_scopes); self.add_to_entry_set(expr.id, body_bits); @@ -581,11 +490,11 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { // <--+ (break) // - let mut body_bits = reslice(in_out).to_owned(); + let mut body_bits = in_out.to_owned(); self.reset(in_out); loop_scopes.push(LoopScope { loop_id: expr.id, - break_bits: reslice(in_out).to_owned() + break_bits: in_out.to_owned() }); self.walk_block(blk, body_bits, loop_scopes); self.add_to_entry_set(expr.id, body_bits); @@ -609,7 +518,7 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { // self.walk_expr(discr, in_out, loop_scopes); - let mut guards = reslice(in_out).to_owned(); + let mut guards = in_out.to_owned(); // We know that exactly one arm will be taken, so we // can start out with a blank slate and just union @@ -622,7 +531,7 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { // determine the bits for the body and then union // them into `in_out`, which reflects all bodies to date - let mut body = reslice(guards).to_owned(); + let mut body = guards.to_owned(); self.walk_pat_alternatives(arm.pats, body, loop_scopes); self.walk_block(arm.body, body, loop_scopes); join_bits(&self.dfcx.oper, body, in_out); @@ -643,7 +552,7 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { ast::ExprAgain(label) => { let scope = self.find_scope(expr, label, loop_scopes); self.pop_scopes(expr, scope, in_out); - self.add_to_entry_set(scope.loop_id, reslice(in_out)); + self.add_to_entry_set(scope.loop_id, in_out); self.reset(in_out); } @@ -693,7 +602,7 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { ast::ExprBinary(_, op, l, r) if ast_util::lazy_binop(op) => { self.walk_expr(l, in_out, loop_scopes); - let temp = reslice(in_out).to_owned(); + let temp = in_out.to_owned(); self.walk_expr(r, in_out, loop_scopes); join_bits(&self.dfcx.oper, temp, in_out); } @@ -756,7 +665,7 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { debug!("pop_scopes(from_expr={}, to_scope={:?}, in_out={})", from_expr.repr(tcx), to_scope.loop_id, - bits_to_str(reslice(in_out))); + bits_to_str(in_out)); let mut id = from_expr.id; while id != to_scope.loop_id { @@ -781,11 +690,11 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { in_out: &mut [uint]) { self.pop_scopes(from_expr, to_scope, in_out); self.dfcx.apply_kill(from_expr.id, in_out); - join_bits(&self.dfcx.oper, reslice(in_out), to_scope.break_bits); - debug!("break_from_to(from_expr={}, to_scope={:?}) final break_bits={}", + join_bits(&self.dfcx.oper, in_out, to_scope.break_bits); + debug!("break_from_to(from_expr={}, to_scope={}) final break_bits={}", from_expr.repr(self.tcx()), to_scope.loop_id, - bits_to_str(reslice(in_out))); + bits_to_str(in_out)); } fn walk_exprs(&mut self, @@ -830,10 +739,10 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { in_out: &mut [uint], _loop_scopes: &mut ~[LoopScope]) { debug!("DataFlowContext::walk_pat(pat={}, in_out={})", - pat.repr(self.dfcx.tcx), bits_to_str(reslice(in_out))); + pat.repr(self.dfcx.tcx), bits_to_str(in_out)); ast_util::walk_pat(pat, |p| { - debug!(" p.id={:?} in_out={}", p.id, bits_to_str(reslice(in_out))); + debug!(" p.id={} in_out={}", p.id, bits_to_str(in_out)); self.merge_with_entry_set(p.id, in_out); self.dfcx.apply_gen_kill(p.id, in_out); true @@ -852,7 +761,7 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { // In the general case, the patterns in `pats` are // alternatives, so we must treat this like an N-way select // statement. - let initial_state = reslice(in_out).to_owned(); + let initial_state = in_out.to_owned(); for &pat in pats.iter() { let mut temp = initial_state.clone(); self.walk_pat(pat, temp, loop_scopes); @@ -929,8 +838,8 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { let (start, end) = self.dfcx.compute_id_range(id); let changed = { // FIXME(#5074) awkward construction let on_entry = self.dfcx.on_entry.mut_slice(start, end); - let changed = join_bits(&self.dfcx.oper, reslice(pred_bits), on_entry); - copy_bits(reslice(on_entry), pred_bits); + let changed = join_bits(&self.dfcx.oper, pred_bits, on_entry); + copy_bits(on_entry, pred_bits); changed }; if changed { @@ -942,7 +851,7 @@ impl<'a, O:DataFlowOperator> PropagationContext<'a, O> { } fn mut_bits_to_str(words: &mut [uint]) -> ~str { - bits_to_str(reslice(words)) + bits_to_str(words) } fn bits_to_str(words: &[uint]) -> ~str { @@ -1007,9 +916,3 @@ fn bit_str(bit: uint) -> ~str { format!("[{}:{}-{:02x}]", bit, byte, lobits) } -fn reslice<'a>(v: &'a mut [uint]) -> &'a [uint] { - // bFIXME(#5074) this function should not be necessary at all - unsafe { - cast::transmute(v) - } -} diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 93f439b653c59..efd19cf73c0ab 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -22,7 +22,7 @@ * forms): * * E = rvalue // some computed rvalue - * | x // address of a local variable, arg, or upvar + * | x // address of a local variable or argument * | *E // deref of a ptr * | E.comp // access to an interior component * @@ -44,13 +44,25 @@ * themselves. For example, auto-derefs are explicit. Also, an index a[b] is * decomposed into two operations: a derefence to reach the array data and * then an index to jump forward to the relevant item. + * + * ## By-reference upvars + * + * One part of the translation which may be non-obvious is that we translate + * closure upvars into the dereference of a borrowed pointer; this more closely + * resembles the runtime translation. So, for example, if we had: + * + * let mut x = 3; + * let y = 5; + * let inc = || x += y; + * + * Then when we categorize `x` (*within* the closure) we would yield a + * result of `*x'`, effectively, where `x'` is a `cat_upvar` reference + * tied to `x`. The type of `x'` will be a borrowed pointer. */ use middle::ty; -use middle::typeck; use util::ppaux::{ty_to_str, region_ptr_to_str, Repr}; -use util::common::indenter; use syntax::ast::{MutImmutable, MutMutable}; use syntax::ast; @@ -63,15 +75,15 @@ pub enum categorization { cat_rvalue(ty::Region), // temporary val, argument is its scope cat_static_item, cat_copied_upvar(CopiedUpvar), // upvar copied into @fn or ~fn env - cat_stack_upvar(cmt), // by ref upvar from || + cat_upvar(ty::UpvarId, ty::UpvarBorrow), // by ref upvar from stack closure cat_local(ast::NodeId), // local variable cat_arg(ast::NodeId), // formal argument cat_deref(cmt, uint, PointerKind), // deref of a ptr cat_interior(cmt, InteriorKind), // something interior: field, tuple, etc - cat_downcast(cmt), // selects a particular enum variant (..) + cat_downcast(cmt), // selects a particular enum variant (*1) cat_discr(cmt, ast::NodeId), // match discriminant (see preserve()) - // (..) downcast is only required if the enum has more than one variant + // (*1) downcast is only required if the enum has more than one variant } #[deriving(Eq)] @@ -83,10 +95,10 @@ pub struct CopiedUpvar { // different kinds of pointers: #[deriving(Eq, IterBytes)] pub enum PointerKind { - uniq_ptr, - gc_ptr, - region_ptr(ast::Mutability, ty::Region), - unsafe_ptr(ast::Mutability) + OwnedPtr, + GcPtr, + BorrowedPtr(ty::BorrowKind, ty::Region), + UnsafePtr(ast::Mutability), } // We use the term "interior" to mean "something reachable from the @@ -114,7 +126,7 @@ pub enum ElementKind { pub enum MutabilityCategory { McImmutable, // Immutable. McDeclared, // Directly declared as mutable. - McInherited // Inherited from the fact that owner is mutable. + McInherited, // Inherited from the fact that owner is mutable. } // `cmt`: "Category, Mutability, and Type". @@ -159,30 +171,32 @@ pub fn opt_deref_kind(t: ty::t) -> Option { ty::ty_vec(_, ty::vstore_uniq) | ty::ty_str(ty::vstore_uniq) | ty::ty_closure(ty::ClosureTy {sigil: ast::OwnedSigil, ..}) => { - Some(deref_ptr(uniq_ptr)) + Some(deref_ptr(OwnedPtr)) } ty::ty_rptr(r, mt) | ty::ty_vec(mt, ty::vstore_slice(r)) => { - Some(deref_ptr(region_ptr(mt.mutbl, r))) + let kind = ty::BorrowKind::from_mutbl(mt.mutbl); + Some(deref_ptr(BorrowedPtr(kind, r))) } ty::ty_trait(_, _, ty::RegionTraitStore(r), m, _) => { - Some(deref_ptr(region_ptr(m, r))) + let kind = ty::BorrowKind::from_mutbl(m); + Some(deref_ptr(BorrowedPtr(kind, r))) } ty::ty_str(ty::vstore_slice(r)) | ty::ty_closure(ty::ClosureTy {sigil: ast::BorrowedSigil, region: r, ..}) => { - Some(deref_ptr(region_ptr(ast::MutImmutable, r))) + Some(deref_ptr(BorrowedPtr(ty::ImmBorrow, r))) } - ty::ty_box(_) => { - Some(deref_ptr(gc_ptr)) + ty::ty_box(..) => { + Some(deref_ptr(GcPtr)) } ty::ty_ptr(ref mt) => { - Some(deref_ptr(unsafe_ptr(mt.mutbl))) + Some(deref_ptr(UnsafePtr(mt.mutbl))) } ty::ty_enum(..) | @@ -210,53 +224,7 @@ pub fn deref_kind(tcx: ty::ctxt, t: ty::t) -> deref_kind { } } -pub fn cat_expr(tcx: ty::ctxt, - method_map: typeck::method_map, - expr: &ast::Expr) - -> cmt { - let mcx = &mem_categorization_ctxt { - tcx: tcx, method_map: method_map - }; - return mcx.cat_expr(expr); -} - -pub fn cat_expr_unadjusted(tcx: ty::ctxt, - method_map: typeck::method_map, - expr: &ast::Expr) - -> cmt { - let mcx = &mem_categorization_ctxt { - tcx: tcx, method_map: method_map - }; - return mcx.cat_expr_unadjusted(expr); -} - -pub fn cat_expr_autoderefd( - tcx: ty::ctxt, - method_map: typeck::method_map, - expr: &ast::Expr, - autoderefs: uint) -> cmt -{ - let mcx = &mem_categorization_ctxt { - tcx: tcx, method_map: method_map - }; - return mcx.cat_expr_autoderefd(expr, autoderefs); -} - -pub fn cat_def( - tcx: ty::ctxt, - method_map: typeck::method_map, - expr_id: ast::NodeId, - expr_span: Span, - expr_ty: ty::t, - def: ast::Def) -> cmt { - - let mcx = &mem_categorization_ctxt { - tcx: tcx, method_map: method_map - }; - return mcx.cat_def(expr_id, expr_span, expr_ty, def); -} - -pub trait ast_node { +trait ast_node { fn id(&self) -> ast::NodeId; fn span(&self) -> Span; } @@ -271,9 +239,37 @@ impl ast_node for ast::Pat { fn span(&self) -> Span { self.span } } -pub struct mem_categorization_ctxt { - tcx: ty::ctxt, - method_map: typeck::method_map, +pub struct MemCategorizationContext { + typer: TYPER +} + +pub type McResult = Result; + +/** + * The `Typer` trait provides the interface for the mem-categorization + * module to the results of the type check. It can be used to query + * the type assigned to an expression node, to inquire after adjustments, + * and so on. + * + * This interface is needed because mem-categorization is used from + * two places: `regionck` and `borrowck`. `regionck` executes before + * type inference is complete, and hence derives types and so on from + * intermediate tables. This also implies that type errors can occur, + * and hence `node_ty()` and friends return a `Result` type -- any + * error will propagate back up through the mem-categorization + * routines. + * + * In the borrow checker, in contrast, type checking is complete and we + * know that no errors have occurred, so we simply consult the tcx and we + * can be sure that only `Ok` results will occur. + */ +pub trait Typer { + fn tcx(&self) -> ty::ctxt; + fn node_ty(&mut self, id: ast::NodeId) -> McResult; + fn adjustment(&mut self, node_id: ast::NodeId) -> Option<@ty::AutoAdjustment>; + fn is_method_call(&mut self, id: ast::NodeId) -> bool; + fn temporary_scope(&mut self, rvalue_id: ast::NodeId) -> Option; + fn upvar_borrow(&mut self, upvar_id: ty::UpvarId) -> ty::UpvarBorrow; } impl ToStr for MutabilityCategory { @@ -290,18 +286,45 @@ impl MutabilityCategory { } } + pub fn from_borrow_kind(borrow_kind: ty::BorrowKind) -> MutabilityCategory { + match borrow_kind { + ty::ImmBorrow => McImmutable, + ty::UniqueImmBorrow => McImmutable, + ty::MutBorrow => McDeclared, + } + } + + pub fn from_pointer_kind(base_mutbl: MutabilityCategory, + ptr: PointerKind) -> MutabilityCategory { + match ptr { + OwnedPtr => { + base_mutbl.inherit() + } + BorrowedPtr(borrow_kind, _) => { + MutabilityCategory::from_borrow_kind(borrow_kind) + } + GcPtr => { + McImmutable + } + UnsafePtr(m) => { + MutabilityCategory::from_mutbl(m) + } + } + } + pub fn inherit(&self) -> MutabilityCategory { match *self { McImmutable => McImmutable, McDeclared => McInherited, - McInherited => McInherited + McInherited => McInherited, } } pub fn is_mutable(&self) -> bool { match *self { McImmutable => false, - McDeclared | McInherited => true + McInherited => true, + McDeclared => true, } } @@ -320,55 +343,78 @@ impl MutabilityCategory { } } -impl mem_categorization_ctxt { - pub fn expr_ty(&self, expr: &ast::Expr) -> ty::t { - ty::expr_ty(self.tcx, expr) +macro_rules! if_ok( + ($inp: expr) => ( + match $inp { + Ok(v) => { v } + Err(e) => { return Err(e); } + } + ) +) + +impl MemCategorizationContext { + fn tcx(&self) -> ty::ctxt { + self.typer.tcx() + } + + fn adjustment(&mut self, id: ast::NodeId) -> Option<@ty::AutoAdjustment> { + self.typer.adjustment(id) + } + + fn expr_ty(&mut self, expr: &ast::Expr) -> McResult { + self.typer.node_ty(expr.id) } - pub fn pat_ty(&self, pat: &ast::Pat) -> ty::t { - ty::node_id_to_type(self.tcx, pat.id) + fn expr_ty_adjusted(&mut self, expr: &ast::Expr) -> McResult { + let unadjusted_ty = if_ok!(self.expr_ty(expr)); + let adjustment = self.adjustment(expr.id); + Ok(ty::adjust_ty(self.tcx(), expr.span, unadjusted_ty, adjustment)) } - pub fn cat_expr(&self, expr: &ast::Expr) -> cmt { - let adjustments = self.tcx.adjustments.borrow(); - match adjustments.get().find(&expr.id) { + fn node_ty(&mut self, id: ast::NodeId) -> McResult { + self.typer.node_ty(id) + } + + fn pat_ty(&mut self, pat: @ast::Pat) -> McResult { + self.typer.node_ty(pat.id) + } + + pub fn cat_expr(&mut self, expr: &ast::Expr) -> McResult { + match self.adjustment(expr.id) { None => { // No adjustments. self.cat_expr_unadjusted(expr) } Some(adjustment) => { - match **adjustment { + match *adjustment { ty::AutoObject(..) => { // Implicity casts a concrete object to trait object // so just patch up the type - let expr_ty = ty::expr_ty_adjusted(self.tcx, expr); - @cmt_ { - ty: expr_ty, - ..*self.cat_expr_unadjusted(expr) - } + let expr_ty = if_ok!(self.expr_ty_adjusted(expr)); + let expr_cmt = if_ok!(self.cat_expr_unadjusted(expr)); + Ok(@cmt_ {ty: expr_ty, ..*expr_cmt}) } ty::AutoAddEnv(..) => { // 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_node(expr.id(), expr.span(), expr_ty) + let expr_ty = if_ok!(self.expr_ty_adjusted(expr)); + Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)) } - ty::AutoDerefRef(ty::AutoDerefRef { - autoref: Some(_), - ..}) => { + ty::AutoDerefRef( + ty::AutoDerefRef { + autoref: Some(_), ..}) => { // Equivalent to &*expr or something similar. // Result is an rvalue. - let expr_ty = ty::expr_ty_adjusted(self.tcx, expr); - self.cat_rvalue_node(expr.id(), expr.span(), expr_ty) + let expr_ty = if_ok!(self.expr_ty_adjusted(expr)); + Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)) } - ty::AutoDerefRef(ty::AutoDerefRef { - autoref: None, - autoderefs: autoderefs - }) => { + ty::AutoDerefRef( + ty::AutoDerefRef { + autoref: None, autoderefs: autoderefs}) => { // Equivalent to *expr or something similar. self.cat_expr_autoderefd(expr, autoderefs) } @@ -377,53 +423,51 @@ impl mem_categorization_ctxt { } } - pub fn cat_expr_autoderefd(&self, expr: &ast::Expr, autoderefs: uint) - -> cmt { - let mut cmt = self.cat_expr_unadjusted(expr); + pub fn cat_expr_autoderefd(&mut self, expr: &ast::Expr, autoderefs: uint) + -> McResult { + let mut cmt = if_ok!(self.cat_expr_unadjusted(expr)); for deref in range(1u, autoderefs + 1) { cmt = self.cat_deref(expr, cmt, deref); } - return cmt; + return Ok(cmt); } - pub fn cat_expr_unadjusted(&self, expr: &ast::Expr) -> cmt { + pub fn cat_expr_unadjusted(&mut self, expr: &ast::Expr) -> McResult { debug!("cat_expr: id={} expr={}", - expr.id, pprust::expr_to_str(expr, self.tcx.sess.intr())); + expr.id, + expr.repr(self.tcx())); - let expr_ty = self.expr_ty(expr); + let expr_ty = if_ok!(self.expr_ty(expr)); match expr.node { ast::ExprUnary(_, ast::UnDeref, e_base) => { - let method_map = self.method_map.borrow(); - if method_map.get().contains_key(&expr.id) { - return self.cat_rvalue_node(expr.id(), expr.span(), expr_ty); + if self.typer.is_method_call(expr.id) { + return Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)); } - let base_cmt = self.cat_expr(e_base); - self.cat_deref(expr, base_cmt, 0) + let base_cmt = if_ok!(self.cat_expr(e_base)); + Ok(self.cat_deref(expr, base_cmt, 0)) } ast::ExprField(base, f_name, _) => { // Method calls are now a special syntactic form, // so `a.b` should always be a field. - let method_map = self.method_map.borrow(); - assert!(!method_map.get().contains_key(&expr.id)); + assert!(!self.typer.is_method_call(expr.id)); - let base_cmt = self.cat_expr(base); - self.cat_field(expr, base_cmt, f_name, self.expr_ty(expr)) + let base_cmt = if_ok!(self.cat_expr(base)); + Ok(self.cat_field(expr, base_cmt, f_name, expr_ty)) } ast::ExprIndex(_, base, _) => { - let method_map = self.method_map.borrow(); - if method_map.get().contains_key(&expr.id) { - return self.cat_rvalue_node(expr.id(), expr.span(), expr_ty); + if self.typer.is_method_call(expr.id) { + return Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)); } - let base_cmt = self.cat_expr(base); - self.cat_index(expr, base_cmt, 0) + let base_cmt = if_ok!(self.cat_expr(base)); + Ok(self.cat_index(expr, base_cmt, 0)) } ast::ExprPath(_) => { - let def_map = self.tcx.def_map.borrow(); + let def_map = self.tcx().def_map.borrow(); let def = def_map.get().get_copy(&expr.id); self.cat_def(expr.id, expr.span, expr_ty, def) } @@ -441,49 +485,48 @@ impl mem_categorization_ctxt { ast::ExprLit(..) | ast::ExprBreak(..) | ast::ExprMac(..) | ast::ExprAgain(..) | ast::ExprStruct(..) | ast::ExprRepeat(..) | ast::ExprInlineAsm(..) | ast::ExprBox(..) => { - return self.cat_rvalue_node(expr.id(), expr.span(), expr_ty); + Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty)) } ast::ExprForLoop(..) => fail!("non-desugared expr_for_loop") } } - pub fn cat_def(&self, + pub fn cat_def(&mut self, id: ast::NodeId, span: Span, expr_ty: ty::t, def: ast::Def) - -> cmt { + -> McResult { debug!("cat_def: id={} expr={}", - id, ty_to_str(self.tcx, expr_ty)); - + id, expr_ty.repr(self.tcx())); match def { ast::DefStruct(..) | ast::DefVariant(..) => { - self.cat_rvalue_node(id, span, expr_ty) + Ok(self.cat_rvalue_node(id, span, expr_ty)) } ast::DefFn(..) | ast::DefStaticMethod(..) | ast::DefMod(_) | ast::DefForeignMod(_) | ast::DefStatic(_, false) | ast::DefUse(_) | ast::DefTrait(_) | ast::DefTy(_) | ast::DefPrimTy(_) | ast::DefTyParam(..) | ast::DefTyParamBinder(..) | ast::DefRegion(_) | ast::DefLabel(_) | ast::DefSelfTy(..) | ast::DefMethod(..) => { - @cmt_ { + Ok(@cmt_ { id:id, span:span, cat:cat_static_item, mutbl: McImmutable, ty:expr_ty - } + }) } ast::DefStatic(_, true) => { - @cmt_ { + Ok(@cmt_ { id:id, span:span, cat:cat_static_item, mutbl: McDeclared, ty:expr_ty - } + }) } ast::DefArg(vid, binding_mode) => { @@ -495,17 +538,17 @@ impl mem_categorization_ctxt { ast::BindByValue(ast::MutMutable) => McDeclared, _ => McImmutable }; - @cmt_ { + Ok(@cmt_ { id: id, span: span, cat: cat_arg(vid), mutbl: m, ty:expr_ty - } + }) } - ast::DefUpvar(upvar_id, inner, fn_node_id, _) => { - let ty = ty::node_id_to_type(self.tcx, fn_node_id); + ast::DefUpvar(var_id, _, fn_node_id, _) => { + let ty = if_ok!(self.node_ty(fn_node_id)); match ty::get(ty).sty { ty::ty_closure(ref closure_ty) => { // Decide whether to use implicit reference or by copy/move @@ -523,33 +566,25 @@ impl mem_categorization_ctxt { }; if var_is_refd { - let upvar_cmt = - self.cat_def(id, span, expr_ty, *inner); - @cmt_ { - id:id, - span:span, - cat:cat_stack_upvar(upvar_cmt), - mutbl:upvar_cmt.mutbl.inherit(), - ty:upvar_cmt.ty - } + self.cat_upvar(id, span, var_id, fn_node_id) } else { // FIXME #2152 allow mutation of moved upvars - @cmt_ { + Ok(@cmt_ { id:id, span:span, cat:cat_copied_upvar(CopiedUpvar { - upvar_id: upvar_id, + upvar_id: var_id, onceness: closure_ty.onceness}), mutbl:McImmutable, ty:expr_ty - } + }) } } _ => { - self.tcx.sess.span_bug( + self.tcx().sess.span_bug( span, - format!("upvar of non-closure {:?} - {}", - fn_node_id, ty.repr(self.tcx))); + format!("Upvar of non-closure {} - {}", + fn_node_id, ty.repr(self.tcx()))); } } } @@ -562,19 +597,73 @@ impl mem_categorization_ctxt { _ => McImmutable }; - @cmt_ { + Ok(@cmt_ { id: id, span: span, cat: cat_local(vid), mutbl: m, ty: expr_ty - } + }) } } } - pub fn cat_rvalue_node(&self, id: ast::NodeId, span: Span, expr_ty: ty::t) -> cmt { - match self.tcx.region_maps.temporary_scope(id) { + fn cat_upvar(&mut self, + id: ast::NodeId, + span: Span, + var_id: ast::NodeId, + fn_node_id: ast::NodeId) + -> McResult { + /*! + * Upvars through a closure are in fact indirect + * references. That is, when a closure refers to a + * variable from a parent stack frame like `x = 10`, + * that is equivalent to `*x_ = 10` where `x_` is a + * borrowed pointer (`&mut x`) created when the closure + * was created and store in the environment. This + * equivalence is expose in the mem-categorization. + */ + + let upvar_id = ty::UpvarId { var_id: var_id, + closure_expr_id: fn_node_id }; + + let upvar_borrow = self.typer.upvar_borrow(upvar_id); + + let var_ty = if_ok!(self.node_ty(var_id)); + + // We can't actually represent the types of all upvars + // as user-describable types, since upvars support const + // and unique-imm borrows! Therefore, we cheat, and just + // give err type. Nobody should be inspecting this type anyhow. + let upvar_ty = ty::mk_err(); + + let base_cmt = @cmt_ { + id:id, + span:span, + cat:cat_upvar(upvar_id, upvar_borrow), + mutbl:McImmutable, + ty:upvar_ty, + }; + + let ptr = BorrowedPtr(upvar_borrow.kind, upvar_borrow.region); + + let deref_cmt = @cmt_ { + id:id, + span:span, + cat:cat_deref(base_cmt, 0, ptr), + mutbl:MutabilityCategory::from_borrow_kind(upvar_borrow.kind), + ty:var_ty, + }; + + Ok(deref_cmt) + } + + pub fn cat_rvalue_node(&mut self, + id: ast::NodeId, + span: Span, + expr_ty: ty::t) + -> cmt { + match self.typer.temporary_scope(id) { Some(scope) => { self.cat_rvalue(id, span, ty::ReScope(scope), expr_ty) } @@ -584,7 +673,7 @@ impl mem_categorization_ctxt { } } - pub fn cat_rvalue(&self, + pub fn cat_rvalue(&mut self, cmt_id: ast::NodeId, span: Span, temp_scope: ty::Region, @@ -602,7 +691,7 @@ impl mem_categorization_ctxt { /// component is inherited from the base it is a part of. For /// example, a record field is mutable if it is declared mutable /// or if the container is mutable. - pub fn inherited_mutability(&self, + pub fn inherited_mutability(&mut self, base_m: MutabilityCategory, interior_m: ast::Mutability) -> MutabilityCategory { @@ -612,7 +701,7 @@ impl mem_categorization_ctxt { } } - pub fn cat_field(&self, + pub fn cat_field(&mut self, node: &N, base_cmt: cmt, f_name: ast::Ident, @@ -627,7 +716,7 @@ impl mem_categorization_ctxt { } } - pub fn cat_deref_fn_or_obj(&self, + pub fn cat_deref_fn_or_obj(&mut self, node: &N, base_cmt: cmt, deref_cnt: uint) @@ -638,11 +727,11 @@ impl mem_categorization_ctxt { // know what type lies at the other end, so we just call it // `()` (the empty tuple). - let opaque_ty = ty::mk_tup(self.tcx, ~[]); + let opaque_ty = ty::mk_tup(self.tcx(), ~[]); return self.cat_deref_common(node, base_cmt, deref_cnt, opaque_ty); } - pub fn cat_deref(&self, + pub fn cat_deref(&mut self, node: &N, base_cmt: cmt, deref_cnt: uint) @@ -650,37 +739,28 @@ impl mem_categorization_ctxt { let mt = match ty::deref(base_cmt.ty, true) { Some(mt) => mt, None => { - self.tcx.sess.span_bug( + self.tcx().sess.span_bug( node.span(), - format!("explicit deref of non-derefable type: {}", - ty_to_str(self.tcx, base_cmt.ty))); + format!("Explicit deref of non-derefable type: {}", + base_cmt.ty.repr(self.tcx()))); } }; return self.cat_deref_common(node, base_cmt, deref_cnt, mt.ty); } - pub fn cat_deref_common(&self, + pub fn cat_deref_common(&mut self, node: &N, base_cmt: cmt, deref_cnt: uint, deref_ty: ty::t) -> cmt { - match deref_kind(self.tcx, base_cmt.ty) { + match deref_kind(self.tcx(), base_cmt.ty) { deref_ptr(ptr) => { // for unique ptrs, we inherit mutability from the // owning reference. - let m = match ptr { - uniq_ptr => { - base_cmt.mutbl.inherit() - } - gc_ptr => { - McImmutable - } - region_ptr(m, _) | unsafe_ptr(m) => { - MutabilityCategory::from_mutbl(m) - } - }; + let m = MutabilityCategory::from_pointer_kind(base_cmt.mutbl, + ptr); @cmt_ { id:node.id(), @@ -704,7 +784,7 @@ impl mem_categorization_ctxt { } } - pub fn cat_index(&self, + pub fn cat_index(&mut self, elt: &N, base_cmt: cmt, derefs: uint) @@ -743,28 +823,18 @@ impl mem_categorization_ctxt { let element_ty = match ty::index(base_cmt.ty) { Some(ref mt) => mt.ty, None => { - self.tcx.sess.span_bug( + self.tcx().sess.span_bug( elt.span(), - format!("explicit index of non-index type `{}`", - ty_to_str(self.tcx, base_cmt.ty))); + format!("Explicit index of non-index type `{}`", + base_cmt.ty.repr(self.tcx()))); } }; - return match deref_kind(self.tcx, base_cmt.ty) { + return match deref_kind(self.tcx(), base_cmt.ty) { deref_ptr(ptr) => { // for unique ptrs, we inherit mutability from the // owning reference. - let m = match ptr { - uniq_ptr => { - base_cmt.mutbl.inherit() - } - gc_ptr => { - McImmutable - } - region_ptr(m, _) | unsafe_ptr(m) => { - MutabilityCategory::from_mutbl(m) - } - }; + let m = MutabilityCategory::from_pointer_kind(base_cmt.mutbl, ptr); // the deref is explicit in the resulting cmt let deref_cmt = @cmt_ { @@ -775,7 +845,7 @@ impl mem_categorization_ctxt { ty:element_ty }; - interior(elt, deref_cmt, base_cmt.ty, m, element_ty) + interior(elt, deref_cmt, base_cmt.ty, m.inherit(), element_ty) } deref_interior(_) => { @@ -801,7 +871,57 @@ impl mem_categorization_ctxt { } } - pub fn cat_imm_interior(&self, + pub fn cat_slice_pattern(&mut self, + vec_cmt: cmt, + slice_pat: @ast::Pat) + -> McResult<(cmt, ast::Mutability, ty::Region)> { + /*! + * Given a pattern P like: `[_, ..Q, _]`, where `vec_cmt` is + * the cmt for `P`, `slice_pat` is the pattern `Q`, returns: + * - a cmt for `Q` + * - the mutability and region of the slice `Q` + * + * These last two bits of info happen to be things that + * borrowck needs. + */ + + let slice_ty = if_ok!(self.node_ty(slice_pat.id)); + let (slice_mutbl, slice_r) = vec_slice_info(self.tcx(), + slice_pat, + slice_ty); + let cmt_slice = self.cat_index(slice_pat, vec_cmt, 0); + return Ok((cmt_slice, slice_mutbl, slice_r)); + + fn vec_slice_info(tcx: ty::ctxt, + pat: @ast::Pat, + slice_ty: ty::t) + -> (ast::Mutability, ty::Region) { + /*! + * In a pattern like [a, b, ..c], normally `c` has slice type, + * but if you have [a, b, ..ref c], then the type of `ref c` + * will be `&&[]`, so to extract the slice details we have + * to recurse through rptrs. + */ + + match ty::get(slice_ty).sty { + ty::ty_vec(slice_mt, ty::vstore_slice(slice_r)) => { + (slice_mt.mutbl, slice_r) + } + + ty::ty_rptr(_, ref mt) => { + vec_slice_info(tcx, pat, mt.ty) + } + + _ => { + tcx.sess.span_bug( + pat.span, + format!("Type of slice pattern is not a slice")); + } + } + } + } + + pub fn cat_imm_interior(&mut self, node: &N, base_cmt: cmt, interior_ty: ty::t, @@ -816,7 +936,7 @@ impl mem_categorization_ctxt { } } - pub fn cat_downcast(&self, + pub fn cat_downcast(&mut self, node: &N, base_cmt: cmt, downcast_ty: ty::t) @@ -830,10 +950,13 @@ impl mem_categorization_ctxt { } } - pub fn cat_pattern(&self, + pub fn cat_pattern(&mut self, cmt: cmt, - pat: &ast::Pat, - op: |cmt, &ast::Pat|) { + pat: @ast::Pat, + op: |&mut MemCategorizationContext, + cmt, + @ast::Pat|) + -> McResult<()> { // Here, `cmt` is the categorization for the value being // matched and pat is the pattern it is being matched against. // @@ -846,7 +969,7 @@ impl mem_categorization_ctxt { // we can be sure that the binding will remain valid for the // duration of the arm. // - // (..) There is subtlety concerning the correspondence between + // (*2) There is subtlety concerning the correspondence between // pattern ids and types as compared to *expression* ids and // types. This is explained briefly. on the definition of the // type `cmt`, so go off and read what it says there, then @@ -856,7 +979,8 @@ impl mem_categorization_ctxt { // In general, the id of the cmt should be the node that // "produces" the value---patterns aren't executable code // exactly, but I consider them to "execute" when they match a - // value. So if you have something like: + // value, and I consider them to produce the value that was + // matched. So if you have something like: // // let x = @@3; // match x { @@ -878,13 +1002,12 @@ impl mem_categorization_ctxt { // step out of sync again. So you'll see below that we always // get the type of the *subpattern* and use that. - let tcx = self.tcx; + let tcx = self.tcx(); debug!("cat_pattern: id={} pat={} cmt={}", pat.id, pprust::pat_to_str(pat, tcx.sess.intr()), cmt.repr(tcx)); - let _i = indenter(); - op(cmt, pat); + op(self, cmt, pat); match pat.node { ast::PatWild | ast::PatWildMulti => { @@ -895,13 +1018,13 @@ impl mem_categorization_ctxt { // variant(..) } ast::PatEnum(_, Some(ref subpats)) => { - let def_map = self.tcx.def_map.borrow(); + let def_map = self.tcx().def_map.borrow(); match def_map.get().find(&pat.id) { Some(&ast::DefVariant(enum_did, _, _)) => { // variant(x, y, z) let downcast_cmt = { - if ty::enum_is_univariant(tcx, enum_did) { + if ty::enum_is_univariant(self.tcx(), enum_did) { cmt // univariant, no downcast needed } else { self.cat_downcast(pat, cmt, cmt.ty) @@ -909,34 +1032,34 @@ impl mem_categorization_ctxt { }; for (i, &subpat) in subpats.iter().enumerate() { - let subpat_ty = self.pat_ty(subpat); // see (..) + let subpat_ty = if_ok!(self.pat_ty(subpat)); // see (*2) let subcmt = self.cat_imm_interior( pat, downcast_cmt, subpat_ty, InteriorField(PositionalField(i))); - self.cat_pattern(subcmt, subpat, |x,y| op(x,y)); + if_ok!(self.cat_pattern(subcmt, subpat, |x,y,z| op(x,y,z))); } } Some(&ast::DefFn(..)) | Some(&ast::DefStruct(..)) => { for (i, &subpat) in subpats.iter().enumerate() { - let subpat_ty = self.pat_ty(subpat); // see (..) + let subpat_ty = if_ok!(self.pat_ty(subpat)); // see (*2) let cmt_field = self.cat_imm_interior( pat, cmt, subpat_ty, InteriorField(PositionalField(i))); - self.cat_pattern(cmt_field, subpat, |x,y| op(x,y)); + if_ok!(self.cat_pattern(cmt_field, subpat, |x,y,z| op(x,y,z))); } } Some(&ast::DefStatic(..)) => { for &subpat in subpats.iter() { - self.cat_pattern(cmt, subpat, |x,y| op(x,y)); + if_ok!(self.cat_pattern(cmt, subpat, |x,y,z| op(x,y,z))); } } _ => { - self.tcx.sess.span_bug( + self.tcx().sess.span_bug( pat.span, "enum pattern didn't resolve to enum or struct"); } @@ -944,7 +1067,7 @@ impl mem_categorization_ctxt { } ast::PatIdent(_, _, Some(subpat)) => { - self.cat_pattern(cmt, subpat, op); + if_ok!(self.cat_pattern(cmt, subpat, op)); } ast::PatIdent(_, _, None) => { @@ -954,42 +1077,42 @@ impl mem_categorization_ctxt { ast::PatStruct(_, ref field_pats, _) => { // {f1: p1, ..., fN: pN} for fp in field_pats.iter() { - let field_ty = self.pat_ty(fp.pat); // see (..) + let field_ty = if_ok!(self.pat_ty(fp.pat)); // see (*2) let cmt_field = self.cat_field(pat, cmt, fp.ident, field_ty); - self.cat_pattern(cmt_field, fp.pat, |x,y| op(x,y)); + if_ok!(self.cat_pattern(cmt_field, fp.pat, |x,y,z| op(x,y,z))); } } ast::PatTup(ref subpats) => { // (p1, ..., pN) for (i, &subpat) in subpats.iter().enumerate() { - let subpat_ty = self.pat_ty(subpat); // see (..) + let subpat_ty = if_ok!(self.pat_ty(subpat)); // see (*2) let subcmt = self.cat_imm_interior( pat, cmt, subpat_ty, InteriorField(PositionalField(i))); - self.cat_pattern(subcmt, subpat, |x,y| op(x,y)); + if_ok!(self.cat_pattern(subcmt, subpat, |x,y,z| op(x,y,z))); } } ast::PatUniq(subpat) | ast::PatRegion(subpat) => { // @p1, ~p1 let subcmt = self.cat_deref(pat, cmt, 0); - self.cat_pattern(subcmt, subpat, op); + if_ok!(self.cat_pattern(subcmt, subpat, op)); } ast::PatVec(ref before, slice, ref after) => { let elt_cmt = self.cat_index(pat, cmt, 0); for &before_pat in before.iter() { - self.cat_pattern(elt_cmt, before_pat, |x,y| op(x,y)); + if_ok!(self.cat_pattern(elt_cmt, before_pat, |x,y,z| op(x,y,z))); } for &slice_pat in slice.iter() { - let slice_ty = self.pat_ty(slice_pat); + let slice_ty = if_ok!(self.pat_ty(slice_pat)); let slice_cmt = self.cat_rvalue_node(pat.id(), pat.span(), slice_ty); - self.cat_pattern(slice_cmt, slice_pat, |x,y| op(x,y)); + if_ok!(self.cat_pattern(slice_cmt, slice_pat, |x,y,z| op(x,y,z))); } for &after_pat in after.iter() { - self.cat_pattern(elt_cmt, after_pat, |x,y| op(x,y)); + if_ok!(self.cat_pattern(elt_cmt, after_pat, |x,y,z| op(x,y,z))); } } @@ -997,9 +1120,11 @@ impl mem_categorization_ctxt { /*always ok*/ } } + + Ok(()) } - pub fn mut_to_str(&self, mutbl: ast::Mutability) -> ~str { + pub fn mut_to_str(&mut self, mutbl: ast::Mutability) -> ~str { match mutbl { MutMutable => ~"mutable", MutImmutable => ~"immutable" @@ -1023,8 +1148,15 @@ impl mem_categorization_ctxt { cat_arg(..) => { ~"argument" } - cat_deref(_, _, pk) => { - format!("dereference of {} pointer", ptr_sigil(pk)) + cat_deref(base, _, pk) => { + match base.cat { + cat_upvar(..) => { + format!("captured outer variable") + } + _ => { + format!("dereference of {} pointer", ptr_sigil(pk)) + } + } } cat_interior(_, InteriorField(NamedField(_))) => { ~"field" @@ -1041,7 +1173,7 @@ impl mem_categorization_ctxt { cat_interior(_, InteriorElement(OtherElement)) => { ~"indexed content" } - cat_stack_upvar(_) => { + cat_upvar(..) => { ~"captured outer variable" } cat_discr(cmt, _) => { @@ -1054,7 +1186,7 @@ impl mem_categorization_ctxt { } pub fn region_to_str(&self, r: ty::Region) -> ~str { - region_ptr_to_str(self.tcx, r) + region_ptr_to_str(self.tcx(), r) } } @@ -1099,7 +1231,7 @@ pub fn field_mutbl(tcx: ty::ctxt, pub enum AliasableReason { AliasableManaged, - AliasableBorrowed(ast::Mutability), + AliasableBorrowed, AliasableOther, AliasableStatic, AliasableStaticMut, @@ -1117,16 +1249,16 @@ impl cmt_ { cat_copied_upvar(..) | cat_local(..) | cat_arg(..) | - cat_deref(_, _, unsafe_ptr(..)) | - cat_deref(_, _, gc_ptr) | - cat_deref(_, _, region_ptr(..)) => { + cat_deref(_, _, UnsafePtr(..)) | + cat_deref(_, _, GcPtr(..)) | + cat_deref(_, _, BorrowedPtr(..)) | + cat_upvar(..) => { @self } cat_downcast(b) | - cat_stack_upvar(b) | cat_discr(b, _) | cat_interior(b, _) | - cat_deref(b, _, uniq_ptr) => { + cat_deref(b, _, OwnedPtr) => { b.guarantor() } } @@ -1143,10 +1275,10 @@ impl cmt_ { // aliased and eventually recused. match self.cat { - cat_deref(b, _, region_ptr(MutMutable, _)) | + cat_deref(b, _, BorrowedPtr(ty::MutBorrow, _)) | + cat_deref(b, _, BorrowedPtr(ty::UniqueImmBorrow, _)) | cat_downcast(b) | - cat_stack_upvar(b) | - cat_deref(b, _, uniq_ptr) | + cat_deref(b, _, OwnedPtr) | cat_interior(b, _) | cat_discr(b, _) => { // Aliasability depends on base cmt @@ -1156,8 +1288,9 @@ impl cmt_ { cat_copied_upvar(CopiedUpvar {onceness: ast::Once, ..}) | cat_rvalue(..) | cat_local(..) | + cat_upvar(..) | cat_arg(_) | - cat_deref(_, _, unsafe_ptr(..)) => { // yes, it's aliasable, but... + cat_deref(_, _, UnsafePtr(..)) => { // yes, it's aliasable, but... None } @@ -1173,12 +1306,12 @@ impl cmt_ { } } - cat_deref(_, _, gc_ptr) => { + cat_deref(_, _, GcPtr) => { Some(AliasableManaged) } - cat_deref(_, _, region_ptr(m @ MutImmutable, _)) => { - Some(AliasableBorrowed(m)) + cat_deref(_, _, BorrowedPtr(ty::ImmBorrow, _)) => { + Some(AliasableBorrowed) } } } @@ -1201,12 +1334,15 @@ impl Repr for categorization { cat_rvalue(..) | cat_copied_upvar(..) | cat_local(..) | + cat_upvar(..) | cat_arg(..) => { format!("{:?}", *self) } cat_deref(cmt, derefs, ptr) => { - format!("{}->({}, {})", cmt.cat.repr(tcx), - ptr_sigil(ptr), derefs) + format!("{}-{}{}->", + cmt.cat.repr(tcx), + ptr_sigil(ptr), + derefs) } cat_interior(cmt, interior) => { format!("{}.{}", @@ -1216,7 +1352,6 @@ impl Repr for categorization { cat_downcast(cmt) => { format!("{}->(enum)", cmt.cat.repr(tcx)) } - cat_stack_upvar(cmt) | cat_discr(cmt, _) => { cmt.cat.repr(tcx) } @@ -1224,12 +1359,14 @@ impl Repr for categorization { } } -pub fn ptr_sigil(ptr: PointerKind) -> ~str { +pub fn ptr_sigil(ptr: PointerKind) -> &'static str { match ptr { - uniq_ptr => ~"~", - gc_ptr => ~"@", - region_ptr(_, _) => ~"&", - unsafe_ptr(_) => ~"*" + OwnedPtr => "~", + GcPtr => "@", + BorrowedPtr(ty::ImmBorrow, _) => "&", + BorrowedPtr(ty::MutBorrow, _) => "&mut", + BorrowedPtr(ty::UniqueImmBorrow, _) => "&unique", + UnsafePtr(_) => "*" } } diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 232b35bb82afc..fcda7cd79e421 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -36,19 +36,42 @@ use syntax::ast_util::{stmt_id}; /** The region maps encode information about region relationships. -- `scope_map` maps from: - - an expression to the expression or block encoding the maximum - (static) lifetime of a value produced by that expression. This is - generally the innermost call, statement, match, or block. - - a variable or binding id to the block in which that variable is declared. -- `free_region_map` maps from: - - a free region `a` to a list of free regions `bs` such that - `a <= b for all b in bs` +- `scope_map` maps from a scope id to the enclosing scope id; this is + usually corresponding to the lexical nesting, though in the case of + closures the parent scope is the innermost conditinal expression or repeating + block + +- `var_map` maps from a variable or binding id to the block in which + that variable is declared. + +- `free_region_map` maps from a free region `a` to a list of free + regions `bs` such that `a <= b for all b in bs` - the free region map is populated during type check as we check each function. See the function `relate_free_regions` for more information. -- `temporary_scopes` includes scopes where cleanups for temporaries occur. - These are statements and loop/fn bodies. + +- `rvalue_scopes` includes entries for those expressions whose cleanup + scope is larger than the default. The map goes from the expression + id to the cleanup scope id. For rvalues not present in this table, + the appropriate cleanup scope is the innermost enclosing statement, + conditional expression, or repeating block (see `terminating_scopes`). + +- `terminating_scopes` is a set containing the ids of each statement, + or conditional/repeating expression. These scopes are calling "terminating + scopes" because, when attempting to find the scope of a temporary, by + default we search up the enclosing scopes until we encounter the + terminating scope. A conditional/repeating + expression is one which is not guaranteed to execute exactly once + upon entering the parent scope. This could be because the expression + only executes conditionally, such as the expression `b` in `a && b`, + or because the expression may execute many times, such as a loop + body. The reason that we distinguish such expressions is that, upon + exiting the parent scope, we cannot statically know how many times + the expression executed, and thus if the expression creates + temporaries we cannot know statically how many such temporaries we + would have to cleanup. Therefore we ensure that the temporaries never + outlast the conditional/repeating expression, preventing the need + for dynamic checks and/or arbitrary amounts of stack space. */ pub struct RegionMaps { priv scope_map: RefCell>, @@ -840,7 +863,16 @@ fn resolve_fn(visitor: &mut RegionResolutionVisitor, visit::FkItemFn(..) | visit::FkMethod(..) => { Context {parent: None, var_parent: None, ..cx} } - visit::FkFnBlock(..) => cx + visit::FkFnBlock(..) => { + // FIXME(#3696) -- at present we are place the closure body + // within the region hierarchy exactly where it appears lexically. + // This is wrong because the closure may live longer + // than the enclosing expression. We should probably fix this, + // but the correct fix is a bit subtle, and I am also not sure + // that the present approach is unsound -- it may not permit + // any illegal programs. See issue for more details. + cx + } }; visitor.visit_block(body, body_cx); } diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index a941c1318cab2..9623f6f0cbc85 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -2567,52 +2567,15 @@ impl Resolver { } } - let merge_import_resolution = |name, name_bindings: @NameBindings| { - let dest_import_resolution; - let mut import_resolutions = module_.import_resolutions - .borrow_mut(); - match import_resolutions.get().find(&name) { - None => { - // Create a new import resolution from this child. - dest_import_resolution = - @ImportResolution::new(id, is_public); - import_resolutions.get().insert(name, - dest_import_resolution); - } - Some(&existing_import_resolution) => { - dest_import_resolution = existing_import_resolution; - } - } - - debug!("(resolving glob import) writing resolution `{}` in `{}` \ - to `{}`", - token::get_ident(name).get().to_str(), - self.module_to_str(containing_module), - self.module_to_str(module_)); - - // Merge the child item into the import resolution. - if name_bindings.defined_in_public_namespace(ValueNS) { - debug!("(resolving glob import) ... for value target"); - dest_import_resolution.value_target.set( - Some(Target::new(containing_module, name_bindings))); - dest_import_resolution.value_id.set(id); - } - if name_bindings.defined_in_public_namespace(TypeNS) { - debug!("(resolving glob import) ... for type target"); - dest_import_resolution.type_target.set( - Some(Target::new(containing_module, name_bindings))); - dest_import_resolution.type_id.set(id); - } - dest_import_resolution.is_public.set(is_public); - }; - // Add all children from the containing module. self.populate_module_if_necessary(containing_module); { let children = containing_module.children.borrow(); for (&name, name_bindings) in children.get().iter() { - merge_import_resolution(name, *name_bindings); + self.merge_import_resolution(module_, containing_module, + id, is_public, + name, *name_bindings); } } @@ -2623,7 +2586,9 @@ impl Resolver { for (&name, module) in external_module_children.get().iter() { let name_bindings = @Resolver::create_name_bindings_from_module(*module); - merge_import_resolution(name, name_bindings); + self.merge_import_resolution(module_, containing_module, + id, is_public, + name, name_bindings); } } @@ -2641,6 +2606,50 @@ impl Resolver { return Success(()); } + fn merge_import_resolution(&mut self, + module_: @Module, + containing_module: @Module, + id: NodeId, + is_public: bool, + name: Name, + name_bindings: @NameBindings) { + let dest_import_resolution; + let mut import_resolutions = module_.import_resolutions.borrow_mut(); + match import_resolutions.get().find(&name) { + None => { + // Create a new import resolution from this child. + dest_import_resolution = + @ImportResolution::new(id, is_public); + import_resolutions.get().insert(name, + dest_import_resolution); + } + Some(&existing_import_resolution) => { + dest_import_resolution = existing_import_resolution; + } + } + + debug!("(resolving glob import) writing resolution `{}` in `{}` \ + to `{}`", + token::get_ident(name).get().to_str(), + self.module_to_str(containing_module), + self.module_to_str(module_)); + + // Merge the child item into the import resolution. + if name_bindings.defined_in_public_namespace(ValueNS) { + debug!("(resolving glob import) ... for value target"); + dest_import_resolution.value_target.set( + Some(Target::new(containing_module, name_bindings))); + dest_import_resolution.value_id.set(id); + } + if name_bindings.defined_in_public_namespace(TypeNS) { + debug!("(resolving glob import) ... for type target"); + dest_import_resolution.type_target.set( + Some(Target::new(containing_module, name_bindings))); + dest_import_resolution.type_id.set(id); + } + dest_import_resolution.is_public.set(is_public); + } + /// Resolves the given module path from the given root `module_`. fn resolve_module_path_from_root(&mut self, module_: @Module, diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs index e7ebc2ef526c4..329301efa5ebb 100644 --- a/src/librustc/middle/trans/datum.rs +++ b/src/librustc/middle/trans/datum.rs @@ -480,9 +480,9 @@ impl Datum { * no cleanup scheduled). */ - let mut bcx = bcx; self.match_kind( |l| { + let mut bcx = bcx; match l.appropriate_rvalue_mode(bcx.ccx()) { ByRef => { let scratch = rvalue_scratch_datum(bcx, l.ty, name); diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 7b2aee9274a7d..7c4cb396b3e26 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -45,6 +45,7 @@ use syntax::attr; use syntax::attr::AttrMetaMethods; use syntax::codemap::Span; use syntax::parse::token; +use syntax::parse::token::InternedString; use syntax::{ast, ast_map}; use syntax::opt_vec::OptVec; use syntax::opt_vec; @@ -350,6 +351,9 @@ pub struct ctxt_ { // is used for lazy resolution of traits. populated_external_traits: RefCell>, + // Borrows + upvar_borrow_map: RefCell, + // These two caches are used by const_eval when decoding external statics // and variants that are found. extern_const_statics: RefCell>>, @@ -493,6 +497,120 @@ pub enum Region { ReEmpty, } +/** + * Upvars do not get their own node-id. Instead, we use the pair of + * the original var id (that is, the root variable that is referenced + * by the upvar) and the id of the closure expression. + */ +#[deriving(Clone, Eq, IterBytes)] +pub struct UpvarId { + var_id: ast::NodeId, + closure_expr_id: ast::NodeId, +} + +#[deriving(Clone, Eq, IterBytes)] +pub enum BorrowKind { + /// Data must be immutable and is aliasable. + ImmBorrow, + + /// Data must be immutable but not aliasable. This kind of borrow + /// cannot currently be expressed by the user and is used only in + /// implicit closure bindings. It is needed when you the closure + /// is borrowing or mutating a mutable referent, e.g.: + /// + /// let x: &mut int = ...; + /// let y = || *x += 5; + /// + /// If we were to try to translate this closure into a more explicit + /// form, we'd encounter an error with the code as written: + /// + /// struct Env { x: & &mut int } + /// let x: &mut int = ...; + /// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// + /// This is then illegal because you cannot mutate a `&mut` found + /// in an aliasable location. To solve, you'd have to translate with + /// an `&mut` borrow: + /// + /// struct Env { x: & &mut int } + /// let x: &mut int = ...; + /// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x + /// fn fn_ptr(env: &mut Env) { **env.x += 5; } + /// + /// Now the assignment to `**env.x` is legal, but creating a + /// mutable pointer to `x` is not because `x` is not mutable. We + /// could fix this by declaring `x` as `let mut x`. This is ok in + /// user code, if awkward, but extra weird for closures, since the + /// borrow is hidden. + /// + /// So we introduce a "unique imm" borrow -- the referent is + /// immutable, but not aliasable. This solves the problem. For + /// simplicity, we don't give users the way to express this + /// borrow, it's just used when translating closures. + UniqueImmBorrow, + + /// Data is mutable and not aliasable. + MutBorrow +} + +/** + * Information describing the borrowing of an upvar. This is computed + * during `typeck`, specifically by `regionck`. The general idea is + * that the compiler analyses treat closures like: + * + * let closure: &'e fn() = || { + * x = 1; // upvar x is assigned to + * use(y); // upvar y is read + * foo(&z); // upvar z is borrowed immutably + * }; + * + * as if they were "desugared" to something loosely like: + * + * struct Vars<'x,'y,'z> { x: &'x mut int, + * y: &'y const int, + * z: &'z int } + * let closure: &'e fn() = { + * fn f(env: &Vars) { + * *env.x = 1; + * use(*env.y); + * foo(env.z); + * } + * let env: &'e mut Vars<'x,'y,'z> = &mut Vars { x: &'x mut x, + * y: &'y const y, + * z: &'z z }; + * (env, f) + * }; + * + * This is basically what happens at runtime. The closure is basically + * an existentially quantified version of the `(env, f)` pair. + * + * This data structure indicates the region and mutability of a single + * one of the `x...z` borrows. + * + * It may not be obvious why each borrowed variable gets its own + * lifetime (in the desugared version of the example, these are indicated + * by the lifetime parameters `'x`, `'y`, and `'z` in the `Vars` definition). + * Each such lifetime must encompass the lifetime `'e` of the closure itself, + * but need not be identical to it. The reason that this makes sense: + * + * - Callers are only permitted to invoke the closure, and hence to + * use the pointers, within the lifetime `'e`, so clearly `'e` must + * be a sublifetime of `'x...'z`. + * - The closure creator knows which upvars were borrowed by the closure + * and thus `x...z` will be reserved for `'x...'z` respectively. + * - Through mutation, the borrowed upvars can actually escape the + * the closure, so sometimes it is necessary for them to be larger + * than the closure lifetime itself. + */ +#[deriving(Eq, Clone)] +pub struct UpvarBorrow { + kind: BorrowKind, + region: ty::Region, +} + +pub type UpvarBorrowMap = HashMap; + impl Region { pub fn is_bound(&self) -> bool { match self { @@ -998,7 +1116,7 @@ pub fn mk_ctxt(s: session::Session, impl_vtables: RefCell::new(HashMap::new()), populated_external_types: RefCell::new(HashSet::new()), populated_external_traits: RefCell::new(HashSet::new()), - + upvar_borrow_map: RefCell::new(HashMap::new()), extern_const_statics: RefCell::new(HashMap::new()), extern_const_variants: RefCell::new(HashMap::new()), } @@ -2668,8 +2786,13 @@ pub fn node_id_to_trait_ref(cx: ctxt, id: ast::NodeId) -> @ty::TraitRef { } } +pub fn try_node_id_to_type(cx: ctxt, id: ast::NodeId) -> Option { + let node_types = cx.node_types.borrow(); + node_types.get().find_copy(&(id as uint)) +} + pub fn node_id_to_type(cx: ctxt, id: ast::NodeId) -> t { - match node_id_to_type_opt(cx, id) { + match try_node_id_to_type(cx, id) { Some(t) => t, None => cx.sess.bug( format!("node_id_to_type: no type for node `{}`", @@ -2883,6 +3006,45 @@ pub fn expr_ty_adjusted(cx: ctxt, expr: &ast::Expr) -> t { adjust_ty(cx, expr.span, unadjusted_ty, adjustment) } +pub fn expr_span(cx: ctxt, id: NodeId) -> Span { + match cx.items.find(id) { + Some(ast_map::NodeExpr(e)) => { + e.span + } + Some(f) => { + cx.sess.bug(format!("Node id {} is not an expr: {:?}", + id, f)); + } + None => { + cx.sess.bug(format!("Node id {} is not present \ + in the node map", id)); + } + } +} + +pub fn local_var_name_str(cx: ctxt, id: NodeId) -> InternedString { + match cx.items.find(id) { + Some(ast_map::NodeLocal(pat)) => { + match pat.node { + ast::PatIdent(_, ref path, _) => { + let ident = ast_util::path_to_ident(path); + token::get_ident(ident.name) + } + _ => { + cx.sess.bug( + format!("Variable id {} maps to {:?}, not local", + id, pat)); + } + } + } + r => { + cx.sess.bug( + format!("Variable id {} maps to {:?}, not local", + id, r)); + } + } +} + pub fn adjust_ty(cx: ctxt, span: Span, unadjusted_ty: ty::t, @@ -5055,3 +5217,28 @@ impl substs { } } } + +impl BorrowKind { + pub fn from_mutbl(m: ast::Mutability) -> BorrowKind { + match m { + ast::MutMutable => MutBorrow, + ast::MutImmutable => ImmBorrow, + } + } + + pub fn to_user_str(&self) -> &'static str { + match *self { + MutBorrow => "mutable", + ImmBorrow => "immutable", + UniqueImmBorrow => "uniquely immutable", + } + } + + pub fn to_short_str(&self) -> &'static str { + match *self { + MutBorrow => "mut", + ImmBorrow => "imm", + UniqueImmBorrow => "own", + } + } +} diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index e2e7a58f5231b..9eec804dd2e11 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -164,6 +164,7 @@ pub struct Inherited { adjustments: RefCell>, method_map: method_map, vtable_map: vtable_map, + upvar_borrow_map: RefCell, } #[deriving(Clone)] @@ -266,6 +267,7 @@ impl Inherited { adjustments: RefCell::new(HashMap::new()), method_map: @RefCell::new(HashMap::new()), vtable_map: @RefCell::new(HashMap::new()), + upvar_borrow_map: RefCell::new(HashMap::new()), } } } diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index ac15d52ff13b5..d3a0da4bbfd83 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -12,23 +12,115 @@ The region check is a final pass that runs over the AST after we have inferred the type constraints but before we have actually finalized -the types. Its purpose is to embed some final region constraints. -The reason that this is not done earlier is that sometimes we don't -know whether a given type will be a region pointer or not until this -phase. - -In particular, we ensure that, if the type of an expression or -variable is `&'r T`, then the expression or variable must occur within -the region scope `r`. Note that in some cases `r` may still be a -region variable, so this gives us a chance to influence the value for -`r` that we infer to ensure we choose a value large enough to enclose -all uses. There is a lengthy comment in visit_node() that explains -this point a bit better. +the types. Its purpose is to embed a variety of region constraints. +Inserting these constraints as a separate pass is good because (1) it +localizes the code that has to do with region inference and (2) often +we cannot know what constraints are needed until the basic types have +been inferred. + +### Interaction with the borrow checker + +In general, the job of the borrowck module (which runs later) is to +check that all soundness criteria are met, given a particular set of +regions. The job of *this* module is to anticipate the needs of the +borrow checker and infer regions that will satisfy its requirements. +It is generally true that the inference doesn't need to be sound, +meaning that if there is a bug and we inferred bad regions, the borrow +checker should catch it. This is not entirely true though; for +example, the borrow checker doesn't check subtyping, and it doesn't +check that region pointers are always live when they are used. It +might be worthwhile to fix this so that borrowck serves as a kind of +verification step -- that would add confidence in the overall +correctness of the compiler, at the cost of duplicating some type +checks and effort. + +### Inferring the duration of borrows, automatic and otherwise + +Whenever we introduce a borrowed pointer, for example as the result of +a borrow expression `let x = &data`, the lifetime of the pointer `x` +is always specified as a region inference variable. `regionck` has the +job of adding constraints such that this inference variable is as +narrow as possible while still accommodating all uses (that is, every +dereference of the resulting pointer must be within the lifetime). + +#### Reborrows + +Generally speaking, `regionck` does NOT try to ensure that the data +`data` will outlive the pointer `x`. That is the job of borrowck. The +one exception is when "re-borrowing" the contents of another borrowed +pointer. For example, imagine you have a borrowed pointer `b` with +lifetime L1 and you have an expression `&*b`. The result of this +expression will be another borrowed pointer with lifetime L2 (which is +an inference variable). The borrow checker is going to enforce the +constraint that L2 < L1, because otherwise you are re-borrowing data +for a lifetime larger than the original loan. However, without the +routines in this module, the region inferencer would not know of this +dependency and thus it might infer the lifetime of L2 to be greater +than L1 (issue #3148). + +There are a number of troublesome scenarios in the tests +`region-dependent-*.rs`, but here is one example: + + struct Foo { i: int } + struct Bar { foo: Foo } + fn get_i(x: &'a Bar) -> &'a int { + let foo = &x.foo; // Lifetime L1 + &foo.i // Lifetime L2 + } + +Note that this comes up either with `&` expressions, `ref` +bindings, and `autorefs`, which are the three ways to introduce +a borrow. + +The key point here is that when you are borrowing a value that +is "guaranteed" by a borrowed pointer, you must link the +lifetime of that borrowed pointer (L1, here) to the lifetime of +the borrow itself (L2). What do I mean by "guaranteed" by a +borrowed pointer? I mean any data that is reached by first +dereferencing a borrowed pointer and then either traversing +interior offsets or owned pointers. We say that the guarantor +of such data it the region of the borrowed pointer that was +traversed. This is essentially the same as the ownership +relation, except that a borrowed pointer never owns its +contents. + +### Inferring borrow kinds for upvars + +Whenever there is a closure expression, we need to determine how each +upvar is used. We do this by initially assigning each upvar an +immutable "borrow kind" (see `ty::BorrowKind` for details) and then +"escalating" the kind as needed. The borrow kind proceeds according to +the following lattice: + + ty::ImmBorrow -> ty::UniqueImmBorrow -> ty::MutBorrow + +So, for example, if we see an assignment `x = 5` to an upvar `x`, we +will promote its borrow kind to mutable borrow. If we see an `&mut x` +we'll do the same. Naturally, this applies not just to the upvar, but +to everything owned by `x`, so the result is the same for something +like `x.f = 5` and so on (presuming `x` is not a borrowed pointer to a +struct). These adjustments are performed in +`adjust_upvar_borrow_kind()` (you can trace backwards through the code +from there). + +The fact that we are inferring borrow kinds as we go results in a +semi-hacky interaction with mem-categorization. In particular, +mem-categorization will query the current borrow kind as it +categorizes, and we'll return the *current* value, but this may get +adjusted later. Therefore, in this module, we generally ignore the +borrow kind (and derived mutabilities) that are returned from +mem-categorization, since they may be inaccurate. (Another option +would be to use a unification scheme, where instead of returning a +concrete borrow kind like `ty::ImmBorrow`, we return a +`ty::InferBorrow(upvar_id)` or something like that, but this would +then mean that all later passes would have to check for these figments +and report an error, and it just seems like more mess in the end.) */ -use middle::freevars::get_freevars; +use middle::freevars; +use mc = middle::mem_categorization; use middle::ty::{ReScope}; use middle::ty; use middle::typeck::astconv::AstConv; @@ -43,6 +135,7 @@ use util::ppaux::{ty_to_str, region_to_str, Repr}; use syntax::ast::{ManagedSigil, OwnedSigil, BorrowedSigil}; use syntax::ast::{DefArg, DefBinding, DefLocal, DefUpvar}; use syntax::ast; +use syntax::ast_util; use syntax::codemap::Span; use syntax::visit; use syntax::visit::Visitor; @@ -149,6 +242,36 @@ impl Rcx { } } +impl<'a> mc::Typer for &'a mut Rcx { + fn tcx(&self) -> ty::ctxt { + self.fcx.tcx() + } + + fn node_ty(&mut self, id: ast::NodeId) -> mc::McResult { + let t = self.resolve_node_type(id); + if ty::type_is_error(t) {Err(())} else {Ok(t)} + } + + fn adjustment(&mut self, id: ast::NodeId) -> Option<@ty::AutoAdjustment> { + let adjustments = self.fcx.inh.adjustments.borrow(); + adjustments.get().find_copy(&id) + } + + fn is_method_call(&mut self, id: ast::NodeId) -> bool { + let method_map = self.fcx.inh.method_map.borrow(); + method_map.get().contains_key(&id) + } + + fn temporary_scope(&mut self, id: ast::NodeId) -> Option { + self.tcx().region_maps.temporary_scope(id) + } + + fn upvar_borrow(&mut self, id: ty::UpvarId) -> ty::UpvarBorrow { + let upvar_borrow_map = self.fcx.inh.upvar_borrow_map.borrow(); + upvar_borrow_map.get().get_copy(&id) + } +} + pub fn regionck_expr(fcx: @FnCtxt, e: &ast::Expr) { let mut rcx = Rcx { fcx: fcx, errors_reported: 0, repeating_scope: e.id }; @@ -213,7 +336,7 @@ fn visit_arm(rcx: &mut Rcx, arm: &ast::Arm) { fn visit_local(rcx: &mut Rcx, l: &ast::Local) { // see above constrain_bindings_in_pat(l.pat, rcx); - guarantor::for_local(rcx, l); + link_local(rcx, l); visit::walk_local(rcx, l, ()); } @@ -273,7 +396,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { let expr_ty = rcx.resolve_node_type(expr.id); constrain_derefs(rcx, expr, autoderefs, expr_ty); for autoref in opt_autoref.iter() { - guarantor::for_autoref(rcx, expr, autoderefs, autoref); + link_autoref(rcx, expr, autoderefs, autoref); // Require that the resulting region encompasses // the current node. @@ -323,8 +446,22 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { visit::walk_expr(rcx, expr, ()); } + ast::ExprAssign(lhs, _) => { + adjust_borrow_kind_for_assignment_lhs(rcx, lhs); + visit::walk_expr(rcx, expr, ()); + } + + ast::ExprAssignOp(callee_id, _, lhs, rhs) => { + if has_method_map { + constrain_call(rcx, callee_id, expr, Some(lhs), [rhs], true); + } + + adjust_borrow_kind_for_assignment_lhs(rcx, lhs); + + visit::walk_expr(rcx, expr, ()); + } + ast::ExprIndex(callee_id, lhs, rhs) | - ast::ExprAssignOp(callee_id, _, lhs, rhs) | ast::ExprBinary(callee_id, _, lhs, rhs) if has_method_map => { // As `expr_method_call`, but the call is via an // overloaded op. Note that we (sadly) currently use an @@ -388,8 +525,8 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { visit::walk_expr(rcx, expr, ()); } - ast::ExprAddrOf(_, base) => { - guarantor::for_addr_of(rcx, expr, base); + ast::ExprAddrOf(m, base) => { + link_addr_of(rcx, expr, m, base); // Require that when you write a `&expr` expression, the // resulting pointer has a lifetime that encompasses the @@ -405,13 +542,13 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { } ast::ExprMatch(discr, ref arms) => { - guarantor::for_match(rcx, discr, *arms); + link_match(rcx, discr, *arms); visit::walk_expr(rcx, expr, ()); } - ast::ExprFnBlock(..) | ast::ExprProc(..) => { - check_expr_fn_block(rcx, expr); + ast::ExprFnBlock(_, ref body) | ast::ExprProc(_, ref body) => { + check_expr_fn_block(rcx, expr, &**body); } ast::ExprLoop(body, _) => { @@ -437,43 +574,136 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { } fn check_expr_fn_block(rcx: &mut Rcx, - expr: &ast::Expr) { + expr: &ast::Expr, + body: &ast::Block) { let tcx = rcx.fcx.tcx(); - match expr.node { - ast::ExprFnBlock(_, ref body) | ast::ExprProc(_, ref body) => { - let function_type = rcx.resolve_node_type(expr.id); - match ty::get(function_type).sty { - ty::ty_closure( - ty::ClosureTy { - sigil: ast::BorrowedSigil, region: region, ..}) => { - if get_freevars(tcx, expr.id).is_empty() { - // No free variables means that the environment - // will be NULL at runtime and hence the closure - // has static lifetime. - } else { - // Otherwise, the closure must not outlive the - // variables it closes over, nor can it - // outlive the innermost repeating scope - // (since otherwise that would require - // infinite stack). - constrain_free_variables(rcx, region, expr); - let repeating_scope = ty::ReScope(rcx.repeating_scope); - rcx.fcx.mk_subr(true, infer::InfStackClosure(expr.span), - region, repeating_scope); - } - } - _ => () + let function_type = rcx.resolve_node_type(expr.id); + match ty::get(function_type).sty { + ty::ty_closure(ty::ClosureTy { + sigil: ast::BorrowedSigil, region: region, ..}) => { + let freevars = freevars::get_freevars(tcx, expr.id); + if freevars.is_empty() { + // No free variables means that the environment + // will be NULL at runtime and hence the closure + // has static lifetime. + } else { + // Closure must not outlive the variables it closes over. + constrain_free_variables(rcx, region, expr, freevars); + + // Closure cannot outlive the appropriate temporary scope. + let s = rcx.repeating_scope; + rcx.fcx.mk_subr(true, infer::InfStackClosure(expr.span), + region, ty::ReScope(s)); } + } + _ => () + } - let repeating_scope = rcx.set_repeating_scope(body.id); - visit::walk_expr(rcx, expr, ()); - rcx.set_repeating_scope(repeating_scope); + let repeating_scope = rcx.set_repeating_scope(body.id); + visit::walk_expr(rcx, expr, ()); + rcx.set_repeating_scope(repeating_scope); + + match ty::get(function_type).sty { + ty::ty_closure(ty::ClosureTy {sigil: ast::BorrowedSigil, ..}) => { + let freevars = freevars::get_freevars(tcx, expr.id); + propagate_upupvar_borrow_kind(rcx, expr, freevars); } + _ => () + } - _ => { - tcx.sess.span_bug( - expr.span, - "expected expr_fn_block"); + fn constrain_free_variables(rcx: &mut Rcx, + region: ty::Region, + expr: &ast::Expr, + freevars: freevars::freevar_info) { + /*! + * Make sure that all free variables referenced inside the closure + * outlive the closure itself. Also, create an entry in the + * upvar_borrows map with a region. + */ + + let tcx = rcx.fcx.ccx.tcx; + let infcx = rcx.fcx.infcx(); + debug!("constrain_free_variables({}, {})", + region.repr(tcx), expr.repr(tcx)); + for freevar in freevars.iter() { + debug!("freevar def is {:?}", freevar.def); + + // Identify the variable being closed over and its node-id. + let def = freevar.def; + let def_id = ast_util::def_id_of_def(def); + assert!(def_id.crate == ast::LOCAL_CRATE); + let upvar_id = ty::UpvarId { var_id: def_id.node, + closure_expr_id: expr.id }; + + // Create a region variable to represent this borrow. This borrow + // must outlive the region on the closure. + let origin = infer::UpvarRegion(upvar_id, expr.span); + let freevar_region = infcx.next_region_var(origin); + rcx.fcx.mk_subr(true, infer::FreeVariable(freevar.span, def_id.node), + region, freevar_region); + + // Create a UpvarBorrow entry. Note that we begin with a + // const borrow_kind, but change it to either mut or + // immutable as dictated by the uses. + let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow, + region: freevar_region }; + let mut upvar_borrow_map = rcx.fcx.inh.upvar_borrow_map.borrow_mut(); + upvar_borrow_map.get().insert(upvar_id, upvar_borrow); + + // Guarantee that the closure does not outlive the variable itself. + let en_region = region_of_def(rcx.fcx, def); + debug!("en_region = {}", en_region.repr(tcx)); + rcx.fcx.mk_subr(true, infer::FreeVariable(freevar.span, def_id.node), + region, en_region); + } + } + + fn propagate_upupvar_borrow_kind(rcx: &mut Rcx, + expr: &ast::Expr, + freevars: freevars::freevar_info) { + let tcx = rcx.fcx.ccx.tcx; + debug!("propagate_upupvar_borrow_kind({})", expr.repr(tcx)); + for freevar in freevars.iter() { + // Because of the semi-hokey way that we are doing + // borrow_kind inference, we need to check for + // indirect dependencies, like so: + // + // let mut x = 0; + // outer_call(|| { + // inner_call(|| { + // x = 1; + // }); + // }); + // + // Here, the `inner_call` is basically "reborrowing" the + // outer pointer. With no other changes, `inner_call` + // would infer that it requires a mutable borrow, but + // `outer_call` would infer that a const borrow is + // sufficient. This is because we haven't linked the + // borrow_kind of the borrow that occurs in the inner + // closure to the borrow_kind of the borrow in the outer + // closure. Note that regions *are* naturally linked + // because we have a proper inference scheme there. + // + // Anyway, for borrow_kind, we basically go back over now + // after checking the inner closure (and hence + // determining the final borrow_kind) and propagate that as + // a constraint on the outer closure. + match freevar.def { + ast::DefUpvar(var_id, _, outer_closure_id, _) => { + // thing being captured is itself an upvar: + let outer_upvar_id = ty::UpvarId { + var_id: var_id, + closure_expr_id: outer_closure_id }; + let inner_upvar_id = ty::UpvarId { + var_id: var_id, + closure_expr_id: expr.id }; + link_upvar_borrow_kind_for_nested_closures(rcx, + inner_upvar_id, + outer_upvar_id); + } + _ => {} + } } } } @@ -554,7 +784,7 @@ fn constrain_call(rcx: &mut Rcx, // result. modes are going away and the "DerefArgs" code // should be ported to use adjustments if implicitly_ref_args { - guarantor::for_by_ref(rcx, arg_expr, callee_scope); + link_by_ref(rcx, arg_expr, callee_scope); } } @@ -564,7 +794,7 @@ fn constrain_call(rcx: &mut Rcx, constrain_regions_in_type_of_node( rcx, r.id, callee_region, infer::CallRcvr(r.span)); if implicitly_ref_args { - guarantor::for_by_ref(rcx, r, callee_scope); + link_by_ref(rcx, r, callee_scope); } } @@ -644,27 +874,6 @@ fn constrain_index(rcx: &mut Rcx, } } -fn constrain_free_variables(rcx: &mut Rcx, - region: ty::Region, - expr: &ast::Expr) { - /*! - * Make sure that all free variables referenced inside the closure - * outlive the closure itself. - */ - - let tcx = rcx.fcx.ccx.tcx; - debug!("constrain_free_variables({}, {})", - region.repr(tcx), expr.repr(tcx)); - for freevar in get_freevars(tcx, expr.id).iter() { - debug!("freevar def is {:?}", freevar.def); - let def = freevar.def; - let def_region = region_of_def(rcx.fcx, def); - debug!("def_region = {}", def_region.repr(tcx)); - rcx.fcx.mk_subr(true, infer::FreeVariable(freevar.span), - region, def_region); - } -} - fn constrain_regions_in_type_of_node( rcx: &mut Rcx, id: ast::NodeId, @@ -744,576 +953,471 @@ fn constrain_regions_in_type( return e == rcx.errors_reported; } -pub mod guarantor { +// If mem categorization results in an error, it's because the type +// check failed (or will fail, when the error is uncovered and +// reported during writeback). In this case, we just ignore this part +// of the code and don't try to add any more region constraints. +macro_rules! ignore_err( + ($inp: expr) => ( + match $inp { + Ok(v) => { v } + Err(()) => { return; } + } + ) +) + +fn link_addr_of(rcx: &mut Rcx, expr: &ast::Expr, + mutability: ast::Mutability, base: &ast::Expr) { /*! - * The routines in this module are aiming to deal with the case - * where a the contents of a reference are re-borrowed. - * Imagine you have a reference `b` with lifetime L1 and - * you have an expression `&*b`. The result of this borrow will - * be another reference with lifetime L2 (which is an - * inference variable). The borrow checker is going to enforce - * the constraint that L2 < L1, because otherwise you are - * re-borrowing data for a lifetime larger than the original loan. - * However, without the routines in this module, the region - * inferencer would not know of this dependency and thus it might - * infer the lifetime of L2 to be greater than L1 (issue #3148). - * - * There are a number of troublesome scenarios in the tests - * `region-dependent-*.rs`, but here is one example: - * - * struct Foo { i: int } - * struct Bar { foo: Foo } - * fn get_i(x: &'a Bar) -> &'a int { - * let foo = &x.foo; // Lifetime L1 - * &foo.i // Lifetime L2 - * } - * - * Note that this comes up either with `&` expressions, `ref` - * bindings, and `autorefs`, which are the three ways to introduce - * a borrow. - * - * The key point here is that when you are borrowing a value that - * is "guaranteed" by a reference, you must link the - * lifetime of that reference (L1, here) to the lifetime of - * the borrow itself (L2). What do I mean by "guaranteed" by a - * reference? I mean any data that is reached by first - * dereferencing a reference and then either traversing - * interior offsets or owned pointers. We say that the guarantor - * of such data it the region of the reference that was - * traversed. This is essentially the same as the ownership - * relation, except that a reference never owns its - * contents. - * - * NB: I really wanted to use the `mem_categorization` code here - * but I cannot because final type resolution hasn't happened yet, - * and `mem_categorization` requires that all types be known. - * So this is very similar logic to what you would find there, - * but more special purpose. + * Computes the guarantor for an expression `&base` and then + * ensures that the lifetime of the resulting pointer is linked + * to the lifetime of its guarantor (if any). */ - use middle::typeck::astconv::AstConv; - use middle::typeck::check::regionck::Rcx; - use middle::typeck::check::regionck::mk_subregion_due_to_derefence; - use middle::typeck::infer; - use middle::ty; - use syntax::ast; - use syntax::codemap::Span; - use util::ppaux::{ty_to_str, Repr}; + debug!("link_addr_of(base=?)"); - pub fn for_addr_of(rcx: &mut Rcx, expr: &ast::Expr, base: &ast::Expr) { - /*! - * Computes the guarantor for an expression `&base` and then - * ensures that the lifetime of the resulting pointer is linked - * to the lifetime of its guarantor (if any). - */ + let cmt = { + let mut mc = mc::MemCategorizationContext { typer: &mut *rcx }; + ignore_err!(mc.cat_expr(base)) + }; + link_region_from_node_type(rcx, expr.span, expr.id, mutability, cmt); +} - debug!("guarantor::for_addr_of(base=?)"); +fn link_local(rcx: &mut Rcx, local: &ast::Local) { + /*! + * Computes the guarantors for any ref bindings in a `let` and + * then ensures that the lifetime of the resulting pointer is + * linked to the lifetime of the initialization expression. + */ - let guarantor = guarantor(rcx, base); - link(rcx, expr.span, expr.id, guarantor); - } + debug!("regionck::for_local()"); + let init_expr = match local.init { + None => { return; } + Some(expr) => expr, + }; + let mut mc = mc::MemCategorizationContext { typer: rcx }; + let discr_cmt = ignore_err!(mc.cat_expr(init_expr)); + link_pattern(&mut mc, discr_cmt, local.pat); +} - pub fn for_match(rcx: &mut Rcx, discr: &ast::Expr, arms: &[ast::Arm]) { - /*! - * Computes the guarantors for any ref bindings in a match and - * then ensures that the lifetime of the resulting pointer is - * linked to the lifetime of its guarantor (if any). - */ +fn link_match(rcx: &mut Rcx, discr: &ast::Expr, arms: &[ast::Arm]) { + /*! + * Computes the guarantors for any ref bindings in a match and + * then ensures that the lifetime of the resulting pointer is + * linked to the lifetime of its guarantor (if any). + */ - debug!("regionck::for_match()"); - let discr_guarantor = guarantor(rcx, discr); - debug!("discr_guarantor={}", discr_guarantor.repr(rcx.tcx())); - for arm in arms.iter() { - for pat in arm.pats.iter() { - link_ref_bindings_in_pat(rcx, *pat, discr_guarantor); - } + debug!("regionck::for_match()"); + let mut mc = mc::MemCategorizationContext { typer: rcx }; + let discr_cmt = ignore_err!(mc.cat_expr(discr)); + debug!("discr_cmt={}", discr_cmt.repr(mc.typer.tcx())); + for arm in arms.iter() { + for &root_pat in arm.pats.iter() { + link_pattern(&mut mc, discr_cmt, root_pat); } } +} - pub fn for_local(rcx: &mut Rcx, local: &ast::Local) { - /*! - * Link the lifetimes of any ref bindings in a let - * pattern to the lifetimes in the initializer. - * - * For example, given something like this: - * - * let &Foo(ref x) = ...; - * - * this would ensure that the lifetime 'a of the - * region pointer being matched must be >= the lifetime - * of the ref binding. - */ - - debug!("regionck::for_match()"); - let init_expr = match local.init { - None => { return; } - Some(e) => e - }; - let init_guarantor = guarantor(rcx, init_expr); - debug!("init_guarantor={}", init_guarantor.repr(rcx.tcx())); - link_ref_bindings_in_pat(rcx, local.pat, init_guarantor); - } - - pub fn for_autoref(rcx: &mut Rcx, - expr: &ast::Expr, - autoderefs: uint, - autoref: &ty::AutoRef) { - /*! - * Computes the guarantor for an expression that has an - * autoref adjustment and links it to the lifetime of the - * autoref. This is only important when auto re-borrowing - * region pointers. - */ - - debug!("guarantor::for_autoref(autoref={:?})", autoref); +fn link_pattern(mc: &mut mc::MemCategorizationContext<&mut Rcx>, + discr_cmt: mc::cmt, + root_pat: @ast::Pat) { + /*! + * Link lifetimes of any ref bindings in `root_pat` to + * the pointers found in the discriminant, if needed. + */ - let mut expr_ct = categorize_unadjusted(rcx, expr); - debug!(" unadjusted cat={:?}", expr_ct.cat); - expr_ct = apply_autoderefs( - rcx, expr, autoderefs, expr_ct); + let _ = mc.cat_pattern(discr_cmt, root_pat, |mc, sub_cmt, sub_pat| { + match sub_pat.node { + // `ref x` pattern + ast::PatIdent(ast::BindByRef(mutbl), _, _) => { + link_region_from_node_type( + mc.typer, sub_pat.span, sub_pat.id, + mutbl, sub_cmt); + } - match *autoref { - ty::AutoPtr(r, _) => { - // In this case, we are implicitly adding an `&`. - maybe_make_subregion(rcx, expr, r, expr_ct.cat.guarantor); + // `[_, ..slice, _]` pattern + ast::PatVec(_, Some(slice_pat), _) => { + match mc.cat_slice_pattern(sub_cmt, slice_pat) { + Ok((slice_cmt, slice_mutbl, slice_r)) => { + link_region(mc.typer, sub_pat.span, slice_r, + slice_mutbl, slice_cmt); + } + Err(()) => {} + } + } + _ => {} } + }); +} - ty::AutoBorrowVec(r, _) | - ty::AutoBorrowVecRef(r, _) | - ty::AutoBorrowFn(r) | - ty::AutoBorrowObj(r, _) => { - // In each of these cases, what is being borrowed is - // not the (autoderef'd) expr itself but rather the - // contents of the autoderef'd expression (i.e., what - // the pointer points at). - maybe_make_subregion(rcx, expr, r, - guarantor_of_deref(&expr_ct.cat)); - } +fn link_autoref(rcx: &mut Rcx, + expr: &ast::Expr, + autoderefs: uint, + autoref: &ty::AutoRef) { + /*! + * Link lifetime of borrowed pointer resulting from autoref + * to lifetimes in the value being autoref'd. + */ - ty::AutoUnsafe(_) => {} + debug!("link_autoref(autoref={:?})", autoref); + let mut mc = mc::MemCategorizationContext { typer: rcx }; + let expr_cmt = ignore_err!(mc.cat_expr_autoderefd(expr, autoderefs)); + debug!("expr_cmt={}", expr_cmt.repr(mc.typer.tcx())); + + match *autoref { + ty::AutoPtr(r, m) => { + link_region(mc.typer, expr.span, r, m, expr_cmt); } - fn maybe_make_subregion( - rcx: &mut Rcx, - expr: &ast::Expr, - sub_region: ty::Region, - sup_region: Option) - { - for r in sup_region.iter() { - rcx.fcx.mk_subr(true, infer::Reborrow(expr.span), - sub_region, *r); - } + ty::AutoBorrowVec(r, m) | ty::AutoBorrowVecRef(r, m) => { + let cmt_index = mc.cat_index(expr, expr_cmt, autoderefs+1); + link_region(mc.typer, expr.span, r, m, cmt_index); } - } - pub fn for_by_ref(rcx: &mut Rcx, - expr: &ast::Expr, - callee_scope: ast::NodeId) { - /*! - * Computes the guarantor for cases where the `expr` is - * being passed by implicit reference and must outlive - * `callee_scope`. - */ + ty::AutoBorrowFn(r) => { + let cmt_deref = mc.cat_deref_fn_or_obj(expr, expr_cmt, 0); + link_region(mc.typer, expr.span, r, ast::MutImmutable, cmt_deref); + } - let tcx = rcx.tcx(); - debug!("guarantor::for_by_ref(expr={}, callee_scope={:?})", - expr.repr(tcx), callee_scope); - let expr_cat = categorize(rcx, expr); - debug!("guarantor::for_by_ref(expr={:?}, callee_scope={:?}) category={:?}", - expr.id, callee_scope, expr_cat); - let minimum_lifetime = ty::ReScope(callee_scope); - for guarantor in expr_cat.guarantor.iter() { - mk_subregion_due_to_derefence(rcx, expr.span, - minimum_lifetime, *guarantor); + ty::AutoBorrowObj(r, m) => { + let cmt_deref = mc.cat_deref_fn_or_obj(expr, expr_cmt, 0); + link_region(mc.typer, expr.span, r, m, cmt_deref); } + + ty::AutoUnsafe(_) => {} } +} - fn link( - rcx: &mut Rcx, - span: Span, - id: ast::NodeId, - guarantor: Option) { - /*! - * - * Links the lifetime of the reference resulting from a borrow - * to the lifetime of its guarantor (if any). - */ +fn link_by_ref(rcx: &mut Rcx, + expr: &ast::Expr, + callee_scope: ast::NodeId) { + /*! + * Computes the guarantor for cases where the `expr` is + * being passed by implicit reference and must outlive + * `callee_scope`. + */ - debug!("link(id={:?}, guarantor={:?})", id, guarantor); + let tcx = rcx.tcx(); + debug!("link_by_ref(expr={}, callee_scope={})", + expr.repr(tcx), callee_scope); + let mut mc = mc::MemCategorizationContext { typer: rcx }; + let expr_cmt = ignore_err!(mc.cat_expr(expr)); + let region_min = ty::ReScope(callee_scope); + link_region(mc.typer, expr.span, region_min, ast::MutImmutable, expr_cmt); +} - let bound = match guarantor { - None => { - // If guarantor is None, then the value being borrowed - // is not guaranteed by a region pointer, so there are - // no lifetimes to link. - return; - } - Some(r) => { r } - }; - - // this routine is used for the result of ref bindings and & - // expressions, both of which always yield a region variable, so - // mk_subr should never fail. - let rptr_ty = rcx.resolve_node_type(id); - if !ty::type_is_bot(rptr_ty) { - let tcx = rcx.fcx.ccx.tcx; - debug!("rptr_ty={}", ty_to_str(tcx, rptr_ty)); - let r = ty::ty_region(tcx, span, rptr_ty); - rcx.fcx.mk_subr(true, infer::Reborrow(span), r, bound); - } - } +fn link_region_from_node_type(rcx: &mut Rcx, + span: Span, + id: ast::NodeId, + mutbl: ast::Mutability, + cmt_borrowed: mc::cmt) { + /*! + * Like `link_region()`, except that the region is + * extracted from the type of `id`, which must be some + * reference (`&T`, `&str`, etc). + */ - /// Categorizes types based on what kind of pointer they are. - /// Note that we don't bother to distinguish between rptrs (&T) - /// and slices (&[T], &str)---they are all just `BorrowedPointer`. - enum PointerCategorization { - NotPointer, - OwnedPointer, - BorrowedPointer(ty::Region), - OtherPointer + let rptr_ty = rcx.resolve_node_type(id); + if !ty::type_is_bot(rptr_ty) && !ty::type_is_error(rptr_ty) { + let tcx = rcx.fcx.ccx.tcx; + debug!("rptr_ty={}", ty_to_str(tcx, rptr_ty)); + let r = ty::ty_region(tcx, span, rptr_ty); + link_region(rcx, span, r, mutbl, cmt_borrowed); } +} - /// Guarantor of an expression paired with the - /// PointerCategorization` of its type. - struct ExprCategorization { - guarantor: Option, - pointer: PointerCategorization - } +fn link_region(rcx: &mut Rcx, + span: Span, + region_min: ty::Region, + mutbl: ast::Mutability, + cmt_borrowed: mc::cmt) { + /*! + * Informs the inference engine that a borrow of `cmt` + * must have mutability `mutbl` and lifetime `region_min`. + * If `cmt` is a deref of a region pointer with + * lifetime `r_borrowed`, this will add the constraint that + * `region_min <= r_borrowed`. + */ - /// ExprCategorization paired with the full type of the expr - struct ExprCategorizationType { - cat: ExprCategorization, - ty: ty::t - } + // Iterate through all the things that must be live at least + // for the lifetime `region_min` for the borrow to be valid: + let mut cmt_borrowed = cmt_borrowed; + loop { + debug!("link_region(region_min={}, mutbl={}, cmt_borrowed={})", + region_min.repr(rcx.tcx()), + mutbl.repr(rcx.tcx()), + cmt_borrowed.repr(rcx.tcx())); + match cmt_borrowed.cat { + mc::cat_deref(base, _, mc::BorrowedPtr(_, r_borrowed)) => { + // References to an upvar `x` are translated to + // `*x`, since that is what happens in the + // underlying machine. We detect such references + // and treat them slightly differently, both to + // offer better error messages and because we need + // to infer the kind of borrow (mut, const, etc) + // to use for each upvar. + let cause = match base.cat { + mc::cat_upvar(ref upvar_id, _) => { + let mut upvar_borrow_map = + rcx.fcx.inh.upvar_borrow_map.borrow_mut(); + match upvar_borrow_map.get().find_mut(upvar_id) { + Some(upvar_borrow) => { + debug!("link_region: {} <= {}", + region_min.repr(rcx.tcx()), + upvar_borrow.region.repr(rcx.tcx())); + adjust_upvar_borrow_kind_for_loan( + *upvar_id, + upvar_borrow, + mutbl); + infer::ReborrowUpvar(span, *upvar_id) + } + None => { + rcx.tcx().sess.span_bug( + span, + format!("Illegal upvar id: {}", + upvar_id.repr(rcx.tcx()))); + } + } + } - fn guarantor(rcx: &mut Rcx, expr: &ast::Expr) -> Option { - /*! - * - * Computes the guarantor of `expr`, or None if `expr` is - * not guaranteed by any region. Here `expr` is some expression - * whose address is being taken (e.g., there is an expression - * `&expr`). - */ + _ => { + infer::Reborrow(span) + } + }; - debug!("guarantor()"); - match expr.node { - ast::ExprUnary(_, ast::UnDeref, b) => { - let cat = categorize(rcx, b); - guarantor_of_deref(&cat) - } - ast::ExprField(b, _, _) => { - categorize(rcx, b).guarantor - } - ast::ExprIndex(_, b, _) => { - let cat = categorize(rcx, b); - guarantor_of_deref(&cat) - } + debug!("link_region: {} <= {}", + region_min.repr(rcx.tcx()), + r_borrowed.repr(rcx.tcx())); + rcx.fcx.mk_subr(true, cause, region_min, r_borrowed); + + if mutbl == ast::MutMutable { + // If this is a mutable borrow, then the thing + // being borrowed will have to be unique. + // In user code, this means it must be an `&mut` + // borrow, but for an upvar, we might opt + // for an immutable-unique borrow. + adjust_upvar_borrow_kind_for_unique(rcx, base); + } - ast::ExprParen(e) => { - guarantor(rcx, e) + // Borrowing an `&mut` pointee for `region_min` is + // only valid if the pointer resides in a unique + // location which is itself valid for + // `region_min`. We don't care about the unique + // part, but we may need to influence the + // inference to ensure that the location remains + // valid. + // + // FIXME(#8624) fixing borrowck will require this + // if m == ast::m_mutbl { + // cmt_borrowed = cmt_base; + // } else { + // return; + // } + return; } - - // Either a variable or constant and hence resides - // in constant memory or on the stack frame. Either way, - // not guaranteed by a region pointer. - ast::ExprPath(..) => None, - - // All of these expressions are rvalues and hence their - // value is not guaranteed by a region pointer. - ast::ExprInlineAsm(..) | - ast::ExprMac(..) | - ast::ExprLit(_) | - ast::ExprUnary(..) | - ast::ExprAddrOf(..) | - ast::ExprBinary(..) | - ast::ExprVstore(..) | - ast::ExprBox(..) | - ast::ExprBreak(..) | - ast::ExprAgain(..) | - ast::ExprRet(..) | - ast::ExprLogLevel | - ast::ExprWhile(..) | - ast::ExprLoop(..) | - ast::ExprAssign(..) | - ast::ExprAssignOp(..) | - ast::ExprCast(..) | - ast::ExprCall(..) | - ast::ExprMethodCall(..) | - ast::ExprStruct(..) | - ast::ExprTup(..) | - ast::ExprIf(..) | - ast::ExprMatch(..) | - ast::ExprFnBlock(..) | - ast::ExprProc(..) | - ast::ExprBlock(..) | - ast::ExprRepeat(..) | - ast::ExprVec(..) => { - assert!(!ty::expr_is_lval( - rcx.fcx.tcx(), rcx.fcx.inh.method_map, expr)); - None + mc::cat_discr(cmt_base, _) | + mc::cat_downcast(cmt_base) | + mc::cat_deref(cmt_base, _, mc::OwnedPtr) | + mc::cat_interior(cmt_base, _) => { + // Interior or owned data requires its base to be valid + cmt_borrowed = cmt_base; + } + mc::cat_deref(_, _, mc::GcPtr(..)) | + mc::cat_deref(_, _, mc::UnsafePtr(..)) | + mc::cat_static_item | + mc::cat_copied_upvar(..) | + mc::cat_local(..) | + mc::cat_arg(..) | + mc::cat_upvar(..) | + mc::cat_rvalue(..) => { + // These are all "base cases" with independent lifetimes + // that are not subject to inference + return; } - ast::ExprForLoop(..) => fail!("non-desugared expr_for_loop"), } } +} - fn categorize(rcx: &mut Rcx, expr: &ast::Expr) -> ExprCategorization { - debug!("categorize()"); - - let mut expr_ct = categorize_unadjusted(rcx, expr); - debug!("before adjustments, cat={:?}", expr_ct.cat); +fn adjust_borrow_kind_for_assignment_lhs(rcx: &mut Rcx, + lhs: &ast::Expr) { + /*! + * Adjusts the inferred borrow_kind as needed to account + * for upvars that are assigned to in an assignment + * expression. + */ - let adjustments = rcx.fcx.inh.adjustments.borrow(); - match adjustments.get().find(&expr.id) { - Some(adjustment) => { - match **adjustment { - ty::AutoAddEnv(..) => { - // This is basically an rvalue, not a pointer, no regions - // involved. - expr_ct.cat = ExprCategorization { - guarantor: None, - pointer: NotPointer - }; - } + let mut mc = mc::MemCategorizationContext { typer: rcx }; + let cmt = ignore_err!(mc.cat_expr(lhs)); + adjust_upvar_borrow_kind_for_mut(mc.typer, cmt); +} - ty::AutoObject(ast::BorrowedSigil, - Some(region), - _, - _, - _, - _) => { - expr_ct.cat = ExprCategorization { - guarantor: None, - pointer: BorrowedPointer(region) - }; - } +fn adjust_upvar_borrow_kind_for_mut(rcx: &mut Rcx, + cmt: mc::cmt) { + let mut cmt = cmt; + loop { + debug!("adjust_upvar_borrow_kind_for_mut(cmt={})", + cmt.repr(rcx.tcx())); + + match cmt.cat { + mc::cat_deref(base, _, mc::OwnedPtr) | + mc::cat_interior(base, _) | + mc::cat_downcast(base) | + mc::cat_discr(base, _) => { + // Interior or owned data is mutable if base is + // mutable, so iterate to the base. + cmt = base; + continue; + } - ty::AutoObject(ast::OwnedSigil, _, _, _, _, _) => { - expr_ct.cat = ExprCategorization { - guarantor: None, - pointer: OwnedPointer - }; + mc::cat_deref(base, _, mc::BorrowedPtr(..)) => { + match base.cat { + mc::cat_upvar(ref upvar_id, _) => { + // if this is an implicit deref of an + // upvar, then we need to modify the + // borrow_kind of the upvar to make sure it + // is inferred to mutable if necessary + let mut upvar_borrow_map = + rcx.fcx.inh.upvar_borrow_map.borrow_mut(); + let ub = upvar_borrow_map.get().get_mut(upvar_id); + return adjust_upvar_borrow_kind(*upvar_id, ub, ty::MutBorrow); } - ty::AutoObject(ast::ManagedSigil, _, _, _, _, _) => { - expr_ct.cat = ExprCategorization { - guarantor: None, - pointer: OtherPointer - }; + _ => { + // assignment to deref of an `&mut` + // borrowed pointer implies that the + // pointer itself must be unique, but not + // necessarily *mutable* + return adjust_upvar_borrow_kind_for_unique(rcx, base); } - ty::AutoDerefRef(ref adjustment) => { - debug!("adjustment={:?}", adjustment); - - expr_ct = apply_autoderefs( - rcx, expr, adjustment.autoderefs, expr_ct); - - match adjustment.autoref { - None => { - } - Some(ty::AutoUnsafe(_)) => { - expr_ct.cat.guarantor = None; - expr_ct.cat.pointer = OtherPointer; - debug!("autoref, cat={:?}", expr_ct.cat); - } - Some(ty::AutoPtr(r, _)) | - Some(ty::AutoBorrowVec(r, _)) | - Some(ty::AutoBorrowVecRef(r, _)) | - Some(ty::AutoBorrowFn(r)) | - Some(ty::AutoBorrowObj(r, _)) => { - // If there is an autoref, then the result of - // this expression will be some sort of - // reference. - expr_ct.cat.guarantor = None; - expr_ct.cat.pointer = BorrowedPointer(r); - debug!("autoref, cat={:?}", expr_ct.cat); - } - } - } - - _ => fail!("invalid or unhandled adjustment"), } } - None => {} - } - - debug!("result={:?}", expr_ct.cat); - return expr_ct.cat; - } - - fn categorize_unadjusted(rcx: &mut Rcx, - expr: &ast::Expr) - -> ExprCategorizationType { - debug!("categorize_unadjusted()"); - - let guarantor = { - let method_map = rcx.fcx.inh.method_map.borrow(); - if method_map.get().contains_key(&expr.id) { - None - } else { - guarantor(rcx, expr) + mc::cat_deref(_, _, mc::UnsafePtr(..)) | + mc::cat_deref(_, _, mc::GcPtr) | + mc::cat_static_item | + mc::cat_rvalue(_) | + mc::cat_copied_upvar(_) | + mc::cat_local(_) | + mc::cat_arg(_) | + mc::cat_upvar(..) => { + return; } - }; - - let expr_ty = rcx.resolve_node_type(expr.id); - ExprCategorizationType { - cat: ExprCategorization { - guarantor: guarantor, - pointer: pointer_categorize(expr_ty) - }, - ty: expr_ty } } +} - fn apply_autoderefs( - rcx: &mut Rcx, - expr: &ast::Expr, - autoderefs: uint, - ct: ExprCategorizationType) - -> ExprCategorizationType { - let mut ct = ct; - let tcx = rcx.fcx.ccx.tcx; - - if ty::type_is_error(ct.ty) { - ct.cat.pointer = NotPointer; - return ct; - } - - for _ in range(0u, autoderefs) { - ct.cat.guarantor = guarantor_of_deref(&ct.cat); - - match ty::deref(ct.ty, true) { - Some(mt) => { - ct.ty = mt.ty; - ct.cat.pointer = pointer_categorize(ct.ty); - } - None => { - tcx.sess.span_bug( - expr.span, - format!("autoderef but type not derefable: {}", - ty_to_str(tcx, ct.ty))); - } +fn adjust_upvar_borrow_kind_for_unique(rcx: &mut Rcx, + cmt: mc::cmt) { + let mut cmt = cmt; + loop { + debug!("adjust_upvar_borrow_kind_for_unique(cmt={})", + cmt.repr(rcx.tcx())); + + match cmt.cat { + mc::cat_deref(base, _, mc::OwnedPtr) | + mc::cat_interior(base, _) | + mc::cat_downcast(base) | + mc::cat_discr(base, _) => { + // Interior or owned data is unique if base is + // unique. + cmt = base; + continue; } - debug!("autoderef, cat={:?}", ct.cat); - } - return ct; - } + mc::cat_deref(base, _, mc::BorrowedPtr(..)) => { + match base.cat { + mc::cat_upvar(ref upvar_id, _) => { + // if this is an implicit deref of an + // upvar, then we need to modify the + // borrow_kind of the upvar to make sure it + // is inferred to unique if necessary + let mut upvar_borrow_map = + rcx.fcx.inh.upvar_borrow_map.borrow_mut(); + let ub = upvar_borrow_map.get().get_mut(upvar_id); + return adjust_upvar_borrow_kind(*upvar_id, ub, ty::UniqueImmBorrow); + } - fn pointer_categorize(ty: ty::t) -> PointerCategorization { - match ty::get(ty).sty { - ty::ty_rptr(r, _) | - ty::ty_vec(_, ty::vstore_slice(r)) | - ty::ty_trait(_, _, ty::RegionTraitStore(r), _, _) | - ty::ty_str(ty::vstore_slice(r)) => { - BorrowedPointer(r) - } - ty::ty_uniq(..) | - ty::ty_str(ty::vstore_uniq) | - ty::ty_trait(_, _, ty::UniqTraitStore, _, _) | - ty::ty_vec(_, ty::vstore_uniq) => { - OwnedPointer - } - ty::ty_box(..) | ty::ty_ptr(..) => { - OtherPointer - } - ty::ty_closure(ref closure_ty) => { - match closure_ty.sigil { - ast::BorrowedSigil => BorrowedPointer(closure_ty.region), - ast::OwnedSigil => OwnedPointer, - ast::ManagedSigil => OtherPointer, + _ => { + // for a borrowed pointer to be unique, its + // base must be unique + return adjust_upvar_borrow_kind_for_unique(rcx, base); + } } } - _ => { - NotPointer + + mc::cat_deref(_, _, mc::UnsafePtr(..)) | + mc::cat_deref(_, _, mc::GcPtr) | + mc::cat_static_item | + mc::cat_rvalue(_) | + mc::cat_copied_upvar(_) | + mc::cat_local(_) | + mc::cat_arg(_) | + mc::cat_upvar(..) => { + return; } } } +} - fn guarantor_of_deref(cat: &ExprCategorization) -> Option { - match cat.pointer { - NotPointer => cat.guarantor, - BorrowedPointer(r) => Some(r), - OwnedPointer => cat.guarantor, - OtherPointer => None +fn link_upvar_borrow_kind_for_nested_closures(rcx: &mut Rcx, + inner_upvar_id: ty::UpvarId, + outer_upvar_id: ty::UpvarId) { + /*! + * Indicates that the borrow_kind of `outer_upvar_id` must + * permit a reborrowing with the borrow_kind of `inner_upvar_id`. + * This occurs in nested closures, see comment above at the call to + * this function. + */ + + debug!("link_upvar_borrow_kind: inner_upvar_id={:?} outer_upvar_id={:?}", + inner_upvar_id, outer_upvar_id); + + let mut upvar_borrow_map = rcx.fcx.inh.upvar_borrow_map.borrow_mut(); + let inner_borrow = upvar_borrow_map.get().get_copy(&inner_upvar_id); + match upvar_borrow_map.get().find_mut(&outer_upvar_id) { + Some(outer_borrow) => { + adjust_upvar_borrow_kind(outer_upvar_id, outer_borrow, inner_borrow.kind); } + None => { /* outer closure is not a stack closure */ } } +} - fn link_ref_bindings_in_pat( - rcx: &mut Rcx, - pat: &ast::Pat, - guarantor: Option) { - /*! - * - * Descends through the pattern, tracking the guarantor - * of the value being matched. When a ref binding is encountered, - * links the lifetime of that ref binding to the lifetime of - * the guarantor. We begin with the guarantor of the - * discriminant but of course as we go we may pass through - * other pointers. - */ +fn adjust_upvar_borrow_kind_for_loan(upvar_id: ty::UpvarId, + upvar_borrow: &mut ty::UpvarBorrow, + mutbl: ast::Mutability) { + debug!("adjust_upvar_borrow_kind_for_loan: upvar_id={:?} kind={:?} -> {:?}", + upvar_id, upvar_borrow.kind, mutbl); - debug!("link_ref_bindings_in_pat(pat={}, guarantor={:?})", - rcx.fcx.pat_to_str(pat), guarantor); + adjust_upvar_borrow_kind(upvar_id, upvar_borrow, + ty::BorrowKind::from_mutbl(mutbl)) +} - match pat.node { - ast::PatWild | ast::PatWildMulti => {} - ast::PatIdent(ast::BindByRef(_), _, opt_p) => { - link(rcx, pat.span, pat.id, guarantor); +fn adjust_upvar_borrow_kind(upvar_id: ty::UpvarId, + upvar_borrow: &mut ty::UpvarBorrow, + kind: ty::BorrowKind) { + /*! + * We infer the borrow_kind with which to borrow upvars in a stack + * closure. The borrow_kind basically follows a lattice of + * `imm < unique-imm < mut`, moving from left to right as needed (but never + * right to left). Here the argument `mutbl` is the borrow_kind that + * is required by some particular use. + */ - for p in opt_p.iter() { - link_ref_bindings_in_pat(rcx, *p, guarantor); - } - } - ast::PatIdent(_, _, opt_p) => { - for p in opt_p.iter() { - link_ref_bindings_in_pat(rcx, *p, guarantor); - } - } - ast::PatEnum(_, None) => {} - ast::PatEnum(_, Some(ref pats)) => { - link_ref_bindings_in_pats(rcx, pats, guarantor); - } - ast::PatStruct(_, ref fpats, _) => { - for fpat in fpats.iter() { - link_ref_bindings_in_pat(rcx, fpat.pat, guarantor); - } - } - ast::PatTup(ref ps) => { - link_ref_bindings_in_pats(rcx, ps, guarantor) - } - ast::PatUniq(p) => { - link_ref_bindings_in_pat(rcx, p, guarantor) - } - ast::PatRegion(p) => { - let rptr_ty = rcx.resolve_node_type(pat.id); - let r = ty::ty_region(rcx.fcx.tcx(), pat.span, rptr_ty); - link_ref_bindings_in_pat(rcx, p, Some(r)); - } - ast::PatLit(..) => {} - ast::PatRange(..) => {} - ast::PatVec(ref before, ref slice, ref after) => { - let vec_ty = rcx.resolve_node_type(pat.id); - let vstore = ty::ty_vstore(vec_ty); - let guarantor1 = match vstore { - ty::vstore_fixed(_) | ty::vstore_uniq => guarantor, - ty::vstore_slice(r) => Some(r), - }; + debug!("adjust_upvar_borrow_kind: id={:?} kind=({:?} -> {:?})", + upvar_id, upvar_borrow.kind, kind); - link_ref_bindings_in_pats(rcx, before, guarantor1); - for &p in slice.iter() { - link_ref_bindings_in_pat(rcx, p, guarantor); - } - link_ref_bindings_in_pats(rcx, after, guarantor1); - } + match (upvar_borrow.kind, kind) { + // Take RHS: + (ty::ImmBorrow, ty::UniqueImmBorrow) | + (ty::ImmBorrow, ty::MutBorrow) | + (ty::UniqueImmBorrow, ty::MutBorrow) => { + upvar_borrow.kind = kind; } - } - - fn link_ref_bindings_in_pats(rcx: &mut Rcx, - pats: &~[@ast::Pat], - guarantor: Option) { - for pat in pats.iter() { - link_ref_bindings_in_pat(rcx, *pat, guarantor); + // Take LHS: + (ty::ImmBorrow, ty::ImmBorrow) | + (ty::UniqueImmBorrow, ty::ImmBorrow) | + (ty::UniqueImmBorrow, ty::UniqueImmBorrow) | + (ty::MutBorrow, _) => { } } - } diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs index 48b1acd3f9b37..8480135599052 100644 --- a/src/librustc/middle/typeck/check/writeback.rs +++ b/src/librustc/middle/typeck/check/writeback.rs @@ -378,10 +378,45 @@ impl Visitor<()> for WbCtxt { fn visit_ty(&mut self, _t: &ast::Ty, _: ()) {} } +fn resolve_upvar_borrow_map(wbcx: &mut WbCtxt) { + if !wbcx.success { + return; + } + + let fcx = wbcx.fcx; + let tcx = fcx.tcx(); + let upvar_borrow_map = fcx.inh.upvar_borrow_map.borrow(); + for (upvar_id, upvar_borrow) in upvar_borrow_map.get().iter() { + let r = upvar_borrow.region; + match resolve_region(fcx.infcx(), r, resolve_all | force_all) { + Ok(r) => { + let new_upvar_borrow = ty::UpvarBorrow { + kind: upvar_borrow.kind, + region: r + }; + debug!("Upvar borrow for {} resolved to {}", + upvar_id.repr(tcx), new_upvar_borrow.repr(tcx)); + let mut tcx_upvar_borrow_map = tcx.upvar_borrow_map.borrow_mut(); + tcx_upvar_borrow_map.get().insert(*upvar_id, new_upvar_borrow); + } + Err(e) => { + let span = ty::expr_span(tcx, upvar_id.closure_expr_id); + fcx.ccx.tcx.sess.span_err( + span, format!("cannot resolve lifetime for \ + captured variable `{}`: {}", + ty::local_var_name_str(tcx, upvar_id.var_id).get().to_str(), + infer::fixup_err_to_str(e))); + wbcx.success = false; + } + }; + } +} + pub fn resolve_type_vars_in_expr(fcx: @FnCtxt, e: &ast::Expr) -> bool { let mut wbcx = WbCtxt { fcx: fcx, success: true }; let wbcx = &mut wbcx; wbcx.visit_expr(e, ()); + resolve_upvar_borrow_map(wbcx); return wbcx.success; } @@ -397,5 +432,6 @@ pub fn resolve_type_vars_in_fn(fcx: @FnCtxt, decl: &ast::FnDecl, resolve_type_vars_for_node(wbcx, arg.pat.span, arg.pat.id); } } + resolve_upvar_borrow_map(wbcx); return wbcx.success; } diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs index 657b75a44ed30..3a3f24a2e2d88 100644 --- a/src/librustc/middle/typeck/infer/error_reporting.rs +++ b/src/librustc/middle/typeck/infer/error_reporting.rs @@ -237,6 +237,24 @@ impl ErrorReporting for InferCtxt { sup, ""); } + infer::ReborrowUpvar(span, ref upvar_id) => { + self.tcx.sess.span_err( + span, + format!("lifetime of borrowed pointer outlives \ + lifetime of captured variable `{}`...", + ty::local_var_name_str(self.tcx, upvar_id.var_id).get().to_str())); + note_and_explain_region( + self.tcx, + "...the borrowed pointer is valid for ", + sub, + "..."); + note_and_explain_region( + self.tcx, + format!("...but `{}` is only valid for ", + ty::local_var_name_str(self.tcx, upvar_id.var_id).get().to_str()), + sup, + ""); + } infer::InfStackClosure(span) => { self.tcx.sess.span_err( span, @@ -272,10 +290,12 @@ impl ErrorReporting for InferCtxt { sup, ""); } - infer::FreeVariable(span) => { + infer::FreeVariable(span, id) => { self.tcx.sess.span_err( span, - "captured variable does not outlive the enclosing closure"); + format!("captured variable `{}` does not \ + outlive the enclosing closure", + ty::local_var_name_str(self.tcx, id).get().to_str())); note_and_explain_region( self.tcx, "captured variable is valid for ", @@ -473,6 +493,10 @@ impl ErrorReportingHelpers for InferCtxt { infer::BoundRegionInCoherence(..) => { format!(" for coherence check") } + infer::UpvarRegion(ref upvar_id, _) => { + format!(" for capture of `{}` by closure", + ty::local_var_name_str(self.tcx, upvar_id.var_id).get().to_str()) + } }; self.tcx.sess.span_err( @@ -533,6 +557,12 @@ impl ErrorReportingHelpers for InferCtxt { "...so that reference does not outlive \ borrowed content"); } + infer::ReborrowUpvar(span, ref upvar_id) => { + self.tcx.sess.span_note( + span, + format!("...so that closure can access `{}`", + ty::local_var_name_str(self.tcx, upvar_id.var_id).get().to_str())) + } infer::InfStackClosure(span) => { self.tcx.sess.span_note( span, @@ -549,11 +579,12 @@ impl ErrorReportingHelpers for InferCtxt { "...so that pointer is not dereferenced \ outside its lifetime"); } - infer::FreeVariable(span) => { + infer::FreeVariable(span, id) => { self.tcx.sess.span_note( span, - "...so that captured variable does not outlive the \ - enclosing closure"); + format!("...so that captured variable `{}` \ + does not outlive the enclosing closure", + ty::local_var_name_str(self.tcx, id).get().to_str())); } infer::IndexSlice(span) => { self.tcx.sess.span_note( diff --git a/src/librustc/middle/typeck/infer/mod.rs b/src/librustc/middle/typeck/infer/mod.rs index 496170c3e47e0..deec410061741 100644 --- a/src/librustc/middle/typeck/infer/mod.rs +++ b/src/librustc/middle/typeck/infer/mod.rs @@ -160,7 +160,7 @@ pub enum SubregionOrigin { DerefPointer(Span), // Closure bound must not outlive captured free variables - FreeVariable(Span), + FreeVariable(Span, ast::NodeId), // Index into slice must be within its lifetime IndexSlice(Span), @@ -172,6 +172,9 @@ pub enum SubregionOrigin { // Creating a pointer `b` to contents of another reference Reborrow(Span), + // Creating a pointer `b` to contents of an upvar + ReborrowUpvar(Span, ty::UpvarId), + // (&'a &'b T) where a >= b ReferenceOutlivesReferent(ty::t, Span), @@ -225,6 +228,8 @@ pub enum RegionVariableOrigin { // when doing subtyping/lub/glb computations BoundRegionInFnType(Span, ty::BoundRegion), + UpvarRegion(ty::UpvarId, Span), + BoundRegionInTypeOrImpl(Span), BoundRegionInCoherence, @@ -876,10 +881,11 @@ impl SubregionOrigin { InfStackClosure(a) => a, InvokeClosure(a) => a, DerefPointer(a) => a, - FreeVariable(a) => a, + FreeVariable(a, _) => a, IndexSlice(a) => a, RelateObjectBound(a) => a, Reborrow(a) => a, + ReborrowUpvar(a, _) => a, ReferenceOutlivesReferent(_, a) => a, BindingTypeIsNotValidAtDecl(a) => a, CallRcvr(a) => a, @@ -898,10 +904,11 @@ impl Repr for SubregionOrigin { InfStackClosure(a) => format!("InfStackClosure({})", a.repr(tcx)), InvokeClosure(a) => format!("InvokeClosure({})", a.repr(tcx)), DerefPointer(a) => format!("DerefPointer({})", a.repr(tcx)), - FreeVariable(a) => format!("FreeVariable({})", a.repr(tcx)), + FreeVariable(a, b) => format!("FreeVariable({}, {})", a.repr(tcx), b), IndexSlice(a) => format!("IndexSlice({})", a.repr(tcx)), RelateObjectBound(a) => format!("RelateObjectBound({})", a.repr(tcx)), Reborrow(a) => format!("Reborrow({})", a.repr(tcx)), + ReborrowUpvar(a, b) => format!("ReborrowUpvar({},{:?})", a.repr(tcx), b), ReferenceOutlivesReferent(_, a) => format!("ReferenceOutlivesReferent({})", a.repr(tcx)), BindingTypeIsNotValidAtDecl(a) => @@ -928,6 +935,7 @@ impl RegionVariableOrigin { BoundRegionInFnType(a, _) => a, BoundRegionInTypeOrImpl(a) => a, BoundRegionInCoherence => codemap::DUMMY_SP, + UpvarRegion(_, a) => a } } } @@ -948,6 +956,9 @@ impl Repr for RegionVariableOrigin { BoundRegionInTypeOrImpl(a) => format!("bound_regionInTypeOrImpl({})", a.repr(tcx)), BoundRegionInCoherence => format!("bound_regionInCoherence"), + UpvarRegion(a, b) => format!("UpvarRegion({}, {})", + a.repr(tcx), + b.repr(tcx)), } } } diff --git a/src/librustc/middle/typeck/infer/region_inference/mod.rs b/src/librustc/middle/typeck/infer/region_inference/mod.rs index 2bc0d7d641956..bbd9d8e1c4dcc 100644 --- a/src/librustc/middle/typeck/infer/region_inference/mod.rs +++ b/src/librustc/middle/typeck/infer/region_inference/mod.rs @@ -270,7 +270,11 @@ impl RegionVarBindings { // cannot add constraints once regions are resolved assert!(self.values_are_none()); - debug!("RegionVarBindings: make_subregion({:?}, {:?})", sub, sup); + debug!("RegionVarBindings: make_subregion({}, {}) due to {}", + sub.repr(self.tcx), + sup.repr(self.tcx), + origin.repr(self.tcx)); + match (sub, sup) { (ReEarlyBound(..), _) | (ReLateBound(..), _) | diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 5a9c3fd031dc4..afac501835dc7 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -565,11 +565,26 @@ impl Repr for Option { fn repr(&self, tcx: ctxt) -> ~str { match self { &None => ~"None", - &Some(ref t) => format!("Some({})", t.repr(tcx)) + &Some(ref t) => t.repr(tcx), } } } +impl Repr for Result { + fn repr(&self, tcx: ctxt) -> ~str { + match self { + &Ok(ref t) => t.repr(tcx), + &Err(ref u) => format!("Err({})", u.repr(tcx)) + } + } +} + +impl Repr for () { + fn repr(&self, _tcx: ctxt) -> ~str { + ~"()" + } +} + impl Repr for @T { fn repr(&self, tcx: ctxt) -> ~str { (&**self).repr(tcx) @@ -1021,3 +1036,32 @@ impl UserString for AbiSet { self.to_str() } } + +impl Repr for ty::UpvarId { + fn repr(&self, tcx: ctxt) -> ~str { + format!("UpvarId({};`{}`;{})", + self.var_id, + ty::local_var_name_str(tcx, self.var_id), + self.closure_expr_id) + } +} + +impl Repr for ast::Mutability { + fn repr(&self, _tcx: ctxt) -> ~str { + format!("{:?}", *self) + } +} + +impl Repr for ty::BorrowKind { + fn repr(&self, _tcx: ctxt) -> ~str { + format!("{:?}", *self) + } +} + +impl Repr for ty::UpvarBorrow { + fn repr(&self, tcx: ctxt) -> ~str { + format!("UpvarBorrow({}, {})", + self.kind.repr(tcx), + self.region.repr(tcx)) + } +} diff --git a/src/librustc/util/sha2.rs b/src/librustc/util/sha2.rs index 1b3f5ec947d7f..116ec6bba2904 100644 --- a/src/librustc/util/sha2.rs +++ b/src/librustc/util/sha2.rs @@ -453,7 +453,8 @@ impl Engine256 { assert!(!self.finished) // Assumes that input.len() can be converted to u64 without overflow self.length_bits = add_bytes_to_bits(self.length_bits, input.len() as u64); - self.buffer.input(input, |input: &[u8]| { self.state.process_block(input) }); + let self_state = &mut self.state; + self.buffer.input(input, |input: &[u8]| { self_state.process_block(input) }); } fn finish(&mut self) { @@ -461,10 +462,11 @@ impl Engine256 { return; } - self.buffer.standard_padding(8, |input: &[u8]| { self.state.process_block(input) }); + let self_state = &mut self.state; + self.buffer.standard_padding(8, |input: &[u8]| { self_state.process_block(input) }); write_u32_be(self.buffer.next(4), (self.length_bits >> 32) as u32 ); write_u32_be(self.buffer.next(4), self.length_bits as u32); - self.state.process_block(self.buffer.full_buffer()); + self_state.process_block(self.buffer.full_buffer()); self.finished = true; } diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index 83af47d585e4d..fee27bd5b317e 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -21,7 +21,6 @@ pub trait DocFolder { fn fold_item_recur(&mut self, item: Item) -> Option { let Item { attrs, name, source, visibility, id, inner } = item; let inner = inner; - let c = |x| self.fold_item(x); let inner = match inner { StructItem(mut i) => { let mut foo = ~[]; swap(&mut foo, &mut i.fields); @@ -72,6 +71,7 @@ pub trait DocFolder { StructVariant(mut j) => { let mut foo = ~[]; swap(&mut foo, &mut j.fields); let num_fields = foo.len(); + let c = |x| self.fold_item(x); j.fields.extend(&mut foo.move_iter().filter_map(c)); j.fields_stripped |= num_fields != j.fields.len(); VariantItem(Variant {kind: StructVariant(j), ..i2}) diff --git a/src/librustuv/net.rs b/src/librustuv/net.rs index 87fadbba176f6..551e2c9faf74f 100644 --- a/src/librustuv/net.rs +++ b/src/librustuv/net.rs @@ -510,8 +510,9 @@ impl rtio::RtioUdpSocket for UdpWatcher { buf: Some(slice_to_uv_buf(buf)), result: None, }; + let handle = self.handle; wait_until_woken_after(&mut cx.task, || { - unsafe { uvll::set_data_for_uv_handle(self.handle, &cx) } + unsafe { uvll::set_data_for_uv_handle(handle, &cx) } }); match cx.result.take_unwrap() { (n, _) if n < 0 => diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 1c1df691a5240..54c0d98c79897 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -277,7 +277,7 @@ use str::{StrSlice, OwnedStr}; use str; use to_str::ToStr; use uint; -use unstable::finally::Finally; +use unstable::finally::try_finally; use vec::{OwnedVector, MutableVector, ImmutableVector, OwnedCloneableVector}; use vec; @@ -473,25 +473,33 @@ pub trait Reader { /// pushed on to the vector, otherwise the amount `len` bytes couldn't be /// read (an error was encountered), and the error is returned. fn push_bytes(&mut self, buf: &mut ~[u8], len: uint) -> IoResult<()> { + struct State<'a> { + buf: &'a mut ~[u8], + total_read: uint + } + let start_len = buf.len(); - let mut total_read = 0; - - buf.reserve_additional(len); - unsafe { buf.set_len(start_len + len); } - - (|| { - while total_read < len { - let len = buf.len(); - let slice = buf.mut_slice(start_len + total_read, len); - match self.read(slice) { - Ok(nread) => { - total_read += nread; + let mut s = State { buf: buf, total_read: 0 }; + + s.buf.reserve_additional(len); + unsafe { s.buf.set_len(start_len + len); } + + try_finally( + &mut s, (), + |s, _| { + while s.total_read < len { + let len = s.buf.len(); + let slice = s.buf.mut_slice(start_len + s.total_read, len); + match self.read(slice) { + Ok(nread) => { + s.total_read += nread; + } + Err(e) => return Err(e) } - Err(e) => return Err(e) } - } - Ok(()) - }).finally(|| unsafe { buf.set_len(start_len + total_read) }) + Ok(()) + }, + |s| unsafe { s.buf.set_len(start_len + s.total_read) }) } /// Reads `len` bytes and gives you back a new vector of length `len` diff --git a/src/libstd/io/net/udp.rs b/src/libstd/io/net/udp.rs index ae99101e17963..aeec36a932c46 100644 --- a/src/libstd/io/net/udp.rs +++ b/src/libstd/io/net/udp.rs @@ -83,7 +83,8 @@ impl Reader for UdpStream { impl Writer for UdpStream { fn write(&mut self, buf: &[u8]) -> IoResult<()> { - self.as_socket(|sock| sock.sendto(buf, self.connectedTo)) + let connectedTo = self.connectedTo; + self.as_socket(|sock| sock.sendto(buf, connectedTo)) } } diff --git a/src/libstd/str.rs b/src/libstd/str.rs index 3225bb3a6789c..bc5991c6eebd0 100644 --- a/src/libstd/str.rs +++ b/src/libstd/str.rs @@ -625,15 +625,17 @@ impl<'a> Iterator for Normalizations<'a> { if !self.sorted { for ch in self.iter { + let buffer = &mut self.buffer; + let sorted = &mut self.sorted; decomposer(ch, |d| { let class = canonical_combining_class(d); - if class == 0 && !self.sorted { - canonical_sort(self.buffer); - self.sorted = true; + if class == 0 && !*sorted { + canonical_sort(*buffer); + *sorted = true; } - self.buffer.push((d, class)); + buffer.push((d, class)); }); - if self.sorted { break } + if *sorted { break } } } diff --git a/src/libstd/unstable/finally.rs b/src/libstd/unstable/finally.rs index 6faf69d2bb98f..433accecdbcfd 100644 --- a/src/libstd/unstable/finally.rs +++ b/src/libstd/unstable/finally.rs @@ -12,6 +12,11 @@ The Finally trait provides a method, `finally` on stack closures that emulates Java-style try/finally blocks. +Using the `finally` method is sometimes convenient, but the type rules +prohibit any shared, mutable state between the "try" case and the +"finally" case. For advanced cases, the `try_finally` function can +also be used. See that function for more details. + # Example ``` @@ -31,53 +36,89 @@ pub trait Finally { fn finally(&self, dtor: ||) -> T; } -macro_rules! finally_fn { - ($fnty:ty) => { - impl Finally for $fnty { - fn finally(&self, dtor: ||) -> T { - let _d = Finallyalizer { - dtor: dtor - }; - (*self)() - } - } +impl<'a,T> Finally for 'a || -> T { + fn finally(&self, dtor: ||) -> T { + try_finally(&mut (), (), + |_, _| (*self)(), + |_| dtor()) } } -impl<'a,T> Finally for 'a || -> T { +impl Finally for fn() -> T { fn finally(&self, dtor: ||) -> T { - let _d = Finallyalizer { - dtor: dtor - }; - - (*self)() + try_finally(&mut (), (), + |_, _| (*self)(), + |_| dtor()) } } -finally_fn!(extern "Rust" fn() -> T) +/** + * The most general form of the `finally` functions. The function + * `try_fn` will be invoked first; whether or not it fails, the + * function `finally_fn` will be invoked next. The two parameters + * `mutate` and `drop` are used to thread state through the two + * closures. `mutate` is used for any shared, mutable state that both + * closures require access to; `drop` is used for any state that the + * `try_fn` requires ownership of. + * + * **WARNING:** While shared, mutable state between the try and finally + * function is often necessary, one must be very careful; the `try` + * function could have failed at any point, so the values of the shared + * state may be inconsistent. + * + * # Example + * + * ``` + * struct State<'a> { buffer: &'a mut [u8], len: uint } + * let mut state = State { buffer: buf, len: 0 }; + * try_finally( + * &mut state, (), + * |state, ()| { + * // use state.buffer, state.len + * } + * |state| { + * // use state.buffer, state.len to cleanup + * }) + * ``` + */ +pub fn try_finally(mutate: &mut T, + drop: U, + try_fn: |&mut T, U| -> R, + finally_fn: |&mut T|) + -> R { + let f = Finallyalizer { + mutate: mutate, + dtor: finally_fn, + }; + try_fn(&mut *f.mutate, drop) +} -struct Finallyalizer<'a> { - dtor: 'a || +struct Finallyalizer<'a,A> { + mutate: &'a mut A, + dtor: 'a |&mut A| } #[unsafe_destructor] -impl<'a> Drop for Finallyalizer<'a> { +impl<'a,A> Drop for Finallyalizer<'a,A> { #[inline] fn drop(&mut self) { - (self.dtor)(); + (self.dtor)(self.mutate); } } #[test] fn test_success() { let mut i = 0; - (|| { - i = 10; - }).finally(|| { - assert!(!failing()); - assert_eq!(i, 10); - i = 20; - }); + try_finally( + &mut i, (), + |i, ()| { + *i = 10; + }, + |i| { + assert!(!failing()); + assert_eq!(*i, 10); + *i = 20; + }); assert_eq!(i, 20); } @@ -85,13 +126,16 @@ fn test_success() { #[should_fail] fn test_fail() { let mut i = 0; - (|| { - i = 10; - fail!(); - }).finally(|| { - assert!(failing()); - assert_eq!(i, 10); - }) + try_finally( + &mut i, (), + |i, ()| { + *i = 10; + fail!(); + }, + |i| { + assert!(failing()); + assert_eq!(*i, 10); + }) } #[test] diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs index b58e0820cfd20..2acafecf95720 100644 --- a/src/libstd/vec.rs +++ b/src/libstd/vec.rs @@ -119,7 +119,7 @@ use mem; use mem::size_of; use kinds::marker; use uint; -use unstable::finally::Finally; +use unstable::finally::try_finally; use unstable::raw::{Repr, Slice, Vec}; /** @@ -132,15 +132,16 @@ pub fn from_fn(n_elts: uint, op: |uint| -> T) -> ~[T] { unsafe { let mut v = with_capacity(n_elts); let p = v.as_mut_ptr(); - let mut i: uint = 0u; - (|| { - while i < n_elts { - mem::move_val_init(&mut(*ptr::mut_offset(p, i as int)), op(i)); - i += 1u; - } - }).finally(|| { - v.set_len(i); - }); + let mut i = 0; + try_finally( + &mut i, (), + |i, ()| while *i < n_elts { + mem::move_val_init( + &mut(*ptr::mut_offset(p, *i as int)), + op(*i)); + *i += 1u; + }, + |i| v.set_len(*i)); v } } @@ -160,14 +161,15 @@ pub fn from_elem(n_elts: uint, t: T) -> ~[T] { let mut v = with_capacity(n_elts); let p = v.as_mut_ptr(); let mut i = 0u; - (|| { - while i < n_elts { - mem::move_val_init(&mut(*ptr::mut_offset(p, i as int)), t.clone()); - i += 1u; - } - }).finally(|| { - v.set_len(i); - }); + try_finally( + &mut i, (), + |i, ()| while *i < n_elts { + mem::move_val_init( + &mut(*ptr::mut_offset(p, *i as int)), + t.clone()); + *i += 1u; + }, + |i| v.set_len(*i)); v } } @@ -294,7 +296,8 @@ impl<'a, T> Iterator<&'a [T]> for RevSplits<'a, T> { return Some(self.v); } - match self.v.iter().rposition(|x| (self.pred)(x)) { + let pred = &mut self.pred; + match self.v.iter().rposition(|x| (*pred)(x)) { None => { self.finished = true; Some(self.v) diff --git a/src/libsyntax/ext/deriving/generic.rs b/src/libsyntax/ext/deriving/generic.rs index 47be3067284a5..9d290c93c6494 100644 --- a/src/libsyntax/ext/deriving/generic.rs +++ b/src/libsyntax/ext/deriving/generic.rs @@ -580,10 +580,12 @@ impl<'a> MethodDef<'a> { ast::SelfStatic => None, _ => Some(ast::Arg::new_self(trait_.span, ast::MutImmutable)) }; - let args = arg_types.move_iter().map(|(name, ty)| { - cx.arg(trait_.span, name, ty) - }); - let args = self_arg.move_iter().chain(args).collect(); + let args = { + let args = arg_types.move_iter().map(|(name, ty)| { + cx.arg(trait_.span, name, ty) + }); + self_arg.move_iter().chain(args).collect() + }; let ret_type = self.get_ret_ty(cx, trait_, generics, type_ident); diff --git a/src/libsyntax/ext/deriving/rand.rs b/src/libsyntax/ext/deriving/rand.rs index a40317286c960..ef7bd7c2bcdec 100644 --- a/src/libsyntax/ext/deriving/rand.rs +++ b/src/libsyntax/ext/deriving/rand.rs @@ -60,7 +60,7 @@ fn rand_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) cx.ident_of("Rand"), cx.ident_of("rand") ]; - let rand_call = |span| { + let rand_call = |cx: &mut ExtCtxt, span| { cx.expr_call_global(span, rand_ident.clone(), ~[ rng[0] ]) @@ -111,7 +111,7 @@ fn rand_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) let i_expr = cx.expr_uint(v_span, i); let pat = cx.pat_lit(v_span, i_expr); - let thing = rand_thing(cx, v_span, ident, summary, |sp| rand_call(sp)); + let thing = rand_thing(cx, v_span, ident, summary, |cx, sp| rand_call(cx, sp)); cx.arm(v_span, ~[ pat ], thing) }).collect::<~[ast::Arm]>(); @@ -130,20 +130,21 @@ fn rand_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) trait_span: Span, ctor_ident: Ident, summary: &StaticFields, - rand_call: |Span| -> @Expr) + rand_call: |&mut ExtCtxt, Span| -> @Expr) -> @Expr { match *summary { Unnamed(ref fields) => { if fields.is_empty() { cx.expr_ident(trait_span, ctor_ident) } else { - let exprs = fields.map(|span| rand_call(*span)); + let exprs = fields.map(|span| rand_call(cx, *span)); cx.expr_call_ident(trait_span, ctor_ident, exprs) } } Named(ref fields) => { let rand_fields = fields.map(|&(ident, span)| { - cx.field_imm(span, ident, rand_call(span)) + let e = rand_call(cx, span); + cx.field_imm(span, ident, e) }); cx.expr_struct_ident(trait_span, ctor_ident, rand_fields) } diff --git a/src/libsyntax/ext/deriving/to_str.rs b/src/libsyntax/ext/deriving/to_str.rs index 186f12544935d..e5145fb15f7a1 100644 --- a/src/libsyntax/ext/deriving/to_str.rs +++ b/src/libsyntax/ext/deriving/to_str.rs @@ -67,31 +67,32 @@ fn to_str_substructure(cx: &mut ExtCtxt, span: Span, substr: &Substructure) let mut stmts = ~[cx.stmt_let(span, true, buf, init)]; let push_str = cx.ident_of("push_str"); - let push = |s: @Expr| { - let ebuf = cx.expr_ident(span, buf); - let call = cx.expr_method_call(span, ebuf, push_str, ~[s]); - stmts.push(cx.stmt_expr(call)); - }; + { + let push = |s: @Expr| { + let ebuf = cx.expr_ident(span, buf); + let call = cx.expr_method_call(span, ebuf, push_str, ~[s]); + stmts.push(cx.stmt_expr(call)); + }; - for (i, &FieldInfo {name, span, self_, .. }) in fields.iter().enumerate() { - if i > 0 { - push(cx.expr_str(span, InternedString::new(", "))); - } - match name { - None => {} - Some(id) => { - let interned_id = token::get_ident(id.name); - let name = interned_id.get() + ": "; - push(cx.expr_str(span, - token::intern_and_get_ident(name))); + for (i, &FieldInfo {name, span, self_, .. }) in fields.iter().enumerate() { + if i > 0 { + push(cx.expr_str(span, InternedString::new(", "))); + } + match name { + None => {} + Some(id) => { + let interned_id = token::get_ident(id.name); + let name = interned_id.get() + ": "; + push(cx.expr_str(span, + token::intern_and_get_ident(name))); + } } + push(cx.expr_method_call(span, self_, to_str, ~[])); } - push(cx.expr_method_call(span, self_, to_str, ~[])); + push(cx.expr_str(span, end)); } - push(cx.expr_str(span, end)); - cx.expr_block(cx.block(span, stmts, Some(cx.expr_ident(span, - buf)))) + cx.expr_block(cx.block(span, stmts, Some(cx.expr_ident(span, buf)))) } }; diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 69611829c7ca9..d146cd4dae392 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -709,14 +709,15 @@ pub fn expand_block(blk: &Block, fld: &mut MacroExpander) -> P { // expand the elements of a block. pub fn expand_block_elts(b: &Block, fld: &mut MacroExpander) -> P { let new_view_items = b.view_items.map(|x| fld.fold_view_item(x)); - let new_stmts = b.stmts.iter() - .map(|x| { + let new_stmts = + b.stmts.iter().flat_map(|x| { + let renamed_stmt = { let pending_renames = &mut fld.extsbox.info().pending_renames; let mut rename_fld = renames_to_fold(pending_renames); rename_fld.fold_stmt(*x).expect_one("rename_fold didn't return one value") - }) - .flat_map(|x| fld.fold_stmt(x).move_iter()) - .collect(); + }; + fld.fold_stmt(renamed_stmt).move_iter() + }).collect(); let new_expr = b.expr.map(|x| { let expr = { let pending_renames = &mut fld.extsbox.info().pending_renames; diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs index 4bc3b804c7ff7..3eacce5eb1d77 100644 --- a/src/libsyntax/ext/format.rs +++ b/src/libsyntax/ext/format.rs @@ -367,157 +367,167 @@ impl<'a> Context<'a> { return ~[unnamed, allow_dead_code]; } - /// Translate a `parse::Piece` to a static `rt::Piece` - fn trans_piece(&mut self, piece: &parse::Piece) -> @ast::Expr { - let sp = self.fmtsp; - let parsepath = |s: &str| { - ~[self.ecx.ident_of("std"), self.ecx.ident_of("fmt"), - self.ecx.ident_of("parse"), self.ecx.ident_of(s)] - }; - let rtpath = |s: &str| { - ~[self.ecx.ident_of("std"), self.ecx.ident_of("fmt"), - self.ecx.ident_of("rt"), self.ecx.ident_of(s)] - }; - let ctpath = |s: &str| { - ~[self.ecx.ident_of("std"), self.ecx.ident_of("fmt"), - self.ecx.ident_of("parse"), self.ecx.ident_of(s)] - }; - let none = self.ecx.path_global(sp, ~[ + fn parsepath(&self, s: &str) -> ~[ast::Ident] { + ~[self.ecx.ident_of("std"), self.ecx.ident_of("fmt"), + self.ecx.ident_of("parse"), self.ecx.ident_of(s)] + } + + fn rtpath(&self, s: &str) -> ~[ast::Ident] { + ~[self.ecx.ident_of("std"), self.ecx.ident_of("fmt"), + self.ecx.ident_of("rt"), self.ecx.ident_of(s)] + } + + fn ctpath(&self, s: &str) -> ~[ast::Ident] { + ~[self.ecx.ident_of("std"), self.ecx.ident_of("fmt"), + self.ecx.ident_of("parse"), self.ecx.ident_of(s)] + } + + fn none(&self) -> @ast::Expr { + let none = self.ecx.path_global(self.fmtsp, ~[ self.ecx.ident_of("std"), self.ecx.ident_of("option"), self.ecx.ident_of("None")]); - let none = self.ecx.expr_path(none); - let some = |e: @ast::Expr| { - let p = self.ecx.path_global(sp, ~[ + self.ecx.expr_path(none) + } + + fn some(&self, e: @ast::Expr) -> @ast::Expr { + let p = self.ecx.path_global(self.fmtsp, ~[ self.ecx.ident_of("std"), self.ecx.ident_of("option"), self.ecx.ident_of("Some")]); - let p = self.ecx.expr_path(p); - self.ecx.expr_call(sp, p, ~[e]) - }; - let trans_count = |c: parse::Count| { - match c { - parse::CountIs(i) => { - self.ecx.expr_call_global(sp, rtpath("CountIs"), - ~[self.ecx.expr_uint(sp, i)]) - } - parse::CountIsParam(i) => { - self.ecx.expr_call_global(sp, rtpath("CountIsParam"), - ~[self.ecx.expr_uint(sp, i)]) - } - parse::CountImplied => { - let path = self.ecx.path_global(sp, rtpath("CountImplied")); - self.ecx.expr_path(path) - } - parse::CountIsNextParam => { - let path = self.ecx.path_global(sp, rtpath("CountIsNextParam")); - self.ecx.expr_path(path) - } - parse::CountIsName(n) => { - let i = match self.name_positions.find_equiv(&n) { - Some(&i) => i, - None => 0, // error already emitted elsewhere - }; - let i = i + self.args.len(); - self.ecx.expr_call_global(sp, rtpath("CountIsParam"), - ~[self.ecx.expr_uint(sp, i)]) - } + let p = self.ecx.expr_path(p); + self.ecx.expr_call(self.fmtsp, p, ~[e]) + } + + fn trans_count(&self, c: parse::Count) -> @ast::Expr { + let sp = self.fmtsp; + match c { + parse::CountIs(i) => { + self.ecx.expr_call_global(sp, self.rtpath("CountIs"), + ~[self.ecx.expr_uint(sp, i)]) } - }; - let trans_method = |method: &parse::Method| { - let method = match *method { - parse::Select(ref arms, ref default) => { - let arms = arms.iter().map(|arm| { - let p = self.ecx.path_global(sp, rtpath("SelectArm")); + parse::CountIsParam(i) => { + self.ecx.expr_call_global(sp, self.rtpath("CountIsParam"), + ~[self.ecx.expr_uint(sp, i)]) + } + parse::CountImplied => { + let path = self.ecx.path_global(sp, self.rtpath("CountImplied")); + self.ecx.expr_path(path) + } + parse::CountIsNextParam => { + let path = self.ecx.path_global(sp, self.rtpath("CountIsNextParam")); + self.ecx.expr_path(path) + } + parse::CountIsName(n) => { + let i = match self.name_positions.find_equiv(&n) { + Some(&i) => i, + None => 0, // error already emitted elsewhere + }; + let i = i + self.args.len(); + self.ecx.expr_call_global(sp, self.rtpath("CountIsParam"), + ~[self.ecx.expr_uint(sp, i)]) + } + } + } + + fn trans_method(&mut self, method: &parse::Method) -> @ast::Expr { + let sp = self.fmtsp; + let method = match *method { + parse::Select(ref arms, ref default) => { + let arms = arms.iter().map(|arm| { + let p = self.ecx.path_global(sp, self.rtpath("SelectArm")); let result = arm.result.iter().map(|p| { self.trans_piece(p) }).collect(); let s = token::intern_and_get_ident(arm.selector); let selector = self.ecx.expr_str(sp, s); self.ecx.expr_struct(sp, p, ~[ - self.ecx.field_imm(sp, - self.ecx.ident_of("selector"), - selector), - self.ecx.field_imm(sp, self.ecx.ident_of("result"), - self.ecx.expr_vec_slice(sp, result)), - ]) + self.ecx.field_imm(sp, + self.ecx.ident_of("selector"), + selector), + self.ecx.field_imm(sp, self.ecx.ident_of("result"), + self.ecx.expr_vec_slice(sp, result)), + ]) }).collect(); - let default = default.iter().map(|p| { + let default = default.iter().map(|p| { self.trans_piece(p) }).collect(); - self.ecx.expr_call_global(sp, rtpath("Select"), ~[ + self.ecx.expr_call_global(sp, self.rtpath("Select"), ~[ self.ecx.expr_vec_slice(sp, arms), self.ecx.expr_vec_slice(sp, default), - ]) - } - parse::Plural(offset, ref arms, ref default) => { - let offset = match offset { - Some(i) => { some(self.ecx.expr_uint(sp, i)) } - None => { none.clone() } - }; - let arms = arms.iter().map(|arm| { - let p = self.ecx.path_global(sp, rtpath("PluralArm")); + ]) + } + parse::Plural(offset, ref arms, ref default) => { + let offset = match offset { + Some(i) => { self.some(self.ecx.expr_uint(sp, i)) } + None => { self.none() } + }; + let arms = arms.iter().map(|arm| { + let p = self.ecx.path_global(sp, self.rtpath("PluralArm")); let result = arm.result.iter().map(|p| { - self.trans_piece(p) - }).collect(); + self.trans_piece(p) + }).collect(); let (lr, selarg) = match arm.selector { parse::Keyword(t) => { - let p = ctpath(format!("{:?}", t)); + let p = self.ctpath(format!("{:?}", t)); let p = self.ecx.path_global(sp, p); - (rtpath("Keyword"), self.ecx.expr_path(p)) + (self.rtpath("Keyword"), self.ecx.expr_path(p)) } parse::Literal(i) => { - (rtpath("Literal"), self.ecx.expr_uint(sp, i)) + (self.rtpath("Literal"), self.ecx.expr_uint(sp, i)) } }; let selector = self.ecx.expr_call_global(sp, - lr, ~[selarg]); + lr, ~[selarg]); self.ecx.expr_struct(sp, p, ~[ - self.ecx.field_imm(sp, - self.ecx.ident_of("selector"), - selector), - self.ecx.field_imm(sp, self.ecx.ident_of("result"), - self.ecx.expr_vec_slice(sp, result)), - ]) + self.ecx.field_imm(sp, + self.ecx.ident_of("selector"), + selector), + self.ecx.field_imm(sp, self.ecx.ident_of("result"), + self.ecx.expr_vec_slice(sp, result)), + ]) }).collect(); - let default = default.iter().map(|p| { + let default = default.iter().map(|p| { self.trans_piece(p) }).collect(); - self.ecx.expr_call_global(sp, rtpath("Plural"), ~[ + self.ecx.expr_call_global(sp, self.rtpath("Plural"), ~[ offset, self.ecx.expr_vec_slice(sp, arms), self.ecx.expr_vec_slice(sp, default), - ]) - } - }; - let life = self.ecx.lifetime(sp, self.ecx.ident_of("static")); - let ty = self.ecx.ty_path(self.ecx.path_all( + ]) + } + }; + let life = self.ecx.lifetime(sp, self.ecx.ident_of("static")); + let ty = self.ecx.ty_path(self.ecx.path_all( sp, true, - rtpath("Method"), + self.rtpath("Method"), opt_vec::with(life), ~[] - ), None); - let st = ast::ItemStatic(ty, ast::MutImmutable, method); - let static_name = self.ecx.ident_of(format!("__STATIC_METHOD_{}", - self.method_statics.len())); - let item = self.ecx.item(sp, static_name, self.static_attrs(), st); - self.method_statics.push(item); - self.ecx.expr_ident(sp, static_name) - }; + ), None); + let st = ast::ItemStatic(ty, ast::MutImmutable, method); + let static_name = self.ecx.ident_of(format!("__STATIC_METHOD_{}", + self.method_statics.len())); + let item = self.ecx.item(sp, static_name, self.static_attrs(), st); + self.method_statics.push(item); + self.ecx.expr_ident(sp, static_name) + } + /// Translate a `parse::Piece` to a static `rt::Piece` + fn trans_piece(&mut self, piece: &parse::Piece) -> @ast::Expr { + let sp = self.fmtsp; match *piece { parse::String(s) => { let s = token::intern_and_get_ident(s); self.ecx.expr_call_global(sp, - rtpath("String"), + self.rtpath("String"), ~[ self.ecx.expr_str(sp, s) ]) } parse::CurrentArgument => { let nil = self.ecx.expr_lit(sp, ast::LitNil); - self.ecx.expr_call_global(sp, rtpath("CurrentArgument"), ~[nil]) + self.ecx.expr_call_global(sp, self.rtpath("CurrentArgument"), ~[nil]) } parse::Argument(ref arg) => { // Translate the position @@ -525,11 +535,11 @@ impl<'a> Context<'a> { // These two have a direct mapping parse::ArgumentNext => { let path = self.ecx.path_global(sp, - rtpath("ArgumentNext")); + self.rtpath("ArgumentNext")); self.ecx.expr_path(path) } parse::ArgumentIs(i) => { - self.ecx.expr_call_global(sp, rtpath("ArgumentIs"), + self.ecx.expr_call_global(sp, self.rtpath("ArgumentIs"), ~[self.ecx.expr_uint(sp, i)]) } // Named arguments are converted to positional arguments at @@ -540,7 +550,7 @@ impl<'a> Context<'a> { None => 0, // error already emitted elsewhere }; let i = i + self.args.len(); - self.ecx.expr_call_global(sp, rtpath("ArgumentIs"), + self.ecx.expr_call_global(sp, self.rtpath("ArgumentIs"), ~[self.ecx.expr_uint(sp, i)]) } }; @@ -550,20 +560,20 @@ impl<'a> Context<'a> { let fill = self.ecx.expr_lit(sp, ast::LitChar(fill as u32)); let align = match arg.format.align { parse::AlignLeft => { - self.ecx.path_global(sp, parsepath("AlignLeft")) + self.ecx.path_global(sp, self.parsepath("AlignLeft")) } parse::AlignRight => { - self.ecx.path_global(sp, parsepath("AlignRight")) + self.ecx.path_global(sp, self.parsepath("AlignRight")) } parse::AlignUnknown => { - self.ecx.path_global(sp, parsepath("AlignUnknown")) + self.ecx.path_global(sp, self.parsepath("AlignUnknown")) } }; let align = self.ecx.expr_path(align); let flags = self.ecx.expr_uint(sp, arg.format.flags); - let prec = trans_count(arg.format.precision); - let width = trans_count(arg.format.width); - let path = self.ecx.path_global(sp, rtpath("FormatSpec")); + let prec = self.trans_count(arg.format.precision); + let width = self.trans_count(arg.format.width); + let path = self.ecx.path_global(sp, self.rtpath("FormatSpec")); let fmt = self.ecx.expr_struct(sp, path, ~[ self.ecx.field_imm(sp, self.ecx.ident_of("fill"), fill), self.ecx.field_imm(sp, self.ecx.ident_of("align"), align), @@ -574,19 +584,19 @@ impl<'a> Context<'a> { // Translate the method (if any) let method = match arg.method { - None => { none.clone() } + None => { self.none() } Some(ref m) => { - let m = trans_method(*m); - some(self.ecx.expr_addr_of(sp, m)) + let m = self.trans_method(*m); + self.some(self.ecx.expr_addr_of(sp, m)) } }; - let path = self.ecx.path_global(sp, rtpath("Argument")); + let path = self.ecx.path_global(sp, self.rtpath("Argument")); let s = self.ecx.expr_struct(sp, path, ~[ self.ecx.field_imm(sp, self.ecx.ident_of("position"), pos), self.ecx.field_imm(sp, self.ecx.ident_of("format"), fmt), self.ecx.field_imm(sp, self.ecx.ident_of("method"), method), ]); - self.ecx.expr_call_global(sp, rtpath("Argument"), ~[s]) + self.ecx.expr_call_global(sp, self.rtpath("Argument"), ~[s]) } } } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 8fbaea7ac1ee0..52ff3798f1b6b 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -75,14 +75,12 @@ pub trait Folder { } fn fold_struct_field(&mut self, sf: &StructField) -> StructField { - let fold_attribute = |x| fold_attribute_(x, self); - Spanned { node: ast::StructField_ { kind: sf.node.kind, id: self.new_id(sf.node.id), ty: self.fold_ty(sf.node.ty), - attrs: sf.node.attrs.map(|e| fold_attribute(*e)) + attrs: sf.node.attrs.map(|e| fold_attribute_(*e, self)) }, span: self.new_span(sf.span) } @@ -225,8 +223,7 @@ pub trait Folder { } } - let fold_attribute = |x| fold_attribute_(x, self); - let attrs = v.node.attrs.map(|x| fold_attribute(*x)); + let attrs = v.node.attrs.map(|x| fold_attribute_(*x, self)); let de = match v.node.disr_expr { Some(e) => Some(self.fold_expr(e)), @@ -323,8 +320,7 @@ fn fold_meta_item_(mi: @MetaItem, fld: &mut T) -> @MetaItem { match mi.node { MetaWord(ref id) => MetaWord((*id).clone()), MetaList(ref id, ref mis) => { - let fold_meta_item = |x| fold_meta_item_(x, fld); - MetaList((*id).clone(), mis.map(|e| fold_meta_item(*e))) + MetaList((*id).clone(), mis.map(|e| fold_meta_item_(*e, fld))) } MetaNameValue(ref id, ref s) => { MetaNameValue((*id).clone(), (*s).clone()) @@ -604,23 +600,18 @@ pub fn noop_fold_mod(m: &Mod, folder: &mut T) -> Mod { } pub fn noop_fold_crate(c: Crate, folder: &mut T) -> Crate { - let fold_meta_item = |x| fold_meta_item_(x, folder); - let fold_attribute = |x| fold_attribute_(x, folder); - Crate { module: folder.fold_mod(&c.module), - attrs: c.attrs.map(|x| fold_attribute(*x)), - config: c.config.map(|x| fold_meta_item(*x)), + attrs: c.attrs.map(|x| fold_attribute_(*x, folder)), + config: c.config.map(|x| fold_meta_item_(*x, folder)), span: folder.new_span(c.span), } } pub fn noop_fold_item(i: &Item, folder: &mut T) -> SmallVector<@Item> { - let fold_attribute = |x| fold_attribute_(x, folder); - SmallVector::one(@Item { ident: folder.fold_ident(i.ident), - attrs: i.attrs.map(|e| fold_attribute(*e)), + attrs: i.attrs.map(|e| fold_attribute_(*e, folder)), id: folder.new_id(i.id), node: folder.fold_item_underscore(&i.node), vis: i.vis, @@ -711,8 +702,6 @@ pub fn noop_fold_pat(p: @Pat, folder: &mut T) -> @Pat { } pub fn noop_fold_expr(e: @Expr, folder: &mut T) -> @Expr { - let fold_field = |x| fold_field_(x, folder); - let node = match e.node { ExprVstore(e, v) => { ExprVstore(folder.fold_expr(e), v) @@ -824,7 +813,7 @@ pub fn noop_fold_expr(e: @Expr, folder: &mut T) -> @Expr { ExprMac(ref mac) => ExprMac(folder.fold_mac(mac)), ExprStruct(ref path, ref fields, maybe_expr) => { ExprStruct(folder.fold_path(path), - fields.map(|x| fold_field(*x)), + fields.map(|x| fold_field_(*x, folder)), maybe_expr.map(|x| folder.fold_expr(x))) }, ExprParen(ex) => ExprParen(folder.fold_expr(ex)) diff --git a/src/test/compile-fail/arc-rw-read-mode-shouldnt-escape.rs b/src/test/compile-fail/arc-rw-read-mode-shouldnt-escape.rs index dfdbd882acff6..653f37d96d473 100644 --- a/src/test/compile-fail/arc-rw-read-mode-shouldnt-escape.rs +++ b/src/test/compile-fail/arc-rw-read-mode-shouldnt-escape.rs @@ -15,7 +15,7 @@ fn main() { let mut y = None; x.write_downgrade(|write_mode| { y = Some(x.downgrade(write_mode)); - //~^ ERROR cannot infer an appropriate lifetime + //~^ ERROR cannot infer }); y.unwrap(); // Adding this line causes a method unification failure instead diff --git a/src/test/compile-fail/borrowck-assign-comp-idx.rs b/src/test/compile-fail/borrowck-assign-comp-idx.rs index b50a657eae79a..dec248a3015d5 100644 --- a/src/test/compile-fail/borrowck-assign-comp-idx.rs +++ b/src/test/compile-fail/borrowck-assign-comp-idx.rs @@ -32,9 +32,9 @@ fn b() { let mut p = ~[1]; - borrow(p, || { - p[0] = 5; //~ ERROR cannot assign to - }); + borrow( + p, + || p[0] = 5); //~ ERROR cannot borrow `p` as mutable } fn c() { diff --git a/src/test/compile-fail/borrowck-autoref-3261.rs b/src/test/compile-fail/borrowck-autoref-3261.rs index b8735fb2d3cf9..29016a2f44f14 100644 --- a/src/test/compile-fail/borrowck-autoref-3261.rs +++ b/src/test/compile-fail/borrowck-autoref-3261.rs @@ -21,13 +21,14 @@ impl X { fn main() { let mut x = X(Right(main)); - (&mut x).with(|opt| { - match opt { - &Right(ref f) => { - x = X(Left((0,0))); //~ ERROR cannot assign to `x` - (*f)() - }, - _ => fail!() - } - }) + (&mut x).with( + |opt| { //~ ERROR cannot borrow `x` as mutable more than once at a time + match opt { + &Right(ref f) => { + x = X(Left((0,0))); + (*f)() + }, + _ => fail!() + } + }) } diff --git a/src/test/compile-fail/borrowck-borrow-mut-object-twice.rs b/src/test/compile-fail/borrowck-borrow-mut-object-twice.rs index c338aac2ddf5e..34b9c31fdd82f 100644 --- a/src/test/compile-fail/borrowck-borrow-mut-object-twice.rs +++ b/src/test/compile-fail/borrowck-borrow-mut-object-twice.rs @@ -18,7 +18,7 @@ trait Foo { fn test(x: &mut Foo) { let _y = x.f1(); - x.f2(); //~ ERROR cannot borrow `*x` because it is already borrowed as mutable + x.f2(); //~ ERROR cannot borrow `*x` as mutable } fn main() {} diff --git a/src/test/compile-fail/borrowck-closures-mut-and-imm.rs b/src/test/compile-fail/borrowck-closures-mut-and-imm.rs new file mode 100644 index 0000000000000..006f475b29d4e --- /dev/null +++ b/src/test/compile-fail/borrowck-closures-mut-and-imm.rs @@ -0,0 +1,79 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests that two closures cannot simultaneously have mutable +// and immutable access to the variable. Issue #6801. + +fn get(x: &int) -> int { + *x +} + +fn set(x: &mut int) { + *x = 4; +} + +fn a() { + let mut x = 3; + let c1 = || x = 4; + let c2 = || x * 5; //~ ERROR cannot borrow `x` +} + +fn b() { + let mut x = 3; + let c1 = || set(&mut x); + let c2 = || get(&x); //~ ERROR cannot borrow `x` +} + +fn c() { + let mut x = 3; + let c1 = || set(&mut x); + let c2 = || x * 5; //~ ERROR cannot borrow `x` +} + +fn d() { + let mut x = 3; + let c2 = || x * 5; + x = 5; //~ ERROR cannot assign +} + +fn e() { + let mut x = 3; + let c1 = || get(&x); + x = 5; //~ ERROR cannot assign +} + +fn f() { + let mut x = ~3; + let c1 = || get(&*x); + *x = 5; //~ ERROR cannot assign +} + +fn g() { + struct Foo { + f: ~int + } + + let mut x = ~Foo { f: ~3 }; + let c1 = || get(&*x.f); + *x.f = 5; //~ ERROR cannot assign to `*x.f` +} + +fn h() { + struct Foo { + f: ~int + } + + let mut x = ~Foo { f: ~3 }; + let c1 = || get(&*x.f); + let c2 = || *x.f = 5; //~ ERROR cannot borrow `x` as mutable +} + +fn main() { +} diff --git a/src/test/compile-fail/borrowck-closures-mut-of-imm.rs b/src/test/compile-fail/borrowck-closures-mut-of-imm.rs new file mode 100644 index 0000000000000..cdfb569762de3 --- /dev/null +++ b/src/test/compile-fail/borrowck-closures-mut-of-imm.rs @@ -0,0 +1,31 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests that two closures cannot simultaneously have mutable +// and immutable access to the variable. Issue #6801. + +fn get(x: &int) -> int { + *x +} + +fn set(x: &mut int) { + *x = 4; +} + +fn a(x: &int) { + let c1 = || set(&mut *x); + //~^ ERROR cannot borrow + let c2 = || set(&mut *x); + //~^ ERROR closure requires unique access to `x` + //~^^ ERROR cannot borrow +} + +fn main() { +} diff --git a/src/test/compile-fail/borrowck-closures-two-mut.rs b/src/test/compile-fail/borrowck-closures-two-mut.rs new file mode 100644 index 0000000000000..570249aed443b --- /dev/null +++ b/src/test/compile-fail/borrowck-closures-two-mut.rs @@ -0,0 +1,56 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests that two closures cannot simultaneously have mutable +// access to the variable, whether that mutable access be used +// for direct assignment or for taking mutable ref. Issue #6801. + +fn a() { + let mut x = 3; + let c1 = || x = 4; + let c2 = || x = 5; //~ ERROR cannot borrow `x` as mutable more than once +} + +fn set(x: &mut int) { + *x = 4; +} + +fn b() { + let mut x = 3; + let c1 = || set(&mut x); + let c2 = || set(&mut x); //~ ERROR cannot borrow `x` as mutable more than once +} + +fn c() { + let mut x = 3; + let c1 = || x = 5; + let c2 = || set(&mut x); //~ ERROR cannot borrow `x` as mutable more than once +} + +fn d() { + let mut x = 3; + let c1 = || x = 5; + let c2 = || { let _y = || set(&mut x); }; // (nested closure) + //~^ ERROR cannot borrow `x` as mutable more than once +} + +fn g() { + struct Foo { + f: ~int + } + + let mut x = ~Foo { f: ~3 }; + let c1 = || set(&mut *x.f); + let c2 = || set(&mut *x.f); + //~^ ERROR cannot borrow `x` as mutable more than once +} + +fn main() { +} diff --git a/src/test/compile-fail/borrowck-closures-unique.rs b/src/test/compile-fail/borrowck-closures-unique.rs new file mode 100644 index 0000000000000..80d942e58d15c --- /dev/null +++ b/src/test/compile-fail/borrowck-closures-unique.rs @@ -0,0 +1,50 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests that a closure which requires mutable access to the referent +// of an `&mut` requires a "unique" borrow -- that is, the variable to +// be borrowed (here, `x`) will not be borrowed *mutably*, but +// may be *immutable*, but we cannot allow +// multiple borrows. + +fn get(x: &int) -> int { + *x +} + +fn set(x: &mut int) -> int { + *x +} + +fn a(x: &mut int) { + let c1 = || get(x); + let c2 = || get(x); +} + +fn b(x: &mut int) { + let c1 = || get(x); + let c2 = || set(x); //~ ERROR closure requires unique access to `x` +} + +fn c(x: &mut int) { + let c1 = || get(x); + let c2 = || { get(x); set(x); }; //~ ERROR closure requires unique access to `x` +} + +fn d(x: &mut int) { + let c1 = || set(x); + let c2 = || set(x); //~ ERROR closure requires unique access to `x` +} + +fn e(x: &mut int) { + let c1: || = || x = fail!(); //~ ERROR closure cannot assign to immutable argument `x` +} + +fn main() { +} diff --git a/src/test/compile-fail/borrowck-closures-use-after-free.rs b/src/test/compile-fail/borrowck-closures-use-after-free.rs new file mode 100644 index 0000000000000..38c13b1fce94f --- /dev/null +++ b/src/test/compile-fail/borrowck-closures-use-after-free.rs @@ -0,0 +1,31 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests that a closure which mutates a local variable +// cannot also be supplied a borrowed version of that +// variable's contents. Issue #11192. + +struct Foo { + x: int +} + +impl Drop for Foo { + fn drop(&mut self) { + println!("drop {}", self.x); + } +} + +fn main() { + let mut ptr = ~Foo { x: 0 }; + let test = |foo: &Foo| { + ptr = ~Foo { x: ptr.x + 1 }; + }; + test(ptr); //~ ERROR cannot borrow `*ptr` +} diff --git a/src/test/compile-fail/borrowck-insert-during-each.rs b/src/test/compile-fail/borrowck-insert-during-each.rs index 94ed47b01e1ee..38ff840ada408 100644 --- a/src/test/compile-fail/borrowck-insert-during-each.rs +++ b/src/test/compile-fail/borrowck-insert-during-each.rs @@ -23,9 +23,10 @@ impl Foo { } fn bar(f: &mut Foo) { - f.foo(|a| { - f.n.insert(*a); //~ ERROR cannot borrow - }) + f.foo( + |a| { //~ ERROR closure requires unique access to `f` + f.n.insert(*a); + }) } fn main() { diff --git a/src/test/compile-fail/borrowck-loan-blocks-move-cc.rs b/src/test/compile-fail/borrowck-loan-blocks-move-cc.rs index c193288468a20..18fd411101834 100644 --- a/src/test/compile-fail/borrowck-loan-blocks-move-cc.rs +++ b/src/test/compile-fail/borrowck-loan-blocks-move-cc.rs @@ -21,7 +21,9 @@ fn box_imm() { info!("v={}", *v); //~^ ERROR cannot move `v` into closure }); +} +fn box_imm_explicit() { let v = ~3; let _w = &v; task::spawn(proc() { diff --git a/src/test/compile-fail/borrowck-loan-blocks-mut-uniq.rs b/src/test/compile-fail/borrowck-loan-blocks-mut-uniq.rs index a54476abb26d6..6a0d3ef82fb21 100644 --- a/src/test/compile-fail/borrowck-loan-blocks-mut-uniq.rs +++ b/src/test/compile-fail/borrowck-loan-blocks-mut-uniq.rs @@ -14,11 +14,12 @@ fn borrow(v: &int, f: |x: &int|) { fn box_imm() { let mut v = ~3; - borrow(v, |w| { - v = ~4; //~ ERROR cannot assign to `v` because it is borrowed - assert_eq!(*v, 3); - assert_eq!(*w, 4); - }) + borrow(v, + |w| { //~ ERROR cannot borrow `v` as mutable + v = ~4; + assert_eq!(*v, 3); + assert_eq!(*w, 4); + }) } fn main() { diff --git a/src/test/compile-fail/borrowck-loan-rcvr.rs b/src/test/compile-fail/borrowck-loan-rcvr.rs index a0071938ce437..dbeeb5213069b 100644 --- a/src/test/compile-fail/borrowck-loan-rcvr.rs +++ b/src/test/compile-fail/borrowck-loan-rcvr.rs @@ -32,8 +32,8 @@ fn a() { p.impurem(); // But in this case we do not honor the loan: - p.blockm(|| { - p.x = 10; //~ ERROR cannot assign + p.blockm(|| { //~ ERROR cannot borrow `p` as mutable + p.x = 10; }) } diff --git a/src/test/compile-fail/borrowck-loan-vec-content.rs b/src/test/compile-fail/borrowck-loan-vec-content.rs index 6527ddfa2ecf6..0e721d7107f8b 100644 --- a/src/test/compile-fail/borrowck-loan-vec-content.rs +++ b/src/test/compile-fail/borrowck-loan-vec-content.rs @@ -23,9 +23,11 @@ fn has_mut_vec_and_does_not_try_to_change_it() { fn has_mut_vec_but_tries_to_change_it() { let mut v = ~[1, 2, 3]; - takes_imm_elt(&v[0], || { - v[1] = 4; //~ ERROR cannot assign - }) + takes_imm_elt( + &v[0], + || { //~ ERROR cannot borrow `v` as mutable + v[1] = 4; + }) } fn main() { diff --git a/src/test/compile-fail/borrowck-move-by-capture.rs b/src/test/compile-fail/borrowck-move-by-capture.rs index 5af1f8312aaa5..f3869e5c9fdba 100644 --- a/src/test/compile-fail/borrowck-move-by-capture.rs +++ b/src/test/compile-fail/borrowck-move-by-capture.rs @@ -9,10 +9,8 @@ // except according to those terms. pub fn main() { - // FIXME(#2202) - Due to the way that borrowck treats closures, - // you get two error reports here. let bar = ~3; - let _g = || { //~ ERROR capture of moved value - let _h: proc() -> int = proc() *bar; //~ ERROR capture of moved value + let _g = || { + let _h: proc() -> int = proc() *bar; //~ ERROR cannot move out of captured outer variable }; } diff --git a/src/test/compile-fail/borrowck-object-lifetime.rs b/src/test/compile-fail/borrowck-object-lifetime.rs index daeb5b7285716..92b77d8243efc 100644 --- a/src/test/compile-fail/borrowck-object-lifetime.rs +++ b/src/test/compile-fail/borrowck-object-lifetime.rs @@ -17,7 +17,7 @@ fn borrowed_receiver<'a>(x: &'a Foo) -> &'a () { } fn owned_receiver(x: ~Foo) -> &() { - x.borrowed() //~ ERROR borrowed value does not live long enough + x.borrowed() //~ ERROR `*x` does not live long enough } fn mut_owned_receiver(mut x: ~Foo) { diff --git a/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs b/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs index 909af7da96084..cca8ed93388bc 100644 --- a/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs +++ b/src/test/compile-fail/borrowck-vec-pattern-move-tail.rs @@ -14,6 +14,6 @@ fn main() { [1, 2, ..tail] => tail, _ => unreachable!() }; - a[0] = 0; //~ ERROR cannot assign to `a[]` because it is borrowed + a[0] = 0; //~ ERROR cannot assign to `a[..]` because it is borrowed t[0]; } diff --git a/src/test/compile-fail/borrowck-vec-pattern-nesting.rs b/src/test/compile-fail/borrowck-vec-pattern-nesting.rs index be66dcf372ed3..cb1a7d393a88f 100644 --- a/src/test/compile-fail/borrowck-vec-pattern-nesting.rs +++ b/src/test/compile-fail/borrowck-vec-pattern-nesting.rs @@ -12,7 +12,7 @@ fn a() { let mut vec = ~[~1, ~2, ~3]; match vec { [~ref _a] => { - vec[0] = ~4; //~ ERROR cannot assign to `(*vec)[]` because it is borrowed + vec[0] = ~4; //~ ERROR cannot assign } _ => fail!("foo") } @@ -22,7 +22,7 @@ fn b() { let mut vec = ~[~1, ~2, ~3]; match vec { [.._b] => { - vec[0] = ~4; //~ ERROR cannot assign to `(*vec)[]` because it is borrowed + vec[0] = ~4; //~ ERROR cannot assign } } } diff --git a/src/test/compile-fail/borrowck-vec-pattern-tail-element-loan.rs b/src/test/compile-fail/borrowck-vec-pattern-tail-element-loan.rs index cf20d57ac58be..b471d40a950f1 100644 --- a/src/test/compile-fail/borrowck-vec-pattern-tail-element-loan.rs +++ b/src/test/compile-fail/borrowck-vec-pattern-tail-element-loan.rs @@ -11,7 +11,7 @@ fn a() -> &int { let vec = ~[1, 2, 3, 4]; let tail = match vec { - [_a, ..tail] => &tail[0], //~ ERROR borrowed value does not live long enough + [_a, ..tail] => &tail[0], //~ ERROR `vec[..]` does not live long enough _ => fail!("foo") }; tail diff --git a/src/test/compile-fail/issue-3154.rs b/src/test/compile-fail/issue-3154.rs index 1cfed882d6c51..dcb705856d997 100644 --- a/src/test/compile-fail/issue-3154.rs +++ b/src/test/compile-fail/issue-3154.rs @@ -13,7 +13,7 @@ struct thing<'a, Q> { } fn thing(x: &Q) -> thing { - thing{ x: x } //~ ERROR cannot infer an appropriate lifetime + thing{ x: x } //~ ERROR cannot infer } fn main() { diff --git a/src/test/compile-fail/issue-4335.rs b/src/test/compile-fail/issue-4335.rs index 4cfa543a93fbe..8e4aa799d1fb9 100644 --- a/src/test/compile-fail/issue-4335.rs +++ b/src/test/compile-fail/issue-4335.rs @@ -11,7 +11,7 @@ fn id(t: T) -> T { t } fn f<'r, T>(v: &'r T) -> 'r || -> T { - id(|| *v) //~ ERROR cannot infer an appropriate lifetime + id(|| *v) //~ ERROR cannot infer } fn main() { diff --git a/src/test/compile-fail/kindck-owned-trait-contains.rs b/src/test/compile-fail/kindck-owned-trait-contains.rs index 5fe9b13f83bef..ed1725f3240fa 100644 --- a/src/test/compile-fail/kindck-owned-trait-contains.rs +++ b/src/test/compile-fail/kindck-owned-trait-contains.rs @@ -24,7 +24,7 @@ fn main() { let y = { let tmp0 = 3; - let tmp1 = &tmp0; //~ ERROR borrowed value does not live long enough + let tmp1 = &tmp0; //~ ERROR `tmp0` does not live long enough repeater(tmp1) }; assert!(3 == *(y.get())); diff --git a/src/test/compile-fail/moves-based-on-type-access-to-field.rs b/src/test/compile-fail/moves-based-on-type-access-to-field.rs index f07d4fcf70c20..1557b290c2cb1 100644 --- a/src/test/compile-fail/moves-based-on-type-access-to-field.rs +++ b/src/test/compile-fail/moves-based-on-type-access-to-field.rs @@ -17,13 +17,13 @@ fn touch(_a: &A) {} fn f10() { let x = Foo { f: ~"hi", y: 3 }; - consume(x.f); //~ NOTE `x.f` moved here + consume(x.f); touch(&x.y); //~ ERROR use of partially moved value: `x` } fn f20() { let x = ~[~"hi"]; - consume(x[0]); //~ NOTE `(*x)[]` moved here + consume(x[0]); touch(&x[0]); //~ ERROR use of partially moved value: `x` } diff --git a/src/test/compile-fail/mut-cant-alias.rs b/src/test/compile-fail/mut-cant-alias.rs index 5b8079b832e79..e3e2ace71adc0 100644 --- a/src/test/compile-fail/mut-cant-alias.rs +++ b/src/test/compile-fail/mut-cant-alias.rs @@ -14,5 +14,5 @@ fn main() { let m = RefCell::new(0); let mut b = m.borrow_mut(); let b1 = b.get(); - let b2 = b.get(); //~ ERROR cannot borrow `b` because it is already borrowed as mutable + let b2 = b.get(); //~ ERROR cannot borrow } diff --git a/src/test/compile-fail/mut-ptr-cant-outlive-ref.rs b/src/test/compile-fail/mut-ptr-cant-outlive-ref.rs index ca276700e8b61..2e5cf1b504b65 100644 --- a/src/test/compile-fail/mut-ptr-cant-outlive-ref.rs +++ b/src/test/compile-fail/mut-ptr-cant-outlive-ref.rs @@ -15,6 +15,6 @@ fn main() { let p; { let b = m.borrow(); - p = b.get(); //~ ERROR borrowed value does not live long enough + p = b.get(); //~ ERROR `b` does not live long enough } } diff --git a/src/test/compile-fail/regionck-closure-lifetimes.rs b/src/test/compile-fail/regionck-closure-lifetimes.rs index f66b17d68c76f..ec51f2dc21244 100644 --- a/src/test/compile-fail/regionck-closure-lifetimes.rs +++ b/src/test/compile-fail/regionck-closure-lifetimes.rs @@ -18,7 +18,7 @@ fn env<'a>(_: &'a uint, blk: |p: 'a |||) { let mut state = 0; let statep = &mut state; - blk(|| *statep = 1); //~ ERROR cannot infer an appropriate lifetime + blk(|| *statep = 1); //~ ERROR cannot infer } fn no_env_no_for<'a>(_: &'a uint, blk: |p: 'a |||) { @@ -40,7 +40,7 @@ fn repeating_loop() { let state = 0; loop { - closure = || state; //~ ERROR cannot infer an appropriate lifetime + closure = || state; //~ ERROR cannot infer break; } @@ -56,7 +56,7 @@ fn repeating_while() { let state = 0; while true { - closure = || state; //~ ERROR cannot infer an appropriate lifetime + closure = || state; //~ ERROR cannot infer break; } diff --git a/src/test/compile-fail/regions-addr-of-arg.rs b/src/test/compile-fail/regions-addr-of-arg.rs index ff13548b4946f..3e568180b53a4 100644 --- a/src/test/compile-fail/regions-addr-of-arg.rs +++ b/src/test/compile-fail/regions-addr-of-arg.rs @@ -12,7 +12,7 @@ // bounded by the current function call. fn foo(a: int) { - let _p: &'static int = &a; //~ ERROR borrowed value does not live long enough + let _p: &'static int = &a; //~ ERROR `a` does not live long enough } fn bar(a: int) { @@ -20,7 +20,7 @@ fn bar(a: int) { } fn zed<'a>(a: int) -> &'a int { - &a //~ ERROR borrowed value does not live long enough + &a //~ ERROR `a` does not live long enough } fn main() { diff --git a/src/test/compile-fail/regions-addr-of-self.rs b/src/test/compile-fail/regions-addr-of-self.rs index 2cd96735a0709..ce89b66cd5b94 100644 --- a/src/test/compile-fail/regions-addr-of-self.rs +++ b/src/test/compile-fail/regions-addr-of-self.rs @@ -14,8 +14,7 @@ struct dog { impl dog { pub fn chase_cat(&mut self) { - let p: &'static mut uint = &mut self.cats_chased; - //~^ ERROR cannot infer an appropriate lifetime + let p: &'static mut uint = &mut self.cats_chased; //~ ERROR cannot infer *p += 1u; } diff --git a/src/test/compile-fail/regions-addr-of-upvar-self.rs b/src/test/compile-fail/regions-addr-of-upvar-self.rs index c8fe60a2490fa..7a146c043c838 100644 --- a/src/test/compile-fail/regions-addr-of-upvar-self.rs +++ b/src/test/compile-fail/regions-addr-of-upvar-self.rs @@ -17,8 +17,7 @@ struct dog { impl dog { pub fn chase_cat(&mut self) { let _f = || { - let p: &'static mut uint = &mut self.food; - //~^ ERROR cannot infer an appropriate lifetime + let p: &'static mut uint = &mut self.food; //~ ERROR cannot infer *p = 3u; }; } diff --git a/src/test/compile-fail/regions-bounds.rs b/src/test/compile-fail/regions-bounds.rs index f74244c498437..5ef043634fbe3 100644 --- a/src/test/compile-fail/regions-bounds.rs +++ b/src/test/compile-fail/regions-bounds.rs @@ -17,12 +17,12 @@ struct a_class<'a> { x:&'a int } fn a_fn1<'a,'b>(e: an_enum<'a>) -> an_enum<'b> { return e; //~ ERROR mismatched types: expected `an_enum<'b>` but found `an_enum<'a>` - //~^ ERROR cannot infer an appropriate lifetime + //~^ ERROR cannot infer } fn a_fn3<'a,'b>(e: a_class<'a>) -> a_class<'b> { return e; //~ ERROR mismatched types: expected `a_class<'b>` but found `a_class<'a>` - //~^ ERROR cannot infer an appropriate lifetime + //~^ ERROR cannot infer } fn main() { } diff --git a/src/test/compile-fail/regions-creating-enums3.rs b/src/test/compile-fail/regions-creating-enums3.rs index 9e36ecc2b75ec..2c3f39795a4f0 100644 --- a/src/test/compile-fail/regions-creating-enums3.rs +++ b/src/test/compile-fail/regions-creating-enums3.rs @@ -14,7 +14,7 @@ enum ast<'a> { } fn mk_add_bad1<'a,'b>(x: &'a ast<'a>, y: &'b ast<'b>) -> ast<'a> { - add(x, y) //~ ERROR cannot infer an appropriate lifetime + add(x, y) //~ ERROR cannot infer } fn main() { diff --git a/src/test/compile-fail/regions-creating-enums4.rs b/src/test/compile-fail/regions-creating-enums4.rs index 7683b678b2b6a..0cd5a97596045 100644 --- a/src/test/compile-fail/regions-creating-enums4.rs +++ b/src/test/compile-fail/regions-creating-enums4.rs @@ -14,7 +14,7 @@ enum ast<'a> { } fn mk_add_bad2<'a>(x: &'a ast<'a>, y: &'a ast<'a>, z: &ast) -> ast { - add(x, y) //~ ERROR cannot infer an appropriate lifetime + add(x, y) //~ ERROR cannot infer } fn main() { diff --git a/src/test/compile-fail/regions-escape-loop-via-variable.rs b/src/test/compile-fail/regions-escape-loop-via-variable.rs index 19bd0bf9747bb..f588655d1afa8 100644 --- a/src/test/compile-fail/regions-escape-loop-via-variable.rs +++ b/src/test/compile-fail/regions-escape-loop-via-variable.rs @@ -18,6 +18,6 @@ fn main() { loop { let x = 1 + *p; - p = &x; //~ ERROR borrowed value does not live long enough + p = &x; //~ ERROR `x` does not live long enough } } diff --git a/src/test/compile-fail/regions-escape-loop-via-vec.rs b/src/test/compile-fail/regions-escape-loop-via-vec.rs index 92e2cd73dfbd8..ccfcc52945daf 100644 --- a/src/test/compile-fail/regions-escape-loop-via-vec.rs +++ b/src/test/compile-fail/regions-escape-loop-via-vec.rs @@ -14,7 +14,7 @@ fn broken() { let mut _y = ~[&mut x]; while x < 10 { let mut z = x; - _y.push(&mut z); //~ ERROR borrowed value does not live long enough + _y.push(&mut z); //~ ERROR `z` does not live long enough x += 1; //~ ERROR cannot assign } } diff --git a/src/test/compile-fail/regions-free-region-ordering-callee.rs b/src/test/compile-fail/regions-free-region-ordering-callee.rs index 94c617b218266..9762e5c4690ec 100644 --- a/src/test/compile-fail/regions-free-region-ordering-callee.rs +++ b/src/test/compile-fail/regions-free-region-ordering-callee.rs @@ -20,13 +20,13 @@ fn ordering1<'a, 'b>(x: &'a &'b uint) -> &'a uint { fn ordering2<'a, 'b>(x: &'a &'b uint, y: &'a uint) -> &'b uint { // However, it is not safe to assume that 'b <= 'a - &*y //~ ERROR cannot infer an appropriate lifetime + &*y //~ ERROR cannot infer } fn ordering3<'a, 'b>(x: &'a uint, y: &'b uint) -> &'a &'b uint { // Do not infer an ordering from the return value. let z: &'b uint = &*x; - //~^ ERROR cannot infer an appropriate lifetime + //~^ ERROR cannot infer fail!(); } diff --git a/src/test/compile-fail/regions-free-region-ordering-caller1.rs b/src/test/compile-fail/regions-free-region-ordering-caller1.rs index 1408f75be896e..b117a1a647643 100644 --- a/src/test/compile-fail/regions-free-region-ordering-caller1.rs +++ b/src/test/compile-fail/regions-free-region-ordering-caller1.rs @@ -18,7 +18,7 @@ fn call1<'a>(x: &'a uint) { let y: uint = 3; let z: &'a & uint = &(&y); //~^ ERROR borrowed value does not live long enough - //~^^ ERROR borrowed value does not live long enough + //~^^ ERROR `y` does not live long enough } fn main() {} diff --git a/src/test/compile-fail/regions-free-region-ordering-incorrect.rs b/src/test/compile-fail/regions-free-region-ordering-incorrect.rs index 54352092794c6..6f6b6761735c2 100644 --- a/src/test/compile-fail/regions-free-region-ordering-incorrect.rs +++ b/src/test/compile-fail/regions-free-region-ordering-incorrect.rs @@ -24,7 +24,7 @@ impl<'b, T> Node<'b, T> { fn get<'a>(&'a self) -> &'b T { match self.next { Some(ref next) => next.get(), - None => &self.val //~ ERROR cannot infer an appropriate lifetime + None => &self.val //~ ERROR cannot infer } } } diff --git a/src/test/compile-fail/regions-freevar.rs b/src/test/compile-fail/regions-freevar.rs index 940a7f9afbbb3..af460dbdd7868 100644 --- a/src/test/compile-fail/regions-freevar.rs +++ b/src/test/compile-fail/regions-freevar.rs @@ -12,8 +12,7 @@ fn wants_static_fn(_x: 'static ||) {} fn main() { let i = 3; - wants_static_fn(|| { - //~^ ERROR cannot infer an appropriate lifetime due to conflicting requirements + wants_static_fn(|| { //~ ERROR cannot infer info!("i={}", i); }) } diff --git a/src/test/compile-fail/regions-glb-free-free.rs b/src/test/compile-fail/regions-glb-free-free.rs index 8f6754b34bc09..1aafd9057c266 100644 --- a/src/test/compile-fail/regions-glb-free-free.rs +++ b/src/test/compile-fail/regions-glb-free-free.rs @@ -24,7 +24,7 @@ mod argparse { impl<'a> Flag<'a> { pub fn set_desc(self, s: &str) -> Flag<'a> { - Flag { //~ ERROR cannot infer an appropriate lifetime + Flag { //~ ERROR cannot infer name: self.name, desc: s, max_count: self.max_count, diff --git a/src/test/compile-fail/regions-infer-at-fn-not-param.rs b/src/test/compile-fail/regions-infer-at-fn-not-param.rs index 46de570eaf4d0..ad6d1b2742d10 100644 --- a/src/test/compile-fail/regions-infer-at-fn-not-param.rs +++ b/src/test/compile-fail/regions-infer-at-fn-not-param.rs @@ -22,7 +22,7 @@ struct not_parameterized2 { fn take1(p: parameterized1) -> parameterized1 { p } //~^ ERROR mismatched types -//~^^ ERROR cannot infer an appropriate lifetime +//~^^ ERROR cannot infer fn take3(p: not_parameterized1) -> not_parameterized1 { p } fn take4(p: not_parameterized2) -> not_parameterized2 { p } diff --git a/src/test/compile-fail/regions-infer-call-3.rs b/src/test/compile-fail/regions-infer-call-3.rs index bb7c487005faa..66f958c789336 100644 --- a/src/test/compile-fail/regions-infer-call-3.rs +++ b/src/test/compile-fail/regions-infer-call-3.rs @@ -16,7 +16,7 @@ fn with(f: |x: &int| -> T) -> T { fn manip<'a>(x: &'a int) -> int { let z = with(|y| { select(x, y) }); - //~^ ERROR cannot infer an appropriate lifetime + //~^ ERROR cannot infer *z } diff --git a/src/test/compile-fail/regions-infer-not-param.rs b/src/test/compile-fail/regions-infer-not-param.rs index b5fce9e21bda7..6596a1d8c2384 100644 --- a/src/test/compile-fail/regions-infer-not-param.rs +++ b/src/test/compile-fail/regions-infer-not-param.rs @@ -23,11 +23,11 @@ struct indirect2<'a> { } fn take_direct(p: direct) -> direct { p } //~ ERROR mismatched types -//~^ ERROR cannot infer an appropriate lifetime +//~^ ERROR cannot infer fn take_indirect1(p: indirect1) -> indirect1 { p } fn take_indirect2(p: indirect2) -> indirect2 { p } //~ ERROR mismatched types -//~^ ERROR cannot infer an appropriate lifetime +//~^ ERROR cannot infer fn main() {} diff --git a/src/test/compile-fail/regions-infer-paramd-indirect.rs b/src/test/compile-fail/regions-infer-paramd-indirect.rs index 63d3338cc8955..e2f4f791652a5 100644 --- a/src/test/compile-fail/regions-infer-paramd-indirect.rs +++ b/src/test/compile-fail/regions-infer-paramd-indirect.rs @@ -32,7 +32,7 @@ impl<'a> set_f<'a> for c<'a> { fn set_f_bad(&self, b: @b) { self.f = b; //~ ERROR mismatched types: expected `@@&'a int` but found `@@&int` - //~^ ERROR cannot infer an appropriate lifetime + //~^ ERROR cannot infer } } diff --git a/src/test/compile-fail/regions-nested-fns-2.rs b/src/test/compile-fail/regions-nested-fns-2.rs index 8e9a7546541d0..60eae9ce80af1 100644 --- a/src/test/compile-fail/regions-nested-fns-2.rs +++ b/src/test/compile-fail/regions-nested-fns-2.rs @@ -12,9 +12,10 @@ fn ignore(_f: <'z>|&'z int| -> &'z int) {} fn nested() { let y = 3; - ignore(|z| { - if false { &y } else { z } //~ ERROR borrowed value does not live long enough - }); + ignore( + |z| { //~ ERROR `y` does not live long enough + if false { &y } else { z } + }); } fn main() {} diff --git a/src/test/compile-fail/regions-nested-fns.rs b/src/test/compile-fail/regions-nested-fns.rs index 11610f422a0c8..c66e5616b849a 100644 --- a/src/test/compile-fail/regions-nested-fns.rs +++ b/src/test/compile-fail/regions-nested-fns.rs @@ -12,7 +12,7 @@ fn ignore(t: T) {} fn nested<'x>(x: &'x int) { let y = 3; - let mut ay = &y; //~ ERROR cannot infer an appropriate lifetime + let mut ay = &y; //~ ERROR cannot infer ignore::< <'z>|&'z int|>(|z| { ay = x; @@ -22,7 +22,7 @@ fn nested<'x>(x: &'x int) { ignore::< <'z>|&'z int| -> &'z int>(|z| { if false { return x; } //~ ERROR mismatched types - //~^ ERROR cannot infer an appropriate lifetime + //~^ ERROR cannot infer if false { return ay; } return z; }); diff --git a/src/test/compile-fail/regions-ret-borrowed-1.rs b/src/test/compile-fail/regions-ret-borrowed-1.rs index 7eb5fa3c60be6..0c335b9d5575f 100644 --- a/src/test/compile-fail/regions-ret-borrowed-1.rs +++ b/src/test/compile-fail/regions-ret-borrowed-1.rs @@ -19,7 +19,7 @@ fn with(f: <'a>|x: &'a int| -> R) -> R { fn return_it<'a>() -> &'a int { with(|o| o) //~ ERROR mismatched types //~^ ERROR lifetime of return value does not outlive the function call - //~^^ ERROR cannot infer an appropriate lifetime + //~^^ ERROR cannot infer } fn main() { diff --git a/src/test/compile-fail/regions-ret-borrowed.rs b/src/test/compile-fail/regions-ret-borrowed.rs index 2f6f2f44cda58..469421751df2b 100644 --- a/src/test/compile-fail/regions-ret-borrowed.rs +++ b/src/test/compile-fail/regions-ret-borrowed.rs @@ -22,7 +22,7 @@ fn with(f: |x: &int| -> R) -> R { fn return_it() -> &int { with(|o| o) //~ ERROR mismatched types //~^ ERROR lifetime of return value does not outlive the function call - //~^^ ERROR cannot infer an appropriate lifetime + //~^^ ERROR cannot infer } fn main() { diff --git a/src/test/compile-fail/regions-steal-closure.rs b/src/test/compile-fail/regions-steal-closure.rs index 32fde3747c1d3..f80e5616bd555 100644 --- a/src/test/compile-fail/regions-steal-closure.rs +++ b/src/test/compile-fail/regions-steal-closure.rs @@ -19,7 +19,7 @@ fn box_it<'r>(x: 'r ||) -> closure_box<'r> { fn main() { let cl_box = { let mut i = 3; - box_it(|| i += 1) //~ ERROR cannot infer an appropriate lifetime + box_it(|| i += 1) //~ ERROR cannot infer }; (cl_box.cl)(); } diff --git a/src/test/compile-fail/regions-trait-3.rs b/src/test/compile-fail/regions-trait-3.rs index 52d80f49aa67f..9222fde7789ee 100644 --- a/src/test/compile-fail/regions-trait-3.rs +++ b/src/test/compile-fail/regions-trait-3.rs @@ -38,7 +38,7 @@ impl get_ctxt for Foo<'a> { } fn make_gc2<'a,'b>(foo: Foo<'a>) -> @get_ctxt<'b> { - return @foo as @get_ctxt; //~ ERROR cannot infer an appropriate lifetime + return @foo as @get_ctxt; //~ ERROR cannot infer } fn main() { diff --git a/src/test/compile-fail/sync-rwlock-read-mode-shouldnt-escape.rs b/src/test/compile-fail/sync-rwlock-read-mode-shouldnt-escape.rs index 0201f9dd51cbf..cb6c11537c609 100644 --- a/src/test/compile-fail/sync-rwlock-read-mode-shouldnt-escape.rs +++ b/src/test/compile-fail/sync-rwlock-read-mode-shouldnt-escape.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern: cannot infer an appropriate lifetime +// error-pattern: cannot infer extern mod sync; use sync::RWLock; fn main() { diff --git a/src/test/compile-fail/trait-coercion-generic-regions.rs b/src/test/compile-fail/trait-coercion-generic-regions.rs index 2aeebc0f1a8dc..76c877dcca821 100644 --- a/src/test/compile-fail/trait-coercion-generic-regions.rs +++ b/src/test/compile-fail/trait-coercion-generic-regions.rs @@ -24,7 +24,7 @@ impl Trait<&'static str> for Struct { fn main() { let person = ~"Fred"; - let person: &str = person; //~ ERROR borrowed value does not live long enough + let person: &str = person; //~ ERROR `person[..]` does not live long enough let s: ~Trait<&'static str> = ~Struct { person: person }; } diff --git a/src/test/compile-fail/vec-mut-iter-borrow.rs b/src/test/compile-fail/vec-mut-iter-borrow.rs index 21ffc1ae7f934..72dbd82e947f1 100644 --- a/src/test/compile-fail/vec-mut-iter-borrow.rs +++ b/src/test/compile-fail/vec-mut-iter-borrow.rs @@ -12,6 +12,6 @@ fn main() { let mut xs = ~[1, 2, 3, 4]; for x in xs.mut_iter() { - xs.push(1) //~ ERROR cannot borrow `xs` because it is already borrowed as mutable + xs.push(1) //~ ERROR cannot borrow `xs` } } diff --git a/src/test/run-pass/borrowck-closures-two-imm.rs b/src/test/run-pass/borrowck-closures-two-imm.rs new file mode 100644 index 0000000000000..3bd12b030411e --- /dev/null +++ b/src/test/run-pass/borrowck-closures-two-imm.rs @@ -0,0 +1,49 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests that two closures can simultaneously have immutable +// access to the variable, whether that immutable access be used +// for direct reads or for taking immutable ref. Also check +// that the main function can read the variable too while +// the closures are in scope. Issue #6801. + +fn a() -> int { + let mut x = 3; + x += 1; + let c1 = || x * 4; + let c2 = || x * 5; + c1() * c2() * x +} + +fn get(x: &int) -> int { + *x * 4 +} + +fn b() -> int { + let mut x = 3; + x += 1; + let c1 = || get(&x); + let c2 = || get(&x); + c1() * c2() * x +} + +fn c() -> int { + let mut x = 3; + x += 1; + let c1 = || x * 5; + let c2 = || get(&x); + c1() * c2() * x +} + +pub fn main() { + assert_eq!(a(), 1280); + assert_eq!(b(), 1024); + assert_eq!(c(), 1280); +} diff --git a/src/test/run-pass/lambda-infer-unresolved.rs b/src/test/run-pass/lambda-infer-unresolved.rs index 65f95f78ea86f..59baf63d28400 100644 --- a/src/test/run-pass/lambda-infer-unresolved.rs +++ b/src/test/run-pass/lambda-infer-unresolved.rs @@ -16,5 +16,6 @@ struct Refs { refs: ~[int], n: int } pub fn main() { let mut e = Refs{refs: ~[], n: 0}; let _f: || = || error!("{}", e.n); - e.refs.push(1); + let x: &[int] = e.refs; + assert_eq!(x.len(), 0); } diff --git a/src/test/run-pass/reflect-visit-data.rs b/src/test/run-pass/reflect-visit-data.rs deleted file mode 100644 index bc491cc9b7f2e..0000000000000 --- a/src/test/run-pass/reflect-visit-data.rs +++ /dev/null @@ -1,627 +0,0 @@ -// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// ignore-fast - -#[feature(managed_boxes)]; - -use std::cell::RefCell; -use std::libc::c_void; -use std::ptr; -use std::mem; -use std::unstable::intrinsics::{TyDesc, get_tydesc, visit_tydesc, TyVisitor, Disr, Opaque}; -use std::unstable::raw::Vec; - -#[doc = "High-level interfaces to `std::unstable::intrinsics::visit_ty` reflection system."] - -/// Trait for visitor that wishes to reflect on data. -trait movable_ptr { - fn move_ptr(&mut self, adjustment: |*c_void| -> *c_void); -} - -/// Helper function for alignment calculation. -#[inline(always)] -fn align(size: uint, align: uint) -> uint { - ((size + align) - 1u) & !(align - 1u) -} - -struct ptr_visit_adaptor(Inner); - -impl ptr_visit_adaptor { - fn inner<'a>(&'a mut self) -> &'a mut V { - let ptr_visit_adaptor(ref mut i) = *self; - &mut i.inner - } -} - -impl ptr_visit_adaptor { - - #[inline(always)] - pub fn bump(&mut self, sz: uint) { - self.inner().move_ptr(|p| ((p as uint) + sz) as *c_void) - } - - #[inline(always)] - pub fn align(&mut self, a: uint) { - self.inner().move_ptr(|p| align(p as uint, a) as *c_void) - } - - #[inline(always)] - pub fn align_to(&mut self) { - self.align(mem::min_align_of::()); - } - - #[inline(always)] - pub fn bump_past(&mut self) { - self.bump(mem::size_of::()); - } - -} - -impl TyVisitor for ptr_visit_adaptor { - - fn visit_bot(&mut self) -> bool { - self.align_to::<()>(); - if ! self.inner().visit_bot() { return false; } - self.bump_past::<()>(); - true - } - - fn visit_nil(&mut self) -> bool { - self.align_to::<()>(); - if ! self.inner().visit_nil() { return false; } - self.bump_past::<()>(); - true - } - - fn visit_bool(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_bool() { return false; } - self.bump_past::(); - true - } - - fn visit_int(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_int() { return false; } - self.bump_past::(); - true - } - - fn visit_i8(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_i8() { return false; } - self.bump_past::(); - true - } - - fn visit_i16(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_i16() { return false; } - self.bump_past::(); - true - } - - fn visit_i32(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_i32() { return false; } - self.bump_past::(); - true - } - - fn visit_i64(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_i64() { return false; } - self.bump_past::(); - true - } - - fn visit_uint(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_uint() { return false; } - self.bump_past::(); - true - } - - fn visit_u8(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_u8() { return false; } - self.bump_past::(); - true - } - - fn visit_u16(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_u16() { return false; } - self.bump_past::(); - true - } - - fn visit_u32(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_u32() { return false; } - self.bump_past::(); - true - } - - fn visit_u64(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_u64() { return false; } - self.bump_past::(); - true - } - - fn visit_f32(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_f32() { return false; } - self.bump_past::(); - true - } - - fn visit_f64(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_f64() { return false; } - self.bump_past::(); - true - } - - fn visit_char(&mut self) -> bool { - self.align_to::(); - if ! self.inner().visit_char() { return false; } - self.bump_past::(); - true - } - - fn visit_estr_box(&mut self) -> bool { - true - } - - fn visit_estr_uniq(&mut self) -> bool { - self.align_to::<~str>(); - if ! self.inner().visit_estr_uniq() { return false; } - self.bump_past::<~str>(); - true - } - - fn visit_estr_slice(&mut self) -> bool { - self.align_to::<&'static str>(); - if ! self.inner().visit_estr_slice() { return false; } - self.bump_past::<&'static str>(); - true - } - - fn visit_estr_fixed(&mut self, n: uint, - sz: uint, - align: uint) -> bool { - self.align(align); - if ! self.inner().visit_estr_fixed(n, sz, align) { return false; } - self.bump(sz); - true - } - - fn visit_box(&mut self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<@u8>(); - if ! self.inner().visit_box(mtbl, inner) { return false; } - self.bump_past::<@u8>(); - true - } - - fn visit_uniq(&mut self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<~u8>(); - if ! self.inner().visit_uniq(mtbl, inner) { return false; } - self.bump_past::<~u8>(); - true - } - - fn visit_ptr(&mut self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<*u8>(); - if ! self.inner().visit_ptr(mtbl, inner) { return false; } - self.bump_past::<*u8>(); - true - } - - fn visit_rptr(&mut self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<&'static u8>(); - if ! self.inner().visit_rptr(mtbl, inner) { return false; } - self.bump_past::<&'static u8>(); - true - } - - fn visit_unboxed_vec(&mut self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::>(); - // FIXME (#3732): Inner really has to move its own pointers on this one. - // or else possibly we could have some weird interface wherein we - // read-off a word from inner's pointers, but the read-word has to - // always be the same in all sub-pointers? Dubious. - if ! self.inner().visit_vec(mtbl, inner) { return false; } - true - } - - fn visit_vec(&mut self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<~[u8]>(); - if ! self.inner().visit_vec(mtbl, inner) { return false; } - self.bump_past::<~[u8]>(); - true - } - - fn visit_evec_box(&mut self, mtbl: uint, inner: *TyDesc) -> bool { - true - } - - fn visit_evec_uniq(&mut self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<~[u8]>(); - if ! self.inner().visit_evec_uniq(mtbl, inner) { return false; } - self.bump_past::<~[u8]>(); - true - } - - fn visit_evec_slice(&mut self, mtbl: uint, inner: *TyDesc) -> bool { - self.align_to::<&'static [u8]>(); - if ! self.inner().visit_evec_slice(mtbl, inner) { return false; } - self.bump_past::<&'static [u8]>(); - true - } - - fn visit_evec_fixed(&mut self, n: uint, sz: uint, align: uint, - mtbl: uint, inner: *TyDesc) -> bool { - self.align(align); - if ! self.inner().visit_evec_fixed(n, sz, align, mtbl, inner) { - return false; - } - self.bump(sz); - true - } - - fn visit_enter_rec(&mut self, n_fields: uint, sz: uint, align: uint) -> bool { - self.align(align); - if ! self.inner().visit_enter_rec(n_fields, sz, align) { return false; } - true - } - - fn visit_rec_field(&mut self, i: uint, name: &str, - mtbl: uint, inner: *TyDesc) -> bool { - if ! self.inner().visit_rec_field(i, name, mtbl, inner) { return false; } - true - } - - fn visit_leave_rec(&mut self, n_fields: uint, sz: uint, align: uint) -> bool { - if ! self.inner().visit_leave_rec(n_fields, sz, align) { return false; } - true - } - - fn visit_enter_class(&mut self, name: &str, named_fields: bool, n_fields: uint, sz: uint, - align: uint) -> bool { - self.align(align); - if ! self.inner().visit_enter_class(name, named_fields, n_fields, sz, align) { - return false; - } - true - } - - fn visit_class_field(&mut self, i: uint, name: &str, named: bool, - mtbl: uint, inner: *TyDesc) -> bool { - if ! self.inner().visit_class_field(i, name, named, mtbl, inner) { - return false; - } - true - } - - fn visit_leave_class(&mut self, name: &str, named_fields: bool, n_fields: uint, sz: uint, - align: uint) -> bool { - if ! self.inner().visit_leave_class(name, named_fields, n_fields, sz, align) { - return false; - } - true - } - - fn visit_enter_tup(&mut self, n_fields: uint, sz: uint, align: uint) -> bool { - self.align(align); - if ! self.inner().visit_enter_tup(n_fields, sz, align) { return false; } - true - } - - fn visit_tup_field(&mut self, i: uint, inner: *TyDesc) -> bool { - if ! self.inner().visit_tup_field(i, inner) { return false; } - true - } - - fn visit_leave_tup(&mut self, n_fields: uint, sz: uint, align: uint) -> bool { - if ! self.inner().visit_leave_tup(n_fields, sz, align) { return false; } - true - } - - fn visit_enter_fn(&mut self, purity: uint, proto: uint, - n_inputs: uint, retstyle: uint) -> bool { - if ! self.inner().visit_enter_fn(purity, proto, n_inputs, retstyle) { - return false - } - true - } - - fn visit_fn_input(&mut self, i: uint, mode: uint, inner: *TyDesc) -> bool { - if ! self.inner().visit_fn_input(i, mode, inner) { return false; } - true - } - - fn visit_fn_output(&mut self, retstyle: uint, variadic: bool, inner: *TyDesc) -> bool { - if ! self.inner().visit_fn_output(retstyle, variadic, inner) { return false; } - true - } - - fn visit_leave_fn(&mut self, purity: uint, proto: uint, - n_inputs: uint, retstyle: uint) -> bool { - if ! self.inner().visit_leave_fn(purity, proto, n_inputs, retstyle) { - return false; - } - true - } - - fn visit_enter_enum(&mut self, n_variants: uint, - get_disr: extern unsafe fn(ptr: *Opaque) -> Disr, - sz: uint, align: uint) - -> bool { - self.align(align); - if ! self.inner().visit_enter_enum(n_variants, get_disr, sz, align) { return false; } - true - } - - fn visit_enter_enum_variant(&mut self, variant: uint, - disr_val: Disr, - n_fields: uint, - name: &str) -> bool { - if ! self.inner().visit_enter_enum_variant(variant, disr_val, - n_fields, name) { - return false; - } - true - } - - fn visit_enum_variant_field(&mut self, i: uint, offset: uint, inner: *TyDesc) -> bool { - if ! self.inner().visit_enum_variant_field(i, offset, inner) { return false; } - true - } - - fn visit_leave_enum_variant(&mut self, variant: uint, - disr_val: Disr, - n_fields: uint, - name: &str) -> bool { - if ! self.inner().visit_leave_enum_variant(variant, disr_val, - n_fields, name) { - return false; - } - true - } - - fn visit_leave_enum(&mut self, n_variants: uint, - get_disr: extern unsafe fn(ptr: *Opaque) -> Disr, - sz: uint, align: uint) - -> bool { - if ! self.inner().visit_leave_enum(n_variants, get_disr, sz, align) { return false; } - true - } - - fn visit_trait(&mut self, name: &str) -> bool { - self.align_to::<~TyVisitor>(); - if ! self.inner().visit_trait(name) { return false; } - self.bump_past::<~TyVisitor>(); - true - } - - fn visit_param(&mut self, i: uint) -> bool { - if ! self.inner().visit_param(i) { return false; } - true - } - - fn visit_self(&mut self) -> bool { - self.align_to::<&'static u8>(); - if ! self.inner().visit_self() { return false; } - self.align_to::<&'static u8>(); - true - } - - fn visit_type(&mut self) -> bool { - if ! self.inner().visit_type() { return false; } - true - } -} - -struct my_visitor(@RefCell); - -#[deriving(Clone)] -struct Stuff { - ptr1: *c_void, - ptr2: *c_void, - vals: ~[~str] -} - -impl my_visitor { - pub fn get(&mut self, f: |T|) { - unsafe { - let my_visitor(s) = *self; - f((*((*s).get().ptr1 as *T)).clone()); - } - } - - pub fn visit_inner(&mut self, inner: *TyDesc) -> bool { - unsafe { - let my_visitor(s) = *self; - let u = my_visitor(s); - let mut v = ptr_visit_adaptor::(Inner {inner: u}); - visit_tydesc(inner, &mut v as &mut TyVisitor); - true - } - } -} - -struct Inner { inner: V } - -impl movable_ptr for my_visitor { - fn move_ptr(&mut self, adjustment: |*c_void| -> *c_void) { - let my_visitor(s) = *self; - let mut this = s.borrow_mut(); - this.get().ptr1 = adjustment(this.get().ptr1); - this.get().ptr2 = adjustment(this.get().ptr2); - } -} - -impl TyVisitor for my_visitor { - - fn visit_bot(&mut self) -> bool { true } - fn visit_nil(&mut self) -> bool { true } - fn visit_bool(&mut self) -> bool { - self.get::(|b| { - let my_visitor(s) = *self; - let mut this = s.borrow_mut(); - this.get().vals.push(b.to_str()); - }); - true - } - fn visit_int(&mut self) -> bool { - self.get::(|i| { - let my_visitor(s) = *self; - let mut this = s.borrow_mut(); - this.get().vals.push(i.to_str()); - }); - true - } - fn visit_i8(&mut self) -> bool { true } - fn visit_i16(&mut self) -> bool { true } - fn visit_i32(&mut self) -> bool { true } - fn visit_i64(&mut self) -> bool { true } - - fn visit_uint(&mut self) -> bool { true } - fn visit_u8(&mut self) -> bool { true } - fn visit_u16(&mut self) -> bool { true } - fn visit_u32(&mut self) -> bool { true } - fn visit_u64(&mut self) -> bool { true } - - fn visit_f32(&mut self) -> bool { true } - fn visit_f64(&mut self) -> bool { true } - - fn visit_char(&mut self) -> bool { true } - - fn visit_estr_box(&mut self) -> bool { true } - fn visit_estr_uniq(&mut self) -> bool { true } - fn visit_estr_slice(&mut self) -> bool { true } - fn visit_estr_fixed(&mut self, _n: uint, _sz: uint, - _align: uint) -> bool { true } - - fn visit_box(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true } - fn visit_uniq(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true } - fn visit_ptr(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true } - fn visit_rptr(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true } - - fn visit_vec(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true } - fn visit_unboxed_vec(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true } - fn visit_evec_box(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true } - fn visit_evec_uniq(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true } - fn visit_evec_slice(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true } - fn visit_evec_fixed(&mut self, _n: uint, _sz: uint, _align: uint, - _mtbl: uint, _inner: *TyDesc) -> bool { true } - - fn visit_enter_rec(&mut self, _n_fields: uint, - _sz: uint, _align: uint) -> bool { true } - fn visit_rec_field(&mut self, _i: uint, _name: &str, - _mtbl: uint, inner: *TyDesc) -> bool { - error!("rec field!"); - self.visit_inner(inner) - } - fn visit_leave_rec(&mut self, _n_fields: uint, - _sz: uint, _align: uint) -> bool { true } - - fn visit_enter_class(&mut self, _name: &str, _named_fields: bool, _n_fields: uint, - _sz: uint, _align: uint) -> bool { true } - fn visit_class_field(&mut self, _i: uint, _name: &str, _named: bool, - _mtbl: uint, inner: *TyDesc) -> bool { - self.visit_inner(inner) - } - fn visit_leave_class(&mut self, _name: &str, _named_fields: bool, _n_fields: uint, - _sz: uint, _align: uint) -> bool { true } - - fn visit_enter_tup(&mut self, _n_fields: uint, - _sz: uint, _align: uint) -> bool { true } - fn visit_tup_field(&mut self, _i: uint, inner: *TyDesc) -> bool { - error!("tup field!"); - self.visit_inner(inner) - } - fn visit_leave_tup(&mut self, _n_fields: uint, - _sz: uint, _align: uint) -> bool { true } - - fn visit_enter_enum(&mut self, _n_variants: uint, - _get_disr: extern unsafe fn(ptr: *Opaque) -> Disr, - _sz: uint, _align: uint) -> bool { - // FIXME (#3732): this needs to rewind between enum variants, or something. - true - } - fn visit_enter_enum_variant(&mut self, _variant: uint, - _disr_val: Disr, - _n_fields: uint, - _name: &str) -> bool { true } - fn visit_enum_variant_field(&mut self, _i: uint, _offset: uint, inner: *TyDesc) -> bool { - self.visit_inner(inner) - } - fn visit_leave_enum_variant(&mut self, _variant: uint, - _disr_val: Disr, - _n_fields: uint, - _name: &str) -> bool { true } - fn visit_leave_enum(&mut self, _n_variants: uint, - _get_disr: extern unsafe fn(ptr: *Opaque) -> Disr, - _sz: uint, _align: uint) -> bool { true } - - fn visit_enter_fn(&mut self, _purity: uint, _proto: uint, - _n_inputs: uint, _retstyle: uint) -> bool { true } - fn visit_fn_input(&mut self, _i: uint, _mode: uint, _inner: *TyDesc) -> bool { - true - } - fn visit_fn_output(&mut self, _retstyle: uint, _variadic: bool, _inner: *TyDesc) -> bool { - true - } - fn visit_leave_fn(&mut self, _purity: uint, _proto: uint, - _n_inputs: uint, _retstyle: uint) -> bool { true } - - - fn visit_trait(&mut self, _name: &str) -> bool { true } - fn visit_param(&mut self, _i: uint) -> bool { true } - fn visit_self(&mut self) -> bool { true } - fn visit_type(&mut self) -> bool { true } -} - -fn get_tydesc_for(_t: T) -> *TyDesc { - unsafe { - get_tydesc::() - } -} - -struct Triple { x: int, y: int, z: int } - -pub fn main() { - unsafe { - let r = (1,2,3,true,false, Triple {x:5,y:4,z:3}, (12,)); - let p = ptr::to_unsafe_ptr(&r) as *c_void; - let u = my_visitor(@RefCell::new(Stuff {ptr1: p, - ptr2: p, - vals: ~[]})); - let mut v = ptr_visit_adaptor(Inner {inner: u}); - let td = get_tydesc_for(r); - error!("tydesc sz: {}, align: {}", - (*td).size, (*td).align); - visit_tydesc(td, &mut v as &mut TyVisitor); - - let my_visitor(m) = u; - let mut ub = m.borrow_mut(); - let r = ub.get().vals.clone(); - for s in r.iter() { - println!("val: {}", *s); - } - error!("{:?}", ub.get().vals.clone()); - assert_eq!(ub.get().vals.clone(), - ~[ ~"1", ~"2", ~"3", ~"true", ~"false", ~"5", ~"4", ~"3", ~"12"]); - } -} diff --git a/src/test/run-pass/regions-copy-closure.rs b/src/test/run-pass/regions-copy-closure.rs index 718394e943fe9..55cb5c6268462 100644 --- a/src/test/run-pass/regions-copy-closure.rs +++ b/src/test/run-pass/regions-copy-closure.rs @@ -18,8 +18,11 @@ fn box_it<'r>(x: 'r ||) -> closure_box<'r> { pub fn main() { let mut i = 3; - let cl_box = box_it(|| i += 1); assert_eq!(i, 3); - (cl_box.cl)(); + { + let cl = || i += 1; + let cl_box = box_it(cl); + (cl_box.cl)(); + } assert_eq!(i, 4); }