From 3322595e896e95c3e19ca33c854ad529f2ef3c19 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 1 Apr 2013 22:32:37 -0700 Subject: [PATCH 1/5] Reason about nested free variables that appear in a function signature. In a nutshell, the idea is to (1) report an error if, for a region pointer `'a T`, the lifetime `'a` is longer than any lifetimes that appear in `T` (in other words, if a borrowed pointer outlives any portion of its contents) and then (2) use this to assume that in a function like `fn(self: &'a &'b T)`, the relationship `'a <= 'b` holds. This is needed for #5656. Fixes #5728. --- src/libcore/cmp.rs | 21 ++ src/libcore/str.rs | 6 +- src/libcore/tuple.rs | 1 - src/librustc/metadata/tydecode.rs | 3 +- src/librustc/metadata/tyencode.rs | 6 +- src/librustc/middle/astencode.rs | 5 +- src/librustc/middle/borrowck/check_loans.rs | 10 +- src/librustc/middle/borrowck/gather_loans.rs | 7 +- src/librustc/middle/borrowck/loan.rs | 4 +- src/librustc/middle/borrowck/mod.rs | 3 +- src/librustc/middle/borrowck/preserve.rs | 18 +- src/librustc/middle/check_match.rs | 7 +- src/librustc/middle/kind.rs | 100 ++++-- src/librustc/middle/region.rs | 308 +++++++++++++----- src/librustc/middle/ty.rs | 136 ++++---- src/librustc/middle/typeck/check/mod.rs | 12 +- src/librustc/middle/typeck/check/regionck.rs | 305 +++++++++++++---- .../middle/typeck/check/regionmanip.rs | 119 ++++++- src/librustc/middle/typeck/collect.rs | 3 +- .../middle/typeck/infer/region_inference.rs | 165 +++++++--- src/librustc/middle/typeck/infer/test.rs | 4 +- src/librustc/util/ppaux.rs | 39 ++- .../kindck-owned-trait-contains.rs | 3 +- .../regions-escape-via-trait-or-not.rs | 4 +- .../regions-free-region-ordering-callee.rs | 37 +++ .../regions-free-region-ordering-caller.rs | 40 +++ src/test/compile-fail/regions-trait-2.rs | 8 +- 27 files changed, 1027 insertions(+), 347 deletions(-) create mode 100644 src/test/compile-fail/regions-free-region-ordering-callee.rs create mode 100644 src/test/compile-fail/regions-free-region-ordering-caller.rs diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index f96575aaf41..bd4832b6faf 100644 --- a/src/libcore/cmp.rs +++ b/src/libcore/cmp.rs @@ -116,6 +116,19 @@ totalord_impl!(i64) totalord_impl!(int) totalord_impl!(uint) +pub fn cmp2( + a1: &A, b1: &B, + a2: &A, b2: &B) -> Ordering +{ + //! Compares (a1, b1) against (a2, b2), where the a values are more significant. + + match a1.cmp(a2) { + Less => Less, + Greater => Greater, + Equal => b1.cmp(b2) + } +} + /** * Trait for values that can be compared for a sort-order. * @@ -193,6 +206,14 @@ mod test { assert_eq!(12.cmp(-5), Greater); } + #[test] + fn test_cmp2() { + assert_eq!(cmp2(1, 2, 3, 4), Less); + assert_eq!(cmp2(3, 2, 3, 4), Less); + assert_eq!(cmp2(5, 2, 3, 4), Greater); + assert_eq!(cmp2(5, 5, 5, 4), Greater); + } + #[test] fn test_int_totaleq() { assert!(5.equals(&5)); diff --git a/src/libcore/str.rs b/src/libcore/str.rs index f1605309fb4..8d15d6afbda 100644 --- a/src/libcore/str.rs +++ b/src/libcore/str.rs @@ -301,7 +301,11 @@ pub fn slice_shift_char<'a>(s: &'a str) -> (char, &'a str) { /// Prepend a char to a string pub fn unshift_char(s: &mut ~str, ch: char) { - *s = from_char(ch) + *s; + // This could be more efficient. + let mut new_str = ~""; + new_str.push_char(ch); + new_str.push_str(*s); + *s = new_str; } /** diff --git a/src/libcore/tuple.rs b/src/libcore/tuple.rs index 35b8496f6c5..8234129e254 100644 --- a/src/libcore/tuple.rs +++ b/src/libcore/tuple.rs @@ -161,7 +161,6 @@ impl Ord for (A,) { fn gt(&self, other: &(A,)) -> bool { other.lt(&(*self)) } } - #[cfg(notest)] impl Eq for (A, B) { #[inline(always)] diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index 867fe0cd816..c1420822d06 100644 --- a/src/librustc/metadata/tydecode.rs +++ b/src/librustc/metadata/tydecode.rs @@ -239,7 +239,8 @@ fn parse_region(st: @mut PState) -> ty::Region { assert!(next(st) == '|'); let br = parse_bound_region(st); assert!(next(st) == ']'); - ty::re_free(id, br) + ty::re_free(ty::FreeRegion {scope_id: id, + bound_region: br}) } 's' => { let id = parse_int(st); diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs index 238f34b8c01..2e1dd8b6dad 100644 --- a/src/librustc/metadata/tyencode.rs +++ b/src/librustc/metadata/tyencode.rs @@ -146,12 +146,12 @@ fn enc_region(w: @io::Writer, cx: @ctxt, r: ty::Region) { w.write_char('b'); enc_bound_region(w, cx, br); } - ty::re_free(id, br) => { + ty::re_free(ref fr) => { w.write_char('f'); w.write_char('['); - w.write_int(id); + w.write_int(fr.scope_id); w.write_char('|'); - enc_bound_region(w, cx, br); + enc_bound_region(w, cx, fr.bound_region); w.write_char(']'); } ty::re_scope(nid) => { diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 68989b31425..b25b975830b 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -475,9 +475,12 @@ impl tr for ty::Region { fn tr(&self, xcx: @ExtendedDecodeContext) -> ty::Region { match *self { ty::re_bound(br) => ty::re_bound(br.tr(xcx)), - ty::re_free(id, br) => ty::re_free(xcx.tr_id(id), br.tr(xcx)), ty::re_scope(id) => ty::re_scope(xcx.tr_id(id)), ty::re_static | ty::re_infer(*) => *self, + ty::re_free(ref fr) => { + ty::re_free(ty::FreeRegion {scope_id: xcx.tr_id(fr.scope_id), + bound_region: fr.bound_region.tr(xcx)}) + } } } } diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs index 16035656742..721bb996262 100644 --- a/src/librustc/middle/borrowck/check_loans.rs +++ b/src/librustc/middle/borrowck/check_loans.rs @@ -128,9 +128,9 @@ pub impl CheckLoanCtxt { Some(e) => return Some(pc_cmt(*e)) } - match self.tcx().region_map.find(&scope_id) { + match self.tcx().region_maps.opt_encl_scope(scope_id) { None => return default_purity, - Some(&next_scope_id) => scope_id = next_scope_id + Some(next_scope_id) => scope_id = next_scope_id } } } @@ -146,9 +146,9 @@ pub impl CheckLoanCtxt { } } - match self.tcx().region_map.find(&scope_id) { + match self.tcx().region_maps.opt_encl_scope(scope_id) { None => return, - Some(&next_scope_id) => scope_id = next_scope_id, + Some(next_scope_id) => scope_id = next_scope_id, } } } @@ -270,7 +270,7 @@ pub impl CheckLoanCtxt { debug!("new_loans has length %?", new_loans.len()); - let par_scope_id = *self.tcx().region_map.get(&scope_id); + let par_scope_id = self.tcx().region_maps.encl_scope(scope_id); for self.walk_loans(par_scope_id) |old_loan| { debug!("old_loan=%?", self.bccx.loan_to_repr(old_loan)); diff --git a/src/librustc/middle/borrowck/gather_loans.rs b/src/librustc/middle/borrowck/gather_loans.rs index 233b1e191dc..4e61b573891 100644 --- a/src/librustc/middle/borrowck/gather_loans.rs +++ b/src/librustc/middle/borrowck/gather_loans.rs @@ -242,7 +242,7 @@ fn req_loans_in_expr(ex: @ast::expr, // (if used like `a.b(...)`), the call where it's an argument // (if used like `x(a.b)`), or the block (if used like `let x // = a.b`). - let scope_r = ty::re_scope(*self.tcx().region_map.get(&ex.id)); + let scope_r = self.tcx().region_maps.encl_region(ex.id); let rcvr_cmt = self.bccx.cat_expr(rcvr); self.guarantee_valid(rcvr_cmt, m_imm, scope_r); visit::visit_expr(ex, self, vt); @@ -524,7 +524,10 @@ pub impl GatherLoanCtxt { // immutable structures, this is just the converse I suppose) let scope_id = match scope_r { - ty::re_scope(scope_id) | ty::re_free(scope_id, _) => scope_id, + ty::re_scope(scope_id) | + ty::re_free(ty::FreeRegion {scope_id, _}) => { + scope_id + } _ => { self.bccx.tcx.sess.span_bug( cmt.span, diff --git a/src/librustc/middle/borrowck/loan.rs b/src/librustc/middle/borrowck/loan.rs index 146e0c712a3..a38cd015654 100644 --- a/src/librustc/middle/borrowck/loan.rs +++ b/src/librustc/middle/borrowck/loan.rs @@ -130,8 +130,8 @@ pub impl LoanContext { } cat_local(local_id) | cat_arg(local_id) | cat_self(local_id) => { // FIXME(#4903) - let local_scope_id = *self.bccx.tcx.region_map.get(&local_id); - self.issue_loan(cmt, ty::re_scope(local_scope_id), loan_kind, + let local_region = self.bccx.tcx.region_maps.encl_region(local_id); + self.issue_loan(cmt, local_region, loan_kind, owns_lent_data) } cat_stack_upvar(cmt) => { diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index 5e7903f6610..01fea2a960d 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -227,7 +227,6 @@ Borrowck results in two maps. use core::prelude::*; use middle::mem_categorization::*; -use middle::region; use middle::ty; use middle::typeck; use middle::moves; @@ -458,7 +457,7 @@ pub fn root_map() -> root_map { pub impl BorrowckCtxt { fn is_subregion_of(&self, r_sub: ty::Region, r_sup: ty::Region) -> bool { - region::is_subregion_of(self.tcx.region_map, r_sub, r_sup) + self.tcx.region_maps.is_subregion_of(r_sub, r_sup) } fn cat_expr(&self, expr: @ast::expr) -> cmt { diff --git a/src/librustc/middle/borrowck/preserve.rs b/src/librustc/middle/borrowck/preserve.rs index 3056edea233..40a59e2f89f 100644 --- a/src/librustc/middle/borrowck/preserve.rs +++ b/src/librustc/middle/borrowck/preserve.rs @@ -108,7 +108,7 @@ pub impl<'self> PreserveCtxt<'self> { // Maybe if we pass in the parent instead here, // we can prevent the "scope not found" error debug!("scope_region thing: %? ", cmt.id); - ty::re_scope(*self.tcx().region_map.get(&cmt.id)) + self.tcx().region_maps.encl_region(cmt.id) }; self.compare_scope(cmt, scope_region) @@ -128,27 +128,27 @@ pub impl<'self> PreserveCtxt<'self> { cmt.span, ~"preserve() called with local and !root_managed_data"); } - let local_scope_id = *self.tcx().region_map.get(&local_id); - self.compare_scope(cmt, ty::re_scope(local_scope_id)) + let local_region = self.tcx().region_maps.encl_region(local_id); + self.compare_scope(cmt, local_region) } cat_binding(local_id) => { // Bindings are these kind of weird implicit pointers (cc // #2329). We require (in gather_loans) that they be // rooted in an immutable location. - let local_scope_id = *self.tcx().region_map.get(&local_id); - self.compare_scope(cmt, ty::re_scope(local_scope_id)) + let local_region = self.tcx().region_maps.encl_region(local_id); + self.compare_scope(cmt, local_region) } cat_arg(local_id) => { // This can happen as not all args are lendable (e.g., && // modes). In that case, the caller guarantees stability // for at least the scope of the fn. This is basically a // deref of a region ptr. - let local_scope_id = *self.tcx().region_map.get(&local_id); - self.compare_scope(cmt, ty::re_scope(local_scope_id)) + let local_region = self.tcx().region_maps.encl_region(local_id); + self.compare_scope(cmt, local_region) } cat_self(local_id) => { - let local_scope_id = *self.tcx().region_map.get(&local_id); - self.compare_scope(cmt, ty::re_scope(local_scope_id)) + let local_region = self.tcx().region_maps.encl_region(local_id); + self.compare_scope(cmt, local_region) } cat_comp(cmt_base, comp_field(*)) | cat_comp(cmt_base, comp_index(*)) | diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index fdea403cc2a..6898dcca45d 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -596,8 +596,11 @@ pub fn specialize(cx: @MatchCheckCtxt, class_id); } _ => { - cx.tcx.sess.span_bug(pat_span, - ~"struct pattern didn't resolve to a struct"); + cx.tcx.sess.span_bug( + pat_span, + fmt!("struct pattern resolved to %s, \ + not a struct", + ty_to_str(cx.tcx, left_ty))); } } let args = vec::map(class_fields, |class_field| { diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs index e82b6ba5e77..1f8401c0d53 100644 --- a/src/librustc/middle/kind.rs +++ b/src/librustc/middle/kind.rs @@ -478,13 +478,13 @@ pub fn check_durable(tcx: ty::ctxt, ty: ty::t, sp: span) -> bool { } } -/// This is rather subtle. When we are casting a value to a -/// instantiated trait like `a as trait<'r>`, regionck already ensures -/// that any borrowed pointers that appear in the type of `a` are -/// bounded by `&r`. However, it is possible that there are *type -/// parameters* in the type of `a`, and those *type parameters* may -/// have borrowed pointers within them. We have to guarantee that the -/// regions which appear in those type parameters are not obscured. +/// This is rather subtle. When we are casting a value to a instantiated +/// trait like `a as trait<'r>`, regionck already ensures that any borrowed +/// pointers that appear in the type of `a` are bounded by `'r` (ed.: modulo +/// FIXME(#5723)). However, it is possible that there are *type parameters* +/// in the type of `a`, and those *type parameters* may have borrowed pointers +/// within them. We have to guarantee that the regions which appear in those +/// type parameters are not obscured. /// /// Therefore, we ensure that one of three conditions holds: /// @@ -501,6 +501,8 @@ pub fn check_durable(tcx: ty::ctxt, ty: ty::t, sp: span) -> bool { /// /// (3) The type parameter is owned (and therefore does not contain /// borrowed ptrs). +/// +/// FIXME(#5723)---This code should probably move into regionck. pub fn check_cast_for_escaping_regions( cx: Context, source: @expr, @@ -509,40 +511,78 @@ pub fn check_cast_for_escaping_regions( // Determine what type we are casting to; if it is not an trait, then no // worries. let target_ty = ty::expr_ty(cx.tcx, target); - let target_substs = match ty::get(target_ty).sty { - ty::ty_trait(_, ref substs, _) => {(/*bad*/copy *substs)} - _ => { return; /* not a cast to a trait */ } - }; + match ty::get(target_ty).sty { + ty::ty_trait(*) => {} + _ => { return; } + } + + // Collect up the regions that appear in the target type. We want to + // ensure that these lifetimes are shorter than all lifetimes that are in + // the source type. See test `src/test/compile-fail/regions-trait-2.rs` + let mut target_regions = ~[]; + ty::walk_regions_and_ty( + cx.tcx, + target_ty, + |r| { + if !r.is_bound() { + target_regions.push(r); + } + }, + |_| true); // Check, based on the region associated with the trait, whether it can // possibly escape the enclosing fn item (note that all type parameters - // must have been declared on the enclosing fn item): - match target_substs.self_r { - Some(ty::re_scope(*)) => { return; /* case (1) */ } - None | Some(ty::re_static) | Some(ty::re_free(*)) => {} - Some(ty::re_bound(*)) | Some(ty::re_infer(*)) => { - cx.tcx.sess.span_bug( - source.span, - fmt!("bad region found in kind: %?", target_substs.self_r)); - } + // must have been declared on the enclosing fn item). + if target_regions.any(|r| is_re_scope(*r)) { + return; /* case (1) */ } // Assuming the trait instance can escape, then ensure that each parameter - // either appears in the trait type or is owned: + // either appears in the trait type or is owned. let target_params = ty::param_tys_in_type(target_ty); let source_ty = ty::expr_ty(cx.tcx, source); - do ty::walk_ty(source_ty) |ty| { - match ty::get(ty).sty { - ty::ty_param(source_param) => { - if target_params.contains(&source_param) { - /* case (2) */ - } else { - check_durable(cx.tcx, ty, source.span); /* case (3) */ + ty::walk_regions_and_ty( + cx.tcx, + source_ty, + + |_r| { + // FIXME(#5723) --- turn this check on once &Objects are usable + // + // if !target_regions.any(|t_r| is_subregion_of(cx, *t_r, r)) { + // cx.tcx.sess.span_err( + // source.span, + // fmt!("source contains borrowed pointer with lifetime \ + // not found in the target type `%s`", + // ty_to_str(cx.tcx, target_ty))); + // note_and_explain_region( + // cx.tcx, "source data is only valid for ", r, ""); + // } + }, + + |ty| { + match ty::get(ty).sty { + ty::ty_param(source_param) => { + if target_params.contains(&source_param) { + /* case (2) */ + } else { + check_durable(cx.tcx, ty, source.span); /* case (3) */ + } + } + _ => {} } - } - _ => {} + true + }); + + fn is_re_scope(+r: ty::Region) -> bool { + match r { + ty::re_scope(*) => true, + _ => false } } + + fn is_subregion_of(cx: Context, r_sub: ty::Region, r_sup: ty::Region) -> bool { + cx.tcx.region_maps.is_subregion_of(r_sub, r_sup) + } } /// Ensures that values placed into a ~Trait are copyable and sendable. diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 50c193ab3b0..79130097078 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -11,7 +11,7 @@ /*! This file actually contains two passes related to regions. The first -pass builds up the `region_map`, which describes the parent links in +pass builds up the `scope_map`, which describes the parent links in the region hierarchy. The second pass infers which types must be region parameterized. @@ -23,7 +23,7 @@ use driver::session::Session; use metadata::csearch; use middle::resolve; use middle::ty::{region_variance, rv_covariant, rv_invariant}; -use middle::ty::{rv_contravariant}; +use middle::ty::{rv_contravariant, FreeRegion}; use middle::ty; use core::hashmap::{HashMap, HashSet}; @@ -37,23 +37,31 @@ use syntax::{ast, visit}; pub type parent = Option; /** -Encodes the bounding lifetime for a given AST node: - -- Expressions are mapped 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. - -- Variables and bindings are mapped to the block in which they are declared. +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` + - the free region map is populated during type check as we check + each function. See the function `relate_free_regions` for + more information. */ -pub type region_map = @mut HashMap; +pub struct RegionMaps { + priv scope_map: HashMap, + priv free_region_map: HashMap, +} pub struct ctxt { sess: Session, def_map: resolve::DefMap, // Generated maps: - region_map: region_map, + region_maps: @mut RegionMaps, // Generally speaking, expressions are parented to their innermost // enclosing block. But some kinds of expressions serve as @@ -98,94 +106,215 @@ pub struct ctxt { parent: parent, } -/// Returns true if `subscope` is equal to or is lexically nested inside -/// `superscope` and false otherwise. -pub fn scope_contains(region_map: region_map, superscope: ast::node_id, - subscope: ast::node_id) -> bool { - let mut subscope = subscope; - while superscope != subscope { - match region_map.find(&subscope) { - None => return false, - Some(&scope) => subscope = scope +pub impl RegionMaps { + fn relate_free_regions(&mut self, + sub: FreeRegion, + sup: FreeRegion) + { + match self.free_region_map.find_mut(&sub) { + Some(sups) => { + if !sups.contains(&sup) { + sups.push(sup); + } + return; + } + None => {} + } + + debug!("relate_free_regions(sub=%?, sup=%?)", sub, sup); + + self.free_region_map.insert(sub, ~[sup]); + } + + fn record_parent(&mut self, + sub: ast::node_id, + sup: ast::node_id) + { + debug!("record_parent(sub=%?, sup=%?)", sub, sup); + + self.scope_map.insert(sub, sup); + } + + fn opt_encl_scope(&self, + id: ast::node_id) -> Option + { + //! Returns the narrowest scope that encloses `id`, if any. + + self.scope_map.find(&id).map(|&x| *x) + } + + fn encl_scope(&self, + id: ast::node_id) -> ast::node_id + { + //! Returns the narrowest scope that encloses `id`, if any. + + match self.scope_map.find(&id) { + Some(&r) => r, + None => { fail!(fmt!("No enclosing scope for id %?", id)); } } } - return true; -} -/// Determines whether one region is a subregion of another. This is -/// intended to run *after inference* and sadly the logic is somewhat -/// duplicated with the code in infer.rs. -pub fn is_subregion_of(region_map: region_map, - sub_region: ty::Region, - super_region: ty::Region) -> bool { - sub_region == super_region || - match (sub_region, super_region) { - (_, ty::re_static) => { - true - } + fn encl_region(&self, + id: ast::node_id) -> ty::Region + { + //! Returns the narrowest scope region that encloses `id`, if any. - (ty::re_scope(sub_scope), ty::re_scope(super_scope)) | - (ty::re_scope(sub_scope), ty::re_free(super_scope, _)) => { - scope_contains(region_map, super_scope, sub_scope) - } + ty::re_scope(self.encl_scope(id)) + } - _ => { - false + fn is_sub_scope(&self, + sub_scope: ast::node_id, + superscope: ast::node_id) -> bool + { + /*! + * Returns true if `sub_scope` is equal to or is lexically + * nested inside `superscope` and false otherwise. + */ + + let mut sub_scope = sub_scope; + while superscope != sub_scope { + match self.scope_map.find(&sub_scope) { + None => return false, + Some(&scope) => sub_scope = scope } } -} + return true; + } -/// Finds the nearest common ancestor (if any) of two scopes. That -/// is, finds the smallest scope which is greater than or equal to -/// both `scope_a` and `scope_b`. -pub fn nearest_common_ancestor(region_map: region_map, - scope_a: ast::node_id, - scope_b: ast::node_id) - -> Option { + fn sub_free_region(&self, + sub: FreeRegion, + sup: FreeRegion) -> bool + { + /*! + * Determines whether two free regions have a subregion relationship + * by walking the graph encoded in `free_region_map`. Note that + * it is possible that `sub != sup` and `sub <= sup` and `sup <= sub` + * (that is, the user can give two different names to the same lifetime). + */ - fn ancestors_of(region_map: region_map, scope: ast::node_id) - -> ~[ast::node_id] { - let mut result = ~[scope]; - let mut scope = scope; - loop { - match region_map.find(&scope) { - None => return result, - Some(&superscope) => { - result.push(superscope); - scope = superscope; + if sub == sup { + return true; + } + + // Do a little breadth-first-search here. The `queue` list + // doubles as a way to detect if we've seen a particular FR + // before. Note that we expect this graph to be an *extremely + // shallow* tree. + let mut queue = ~[sub]; + let mut i = 0; + while i < queue.len() { + match self.free_region_map.find(&queue[i]) { + Some(parents) => { + for parents.each |parent| { + if *parent == sup { + return true; + } + + if !queue.contains(parent) { + queue.push(*parent); + } + } + } + None => {} + } + i += 1; + } + return false; + } + + fn is_subregion_of(&self, + sub_region: ty::Region, + super_region: ty::Region) -> bool + { + /*! + * Determines whether one region is a subregion of another. This is + * intended to run *after inference* and sadly the logic is somewhat + * duplicated with the code in infer.rs. + */ + + debug!("is_subregion_of(sub_region=%?, super_region=%?)", + sub_region, super_region); + + sub_region == super_region || { + match (sub_region, super_region) { + (_, ty::re_static) => { + true + } + + (ty::re_scope(sub_scope), ty::re_scope(super_scope)) => { + self.is_sub_scope(sub_scope, super_scope) + } + + (ty::re_scope(sub_scope), ty::re_free(ref fr)) => { + self.is_sub_scope(sub_scope, fr.scope_id) + } + + (ty::re_free(sub_fr), ty::re_free(super_fr)) => { + self.sub_free_region(sub_fr, super_fr) + } + + _ => { + false } } } } - if scope_a == scope_b { return Some(scope_a); } + fn nearest_common_ancestor(&self, + scope_a: ast::node_id, + scope_b: ast::node_id) -> Option + { + /*! + * Finds the nearest common ancestor (if any) of two scopes. That + * is, finds the smallest scope which is greater than or equal to + * both `scope_a` and `scope_b`. + */ - let a_ancestors = ancestors_of(region_map, scope_a); - let b_ancestors = ancestors_of(region_map, scope_b); - let mut a_index = vec::len(a_ancestors) - 1u; - let mut b_index = vec::len(b_ancestors) - 1u; + if scope_a == scope_b { return Some(scope_a); } - // Here, ~[ab]_ancestors is a vector going from narrow to broad. - // The end of each vector will be the item where the scope is - // defined; if there are any common ancestors, then the tails of - // the vector will be the same. So basically we want to walk - // backwards from the tail of each vector and find the first point - // where they diverge. If one vector is a suffix of the other, - // then the corresponding scope is a superscope of the other. + let a_ancestors = ancestors_of(self, scope_a); + let b_ancestors = ancestors_of(self, scope_b); + let mut a_index = vec::len(a_ancestors) - 1u; + let mut b_index = vec::len(b_ancestors) - 1u; - if a_ancestors[a_index] != b_ancestors[b_index] { - return None; - } + // Here, ~[ab]_ancestors is a vector going from narrow to broad. + // The end of each vector will be the item where the scope is + // defined; if there are any common ancestors, then the tails of + // the vector will be the same. So basically we want to walk + // backwards from the tail of each vector and find the first point + // where they diverge. If one vector is a suffix of the other, + // then the corresponding scope is a superscope of the other. - loop { - // Loop invariant: a_ancestors[a_index] == b_ancestors[b_index] - // for all indices between a_index and the end of the array - if a_index == 0u { return Some(scope_a); } - if b_index == 0u { return Some(scope_b); } - a_index -= 1u; - b_index -= 1u; if a_ancestors[a_index] != b_ancestors[b_index] { - return Some(a_ancestors[a_index + 1u]); + return None; + } + + loop { + // Loop invariant: a_ancestors[a_index] == b_ancestors[b_index] + // for all indices between a_index and the end of the array + if a_index == 0u { return Some(scope_a); } + if b_index == 0u { return Some(scope_b); } + a_index -= 1u; + b_index -= 1u; + if a_ancestors[a_index] != b_ancestors[b_index] { + return Some(a_ancestors[a_index + 1u]); + } + } + + fn ancestors_of(self: &RegionMaps, scope: ast::node_id) + -> ~[ast::node_id] + { + let mut result = ~[scope]; + let mut scope = scope; + loop { + match self.scope_map.find(&scope) { + None => return result, + Some(&superscope) => { + result.push(superscope); + scope = superscope; + } + } + } } } } @@ -205,8 +334,7 @@ pub fn parent_id(cx: ctxt, span: span) -> ast::node_id { /// Records the current parent (if any) as the parent of `child_id`. pub fn record_parent(cx: ctxt, child_id: ast::node_id) { for cx.parent.each |parent_id| { - debug!("parent of node %d is node %d", child_id, *parent_id); - cx.region_map.insert(child_id, *parent_id); + cx.region_maps.record_parent(child_id, *parent_id); } } @@ -328,7 +456,7 @@ pub fn resolve_fn(fk: &visit::fn_kind, // Record the ID of `self`. match *fk { visit::fk_method(_, _, method) => { - cx.region_map.insert(method.self_id, body.node.id); + cx.region_maps.record_parent(method.self_id, body.node.id); } _ => {} } @@ -338,7 +466,7 @@ pub fn resolve_fn(fk: &visit::fn_kind, body.node.id, cx.parent, fn_cx.parent); for decl.inputs.each |input| { - cx.region_map.insert(input.id, body.node.id); + cx.region_maps.record_parent(input.id, body.node.id); } visit::visit_fn(fk, decl, body, sp, id, fn_cx, visitor); @@ -346,11 +474,15 @@ pub fn resolve_fn(fk: &visit::fn_kind, pub fn resolve_crate(sess: Session, def_map: resolve::DefMap, - crate: @ast::crate) - -> region_map { + crate: @ast::crate) -> @mut RegionMaps +{ + let region_maps = @mut RegionMaps { + scope_map: HashMap::new(), + free_region_map: HashMap::new() + }; let cx: ctxt = ctxt {sess: sess, def_map: def_map, - region_map: @mut HashMap::new(), + region_maps: region_maps, root_exprs: @mut HashSet::new(), parent: None}; let visitor = visit::mk_vt(@visit::Visitor { @@ -365,7 +497,7 @@ pub fn resolve_crate(sess: Session, .. *visit::default_visitor() }); visit::visit_crate(*crate, cx, visitor); - return cx.region_map; + return region_maps; } // ___________________________________________________________________________ diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 74215312434..3921764e6af 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -240,7 +240,7 @@ struct ctxt_ { sess: session::Session, def_map: resolve::DefMap, - region_map: middle::region::region_map, + region_maps: @mut middle::region::RegionMaps, region_paramd_items: middle::region::region_paramd_items, // Stores the types for various nodes in the AST. Note that this table @@ -410,7 +410,7 @@ pub struct param_ty { /// Representation of regions: #[auto_encode] #[auto_decode] -#[deriving(Eq)] +#[deriving(Eq, IterBytes)] pub enum Region { /// Bound regions are found (primarily) in function types. They indicate /// region parameters that have yet to be replaced with actual regions @@ -426,7 +426,7 @@ pub enum Region { /// When checking a function body, the types of all arguments and so forth /// that refer to bound region parameters are modified to refer to free /// region parameters. - re_free(node_id, bound_region), + re_free(FreeRegion), /// A concrete region naming some expression within the current function. re_scope(node_id), @@ -438,9 +438,26 @@ pub enum Region { re_infer(InferRegion) } +pub impl Region { + fn is_bound(&self) -> bool { + match self { + &re_bound(*) => true, + _ => false + } + } +} + #[auto_encode] #[auto_decode] -#[deriving(Eq)] +#[deriving(Eq, IterBytes)] +pub struct FreeRegion { + scope_id: node_id, + bound_region: bound_region +} + +#[auto_encode] +#[auto_decode] +#[deriving(Eq, IterBytes)] pub enum bound_region { /// The self region for structs, impls (&T in a type defn or &'self T) br_self, @@ -810,7 +827,7 @@ pub fn mk_ctxt(s: session::Session, dm: resolve::DefMap, amap: ast_map::map, freevars: freevars::freevar_map, - region_map: middle::region::region_map, + region_maps: @mut middle::region::RegionMaps, region_paramd_items: middle::region::region_paramd_items, +lang_items: middle::lang_items::LanguageItems, crate: @ast::crate) @@ -837,7 +854,7 @@ pub fn mk_ctxt(s: session::Session, cstore: s.cstore, sess: s, def_map: dm, - region_map: region_map, + region_maps: region_maps, region_paramd_items: region_paramd_items, node_types: @mut SmallIntMap::new(), node_type_substs: @mut HashMap::new(), @@ -1176,15 +1193,6 @@ pub fn default_arg_mode_for_ty(tcx: ctxt, ty: ty::t) -> ast::rmode { } } -// Returns the narrowest lifetime enclosing the evaluation of the expression -// with id `id`. -pub fn encl_region(cx: ctxt, id: ast::node_id) -> ty::Region { - match cx.region_map.find(&id) { - Some(&encl_scope) => ty::re_scope(encl_scope), - None => ty::re_static - } -} - pub fn walk_ty(ty: t, f: &fn(t)) { maybe_walk_ty(ty, |t| { f(t); true }); } @@ -1308,8 +1316,8 @@ pub fn walk_regions_and_ty( fold_regions_and_ty( cx, ty, |r| { walkr(r); r }, - |t| { walkt(t); walk_regions_and_ty(cx, t, walkr, walkt); t }, - |t| { walkt(t); walk_regions_and_ty(cx, t, walkr, walkt); t }); + |t| { walk_regions_and_ty(cx, t, walkr, walkt); t }, + |t| { walk_regions_and_ty(cx, t, walkr, walkt); t }); } } @@ -2506,43 +2514,52 @@ pub fn index_sty(cx: ctxt, sty: &sty) -> Option { } } -impl to_bytes::IterBytes for bound_region { - fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) { - match *self { - ty::br_self => 0u8.iter_bytes(lsb0, f), +/** + * Enforces an arbitrary but consistent total ordering over + * free regions. This is needed for establishing a consistent + * LUB in region_inference. */ +impl cmp::TotalOrd for FreeRegion { + fn cmp(&self, other: &FreeRegion) -> Ordering { + cmp::cmp2(&self.scope_id, &self.bound_region, + &other.scope_id, &other.bound_region) + } +} - ty::br_anon(ref idx) => - to_bytes::iter_bytes_2(&1u8, idx, lsb0, f), +impl cmp::TotalEq for FreeRegion { + fn equals(&self, other: &FreeRegion) -> bool { + *self == *other + } +} - ty::br_named(ref ident) => - to_bytes::iter_bytes_2(&2u8, ident, lsb0, f), +/** + * Enforces an arbitrary but consistent total ordering over + * bound regions. This is needed for establishing a consistent + * LUB in region_inference. */ +impl cmp::TotalOrd for bound_region { + fn cmp(&self, other: &bound_region) -> Ordering { + match (self, other) { + (&ty::br_self, &ty::br_self) => cmp::Equal, + (&ty::br_self, _) => cmp::Less, - ty::br_cap_avoid(ref id, ref br) => - to_bytes::iter_bytes_3(&3u8, id, br, lsb0, f), + (&ty::br_anon(ref a1), &ty::br_anon(ref a2)) => a1.cmp(a2), + (&ty::br_anon(*), _) => cmp::Less, - ty::br_fresh(ref x) => - to_bytes::iter_bytes_2(&4u8, x, lsb0, f) + (&ty::br_named(ref a1), &ty::br_named(ref a2)) => a1.repr.cmp(&a2.repr), + (&ty::br_named(*), _) => cmp::Less, + + (&ty::br_cap_avoid(ref a1, @ref b1), + &ty::br_cap_avoid(ref a2, @ref b2)) => cmp::cmp2(a1, b1, a2, b2), + (&ty::br_cap_avoid(*), _) => cmp::Less, + + (&ty::br_fresh(ref a1), &ty::br_fresh(ref a2)) => a1.cmp(a2), + (&ty::br_fresh(*), _) => cmp::Less, } } } -impl to_bytes::IterBytes for Region { - fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) { - match *self { - re_bound(ref br) => - to_bytes::iter_bytes_2(&0u8, br, lsb0, f), - - re_free(ref id, ref br) => - to_bytes::iter_bytes_3(&1u8, id, br, lsb0, f), - - re_scope(ref id) => - to_bytes::iter_bytes_2(&2u8, id, lsb0, f), - - re_infer(ref id) => - to_bytes::iter_bytes_2(&3u8, id, lsb0, f), - - re_static => 4u8.iter_bytes(lsb0, f) - } +impl cmp::TotalEq for bound_region { + fn equals(&self, other: &bound_region) -> bool { + *self == *other } } @@ -2856,8 +2873,17 @@ pub fn expr_ty_adjusted(cx: ctxt, expr: @ast::expr) -> t { */ let unadjusted_ty = expr_ty(cx, expr); + adjust_ty(cx, expr.span, unadjusted_ty, cx.adjustments.find(&expr.id)) +} - return match cx.adjustments.find(&expr.id) { +pub fn adjust_ty(cx: ctxt, + span: span, + unadjusted_ty: ty::t, + adjustment: Option<&@AutoAdjustment>) -> ty::t +{ + /*! See `expr_ty_adjusted` */ + + return match adjustment { None => unadjusted_ty, Some(&@AutoAddEnv(r, s)) => { @@ -2886,7 +2912,7 @@ pub fn expr_ty_adjusted(cx: ctxt, expr: @ast::expr) -> t { Some(mt) => { adjusted_ty = mt.ty; } None => { cx.sess.span_bug( - expr.span, + span, fmt!("The %uth autoderef failed: %s", i, ty_to_str(cx, adjusted_ty))); @@ -2905,18 +2931,18 @@ pub fn expr_ty_adjusted(cx: ctxt, expr: @ast::expr) -> t { } AutoBorrowVec => { - borrow_vec(cx, expr, autoref, adjusted_ty) + borrow_vec(cx, span, autoref, adjusted_ty) } AutoBorrowVecRef => { - adjusted_ty = borrow_vec(cx, expr, autoref, + adjusted_ty = borrow_vec(cx, span, autoref, adjusted_ty); mk_rptr(cx, autoref.region, mt {ty: adjusted_ty, mutbl: ast::m_imm}) } AutoBorrowFn => { - borrow_fn(cx, expr, autoref, adjusted_ty) + borrow_fn(cx, span, autoref, adjusted_ty) } } } @@ -2924,7 +2950,7 @@ pub fn expr_ty_adjusted(cx: ctxt, expr: @ast::expr) -> t { } }; - fn borrow_vec(cx: ctxt, expr: @ast::expr, + fn borrow_vec(cx: ctxt, span: span, autoref: &AutoRef, ty: ty::t) -> ty::t { match get(ty).sty { ty_evec(mt, _) => { @@ -2938,14 +2964,14 @@ pub fn expr_ty_adjusted(cx: ctxt, expr: @ast::expr) -> t { ref s => { cx.sess.span_bug( - expr.span, + span, fmt!("borrow-vec associated with bad sty: %?", s)); } } } - fn borrow_fn(cx: ctxt, expr: @ast::expr, + fn borrow_fn(cx: ctxt, span: span, autoref: &AutoRef, ty: ty::t) -> ty::t { match get(ty).sty { ty_closure(ref fty) => { @@ -2958,7 +2984,7 @@ pub fn expr_ty_adjusted(cx: ctxt, expr: @ast::expr) -> t { ref s => { cx.sess.span_bug( - expr.span, + span, fmt!("borrow-fn associated with bad sty: %?", s)); } diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 24552019074..6bc1317d5f9 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -95,6 +95,7 @@ use middle::typeck::check::method::{CheckTraitsAndInherentMethods}; use middle::typeck::check::method::{CheckTraitsOnly, DontAutoderefReceiver}; use middle::typeck::check::method::{TransformTypeNormally}; use middle::typeck::check::regionmanip::replace_bound_regions_in_fn_sig; +use middle::typeck::check::regionmanip::relate_free_regions; use middle::typeck::check::vtable::{LocationInfo, VtableContext}; use middle::typeck::CrateCtxt; use middle::typeck::infer::{resolve_type, force_tvar}; @@ -308,10 +309,14 @@ pub fn check_fn(ccx: @mut CrateCtxt, // the node_id of the body block. let (isr, self_info, fn_sig) = { - replace_bound_regions_in_fn_sig(tcx, inherited_isr, self_info, fn_sig, - |br| ty::re_free(body.node.id, br)) + replace_bound_regions_in_fn_sig( + tcx, inherited_isr, self_info, fn_sig, + |br| ty::re_free(ty::FreeRegion {scope_id: body.node.id, + bound_region: br})) }; + relate_free_regions(tcx, self_info.map(|s| s.self_ty), &fn_sig); + let arg_tys = fn_sig.inputs.map(|a| a.ty); let ret_ty = fn_sig.output; @@ -2841,8 +2846,7 @@ pub fn check_decl_local(fcx: @mut FnCtxt, local: @ast::local) { _ => {} } - let region = - ty::re_scope(*tcx.region_map.get(&local.node.id)); + let region = tcx.region_maps.encl_region(local.node.id); let pcx = pat_ctxt { fcx: fcx, map: pat_id_map(tcx.def_map, local.node.pat), diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 833dbff89a0..5594f2a0f65 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -31,13 +31,15 @@ use core::prelude::*; use middle::freevars::get_freevars; use middle::pat_util::pat_bindings; -use middle::ty::{encl_region, re_scope}; +use middle::ty::{re_scope}; use middle::ty; use middle::typeck::check::FnCtxt; use middle::typeck::check::lookup_def; +use middle::typeck::check::regionmanip::relate_nested_regions; use middle::typeck::infer::resolve_and_force_all_but_regions; use middle::typeck::infer::resolve_type; -use util::ppaux::{note_and_explain_region, ty_to_str}; +use util::ppaux::{note_and_explain_region, ty_to_str, + region_to_str}; use core::result; use syntax::ast::{ManagedSigil, OwnedSigil, BorrowedSigil}; @@ -53,12 +55,13 @@ pub struct Rcx { pub type rvt = visit::vt<@mut Rcx>; -pub fn encl_region_of_def(fcx: @mut FnCtxt, def: ast::def) -> ty::Region { +fn encl_region_of_def(fcx: @mut FnCtxt, def: ast::def) -> ty::Region { let tcx = fcx.tcx(); match def { def_local(node_id, _) | def_arg(node_id, _, _) | - def_self(node_id, _) | def_binding(node_id, _) => - return encl_region(tcx, node_id), + def_self(node_id, _) | def_binding(node_id, _) => { + tcx.region_maps.encl_region(node_id) + } def_upvar(_, subdef, closure_id, body_id) => { match ty::ty_closure_sigil(fcx.node_ty(closure_id)) { BorrowedSigil => encl_region_of_def(fcx, *subdef), @@ -113,6 +116,24 @@ pub impl Rcx { fn resolve_node_type(@mut self, id: ast::node_id) -> ty::t { self.resolve_type(self.fcx.node_ty(id)) } + + /// Try to resolve the type for the given node. + fn resolve_expr_type_adjusted(@mut self, expr: @ast::expr) -> ty::t { + let ty_unadjusted = self.resolve_node_type(expr.id); + if ty::type_is_error(ty_unadjusted) || ty::type_is_bot(ty_unadjusted) { + ty_unadjusted + } else { + let tcx = self.fcx.tcx(); + let adjustments = self.fcx.inh.adjustments; + match adjustments.find(&expr.id) { + None => ty_unadjusted, + Some(&adjustment) => { + // FIXME(#3850) --- avoid region scoping errors + ty::adjust_ty(tcx, expr.span, ty_unadjusted, Some(&adjustment)) + } + } + } + } } pub fn regionck_expr(fcx: @mut FnCtxt, e: @ast::expr) { @@ -129,7 +150,7 @@ pub fn regionck_fn(fcx: @mut FnCtxt, blk: &ast::blk) { fcx.infcx().resolve_regions(); } -pub fn regionck_visitor() -> rvt { +fn regionck_visitor() -> rvt { visit::mk_vt(@visit::Visitor {visit_item: visit_item, visit_stmt: visit_stmt, visit_expr: visit_expr, @@ -138,11 +159,11 @@ pub fn regionck_visitor() -> rvt { .. *visit::default_visitor()}) } -pub fn visit_item(_item: @ast::item, &&_rcx: @mut Rcx, _v: rvt) { +fn visit_item(_item: @ast::item, &&_rcx: @mut Rcx, _v: rvt) { // Ignore items } -pub fn visit_local(l: @ast::local, &&rcx: @mut Rcx, v: rvt) { +fn visit_local(l: @ast::local, &&rcx: @mut Rcx, v: rvt) { // Check to make sure that the regions in all local variables are // within scope. // @@ -173,19 +194,24 @@ pub fn visit_local(l: @ast::local, &&rcx: @mut Rcx, v: rvt) { } } -pub fn visit_block(b: &ast::blk, &&rcx: @mut Rcx, v: rvt) { +fn visit_block(b: &ast::blk, &&rcx: @mut Rcx, v: rvt) { visit::visit_block(b, rcx, v); } -pub fn visit_expr(expr: @ast::expr, &&rcx: @mut Rcx, v: rvt) { - debug!("visit_expr(e=%s)", rcx.fcx.expr_to_str(expr)); +fn visit_expr(expr: @ast::expr, &&rcx: @mut Rcx, v: rvt) { + debug!("regionck::visit_expr(e=%s)", rcx.fcx.expr_to_str(expr)); for rcx.fcx.inh.adjustments.find(&expr.id).each |&adjustment| { + debug!("adjustment=%?", adjustment); match *adjustment { @ty::AutoDerefRef( - ty::AutoDerefRef { - autoderefs: autoderefs, autoref: Some(ref autoref)}) => { - guarantor::for_autoref(rcx, expr, autoderefs, autoref); + ty::AutoDerefRef {autoderefs: autoderefs, autoref: opt_autoref}) => + { + let expr_ty = rcx.resolve_node_type(expr.id); + constrain_derefs(rcx, expr, autoderefs, expr_ty); + for opt_autoref.each |autoref| { + guarantor::for_autoref(rcx, expr, autoderefs, autoref); + } } _ => {} } @@ -271,6 +297,16 @@ pub fn visit_expr(expr: @ast::expr, &&rcx: @mut Rcx, v: rvt) { } } + ast::expr_index(vec_expr, _) => { + let vec_type = rcx.resolve_expr_type_adjusted(vec_expr); + constrain_index(rcx, expr, vec_type); + } + + ast::expr_unary(ast::deref, base) => { + let base_ty = rcx.resolve_node_type(base.id); + constrain_derefs(rcx, expr, 1, base_ty); + } + ast::expr_addr_of(_, base) => { guarantor::for_addr_of(rcx, expr, base); } @@ -297,11 +333,11 @@ pub fn visit_expr(expr: @ast::expr, &&rcx: @mut Rcx, v: rvt) { visit::visit_expr(expr, rcx, v); } -pub fn visit_stmt(s: @ast::stmt, &&rcx: @mut Rcx, v: rvt) { +fn visit_stmt(s: @ast::stmt, &&rcx: @mut Rcx, v: rvt) { visit::visit_stmt(s, rcx, v); } -pub fn visit_node(id: ast::node_id, span: span, rcx: @mut Rcx) -> bool { +fn visit_node(id: ast::node_id, span: span, rcx: @mut Rcx) -> bool { /*! * * checks the type of the node `id` and reports an error if it @@ -314,13 +350,119 @@ pub fn visit_node(id: ast::node_id, span: span, rcx: @mut Rcx) -> bool { // find the region where this expr evaluation is taking place let tcx = fcx.ccx.tcx; - let encl_region = ty::encl_region(tcx, id); + let encl_region = match tcx.region_maps.opt_encl_scope(id) { + None => ty::re_static, + Some(r) => ty::re_scope(r) + }; // Otherwise, look at the type and see if it is a region pointer. constrain_regions_in_type_of_node(rcx, id, encl_region, span) } -pub fn constrain_auto_ref(rcx: @mut Rcx, expr: @ast::expr) { +fn encl_region_or_static(rcx: @mut Rcx, expr: @ast::expr) -> ty::Region { + // FIXME(#3850) --- interactions with modes compel overly large granularity + // that is, we would probably prefer to just return re_scope(expr.id) + // here but we cannot just yet. + + let tcx = rcx.fcx.tcx(); + match tcx.region_maps.opt_encl_scope(expr.id) { + Some(s) => ty::re_scope(s), + None => ty::re_static // occurs in constants + } +} + +fn constrain_derefs(rcx: @mut Rcx, + deref_expr: @ast::expr, + derefs: uint, + mut derefd_ty: ty::t) +{ + /*! + * Invoked on any dereference that occurs, whether explicitly + * or through an auto-deref. Checks that if this is a region + * pointer being derefenced, the lifetime of the pointer includes + * the deref expr. + */ + + let tcx = rcx.fcx.tcx(); + let r_deref_expr = encl_region_or_static(rcx, deref_expr); + for uint::range(0, derefs) |i| { + debug!("constrain_derefs(deref_expr=%s, derefd_ty=%s, derefs=%?/%?", + rcx.fcx.expr_to_str(deref_expr), + rcx.fcx.infcx().ty_to_str(derefd_ty), + i, derefs); + + match ty::get(derefd_ty).sty { + ty::ty_rptr(r_ptr, _) => { + match rcx.fcx.mk_subr(true, deref_expr.span, r_deref_expr, r_ptr) { + result::Ok(*) => {} + result::Err(*) => { + tcx.sess.span_err( + deref_expr.span, + fmt!("dereference of reference outside its lifetime")); + note_and_explain_region( + tcx, + "the reference is only valid for ", + r_ptr, + ""); + } + } + } + + _ => {} + } + + match ty::deref(tcx, derefd_ty, true) { + Some(mt) => derefd_ty = mt.ty, + None => { + tcx.sess.span_bug( + deref_expr.span, + fmt!("%?'th deref is of a non-deref'able type `%s`", + i, rcx.fcx.infcx().ty_to_str(derefd_ty))); + } + } + } +} + +fn constrain_index(rcx: @mut Rcx, + index_expr: @ast::expr, + indexed_ty: ty::t) +{ + /*! + * Invoked on any index expression that occurs. Checks that if + * this is a slice being indexed, the lifetime of the pointer + * includes the deref expr. + */ + + let tcx = rcx.fcx.tcx(); + + debug!("constrain_index(index_expr=%s, indexed_ty=%s", + rcx.fcx.expr_to_str(index_expr), + rcx.fcx.infcx().ty_to_str(indexed_ty)); + + let r_index_expr = encl_region_or_static(rcx, index_expr); + match ty::get(indexed_ty).sty { + ty::ty_estr(ty::vstore_slice(r_ptr)) | + ty::ty_evec(_, ty::vstore_slice(r_ptr)) => { + match rcx.fcx.mk_subr(true, index_expr.span, r_index_expr, r_ptr) { + result::Ok(*) => {} + result::Err(*) => { + tcx.sess.span_err( + index_expr.span, + fmt!("index of slice outside its lifetime")); + note_and_explain_region( + tcx, + "the slice is only valid for ", + r_ptr, + ""); + } + } + } + + _ => {} + } +} + +fn constrain_auto_ref(rcx: @mut Rcx, expr: @ast::expr) { /*! * * If `expr` is auto-ref'd (e.g., as part of a borrow), then this @@ -340,7 +482,7 @@ pub fn constrain_auto_ref(rcx: @mut Rcx, expr: @ast::expr) { }; let tcx = rcx.fcx.tcx(); - let encl_region = ty::encl_region(tcx, expr.id); + let encl_region = tcx.region_maps.encl_region(expr.id); match rcx.fcx.mk_subr(true, expr.span, encl_region, region) { result::Ok(()) => {} result::Err(_) => { @@ -366,7 +508,7 @@ pub fn constrain_auto_ref(rcx: @mut Rcx, expr: @ast::expr) { } } -pub fn constrain_free_variables( +fn constrain_free_variables( rcx: @mut Rcx, region: ty::Region, expr: @ast::expr) { @@ -402,81 +544,103 @@ pub fn constrain_free_variables( } } -pub fn constrain_regions_in_type_of_node( +fn constrain_regions_in_type_of_node( rcx: @mut Rcx, id: ast::node_id, encl_region: ty::Region, - span: span) -> bool { + span: span) -> bool +{ let tcx = rcx.fcx.tcx(); // Try to resolve the type. If we encounter an error, then typeck // is going to fail anyway, so just stop here and let typeck // report errors later on in the writeback phase. - let ty = rcx.resolve_node_type(id); + let ty0 = rcx.resolve_node_type(id); + let adjustment = rcx.fcx.inh.adjustments.find(&id); + let ty = ty::adjust_ty(tcx, span, ty0, adjustment); debug!("constrain_regions_in_type_of_node(\ - ty=%s, id=%d, encl_region=%?)", - ty_to_str(tcx, ty), id, encl_region); + ty=%s, ty0=%s, id=%d, encl_region=%?, adjustment=%?)", + ty_to_str(tcx, ty), ty_to_str(tcx, ty0), + id, encl_region, adjustment); constrain_regions_in_type(rcx, encl_region, span, ty) } -pub fn constrain_regions_in_type( +fn constrain_regions_in_type( rcx: @mut Rcx, encl_region: ty::Region, span: span, - ty: ty::t) -> bool { + ty: ty::t) -> bool +{ /*! * * Requires that any regions which appear in `ty` must be - * superregions of `encl_region`. This prevents regions from - * being used outside of the block in which they are valid. - * Recall that regions represent blocks of code or expressions: - * this requirement basically says "any place that uses or may use - * a region R must be within the block of code that R corresponds - * to." */ + * superregions of `encl_region`. Also enforces the constraint + * that given a pointer type `&'r T`, T must not contain regions + * that outlive 'r, as well as analogous constraints for other + * lifetime'd types. + * + * This check prevents regions from being used outside of the block in + * which they are valid. Recall that regions represent blocks of + * code or expressions: this requirement basically says "any place + * that uses or may use a region R must be within the block of + * code that R corresponds to." + */ let e = rcx.errors_reported; - ty::walk_regions_and_ty( - rcx.fcx.ccx.tcx, ty, - |r| constrain_region(rcx, encl_region, span, r), - |t| ty::type_has_regions(t)); - return (e == rcx.errors_reported); + let tcx = rcx.fcx.ccx.tcx; - fn constrain_region(rcx: @mut Rcx, - encl_region: ty::Region, - span: span, - region: ty::Region) { - let tcx = rcx.fcx.ccx.tcx; + debug!("constrain_regions_in_type(encl_region=%s, ty=%s)", + region_to_str(tcx, encl_region), + ty_to_str(tcx, ty)); - debug!("constrain_region(encl_region=%?, region=%?)", - encl_region, region); + do relate_nested_regions(tcx, Some(encl_region), ty) |r_sub, r_sup| { + debug!("relate(r_sub=%s, r_sup=%s)", + region_to_str(tcx, r_sub), + region_to_str(tcx, r_sup)); - match region { - ty::re_bound(_) => { + if r_sup.is_bound() || r_sub.is_bound() { // a bound region is one which appears inside an fn type. // (e.g., the `&` in `fn(&T)`). Such regions need not be // constrained by `encl_region` as they are placeholders // for regions that are as-yet-unknown. - return; - } - _ => () - } - - match rcx.fcx.mk_subr(true, span, encl_region, region) { - result::Err(_) => { - tcx.sess.span_err( - span, - fmt!("reference is not valid outside of its lifetime")); - note_and_explain_region( - tcx, - ~"the reference is only valid for ", - region, - ~""); - rcx.errors_reported += 1u; - } - result::Ok(()) => { - } + } else { + match rcx.fcx.mk_subr(true, span, r_sub, r_sup) { + result::Err(_) => { + if r_sub == encl_region { + tcx.sess.span_err( + span, + fmt!("reference is not valid outside of its lifetime")); + note_and_explain_region( + tcx, + "the reference is only valid for ", + r_sup, + ""); + } else { + tcx.sess.span_err( + span, + fmt!("in type `%s`, pointer has a longer lifetime than \ + the data it references", + rcx.fcx.infcx().ty_to_str(ty))); + note_and_explain_region( + tcx, + "the pointer is valid for ", + r_sub, + ""); + note_and_explain_region( + tcx, + "but the referenced data is only valid for ", + r_sup, + ""); + } + rcx.errors_reported += 1u; + } + result::Ok(()) => { + } + } } } + + return (e == rcx.errors_reported); } pub mod guarantor { @@ -577,10 +741,12 @@ pub mod guarantor { * region pointers. */ - debug!("guarantor::for_autoref(expr=%s)", rcx.fcx.expr_to_str(expr)); + debug!("guarantor::for_autoref(expr=%s, autoref=%?)", + rcx.fcx.expr_to_str(expr), autoref); let _i = ::util::common::indenter(); let mut expr_ct = categorize_unadjusted(rcx, expr); + debug!(" unadjusted cat=%?", expr_ct.cat); expr_ct = apply_autoderefs( rcx, expr, autoderefs, expr_ct); @@ -626,7 +792,7 @@ pub mod guarantor { * to the lifetime of its guarantor (if any). */ - debug!("opt_constrain_region(id=%?, guarantor=%?)", id, guarantor); + debug!("link(id=%?, guarantor=%?)", id, guarantor); let bound = match guarantor { None => { @@ -860,8 +1026,6 @@ pub mod guarantor { match closure_ty.sigil { ast::BorrowedSigil => BorrowedPointer(closure_ty.region), ast::OwnedSigil => OwnedPointer, - - // NOTE This is...not quite right. Deduce a test etc. ast::ManagedSigil => OtherPointer, } } @@ -972,7 +1136,6 @@ pub fn infallibly_mk_subr(rcx: @mut Rcx, a: ty::Region, b: ty::Region) { /*! - * * Constrains `a` to be a subregion of `b`. In many cases, we * know that this can never yield an error due to the way that * region inferencing works. Therefore just report a bug if we diff --git a/src/librustc/middle/typeck/check/regionmanip.rs b/src/librustc/middle/typeck/check/regionmanip.rs index abbefd1f7e6..359f938d0d9 100644 --- a/src/librustc/middle/typeck/check/regionmanip.rs +++ b/src/librustc/middle/typeck/check/regionmanip.rs @@ -99,7 +99,7 @@ pub fn replace_bound_regions_in_fn_sig( to_r: &fn(ty::bound_region) -> ty::Region, r: ty::Region) -> isr_alist { match r { - ty::re_free(_, _) | ty::re_static | ty::re_scope(_) | + ty::re_free(*) | ty::re_static | ty::re_scope(_) | ty::re_infer(_) => { isr } @@ -167,10 +167,125 @@ pub fn replace_bound_regions_in_fn_sig( // Free regions like these just stay the same: ty::re_static | ty::re_scope(_) | - ty::re_free(_, _) | + ty::re_free(*) | ty::re_infer(_) => r }; r1 } } } + +pub fn relate_nested_regions( + tcx: ty::ctxt, + opt_region: Option, + ty: ty::t, + relate_op: &fn(ty::Region, ty::Region)) +{ + /*! + * + * This rather specialized function walks each region `r` that appear + * in `ty` and invokes `relate_op(r_encl, r)` for each one. `r_encl` + * here is the region of any enclosing `&'r T` pointer. If there is + * no enclosing pointer, and `opt_region` is Some, then `opt_region.get()` + * is used instead. Otherwise, no callback occurs at all). + * + * Here are some examples to give you an intution: + * + * - `relate_nested_regions(Some('r1), &'r2 uint)` invokes + * - `relate_op('r1, 'r2)` + * - `relate_nested_regions(Some('r1), &'r2 &'r3 uint)` invokes + * - `relate_op('r1, 'r2)` + * - `relate_op('r2, 'r3)` + * - `relate_nested_regions(None, &'r2 &'r3 uint)` invokes + * - `relate_op('r2, 'r3)` + * - `relate_nested_regions(None, &'r2 &'r3 &'r4 uint)` invokes + * - `relate_op('r2, 'r3)` + * - `relate_op('r2, 'r4)` + * - `relate_op('r3, 'r4)` + * + * This function is used in various pieces of code because we enforce the + * constraint that a region pointer cannot outlive the things it points at. + * Hence, in the second example above, `'r2` must be a subregion of `'r3`. + */ + + let mut the_stack = ~[]; + for opt_region.each |&r| { the_stack.push(r); } + walk_ty(tcx, &mut the_stack, ty, relate_op); + + fn walk_ty(tcx: ty::ctxt, + the_stack: &mut ~[ty::Region], + ty: ty::t, + relate_op: &fn(ty::Region, ty::Region)) + { + match ty::get(ty).sty { + ty::ty_rptr(r, ref mt) | + ty::ty_evec(ref mt, ty::vstore_slice(r)) => { + relate(*the_stack, r, relate_op); + the_stack.push(r); + walk_ty(tcx, the_stack, mt.ty, relate_op); + the_stack.pop(); + } + _ => { + ty::fold_regions_and_ty( + tcx, + ty, + |r| { relate(*the_stack, r, relate_op); r }, + |t| { walk_ty(tcx, the_stack, t, relate_op); t }, + |t| { walk_ty(tcx, the_stack, t, relate_op); t }); + } + } + } + + fn relate(the_stack: &[ty::Region], + r_sub: ty::Region, + relate_op: &fn(ty::Region, ty::Region)) + { + for the_stack.each |&r| { + if !r.is_bound() && !r_sub.is_bound() { + relate_op(r, r_sub); + } + } + } +} + +pub fn relate_free_regions( + tcx: ty::ctxt, + self_ty: Option, + fn_sig: &ty::FnSig) +{ + /*! + * This function populates the region map's `free_region_map`. + * It walks over the transformed self type and argument types + * for each function just before we check the body of that + * function, looking for types where you have a borrowed + * pointer to other borrowed data (e.g., `&'a &'b [uint]`. + * We do not allow borrowed pointers to outlive the things they + * point at, so we can assume that `'a <= 'b`. + * + * Tests: `src/test/compile-fail/regions-free-region-ordering-*.rs` + */ + + debug!("relate_free_regions >>"); + + let mut all_tys = ~[]; + for fn_sig.inputs.each |arg| { + all_tys.push(arg.ty); + } + for self_ty.each |&t| { + all_tys.push(t); + } + + for all_tys.each |&t| { + debug!("relate_free_regions(t=%s)", ppaux::ty_to_str(tcx, t)); + relate_nested_regions(tcx, None, t, |a, b| { + match (&a, &b) { + (&ty::re_free(free_a), &ty::re_free(free_b)) => { + tcx.region_maps.relate_free_regions(free_a, free_b); + } + _ => {} + } + }) + } + + debug!("<< relate_free_regions"); +} \ No newline at end of file diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index a3296f6c209..0ef6e2512f3 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -535,7 +535,8 @@ pub fn compare_impl_method(tcx: ty::ctxt, // a free region. So, for example, if the impl type is // "&'self str", then this would replace the self type with a free // region `self`. - let dummy_self_r = ty::re_free(cm.body_id, ty::br_self); + let dummy_self_r = ty::re_free(ty::FreeRegion {scope_id: cm.body_id, + bound_region: ty::br_self}); let self_ty = replace_bound_self(tcx, self_ty, dummy_self_r); // Perform substitutions so that the trait/impl methods are expressed diff --git a/src/librustc/middle/typeck/infer/region_inference.rs b/src/librustc/middle/typeck/infer/region_inference.rs index e6354d4ab88..7252566d84c 100644 --- a/src/librustc/middle/typeck/infer/region_inference.rs +++ b/src/librustc/middle/typeck/infer/region_inference.rs @@ -538,10 +538,9 @@ more convincing in the future. use core::prelude::*; -use middle::region::is_subregion_of; -use middle::region; use middle::ty; -use middle::ty::{Region, RegionVid, re_static, re_infer, re_free, re_bound}; +use middle::ty::{FreeRegion, Region, RegionVid}; +use middle::ty::{re_static, re_infer, re_free, re_bound}; use middle::ty::{re_scope, ReVar, ReSkolemized, br_fresh}; use middle::typeck::infer::cres; use util::common::indenter; @@ -554,6 +553,7 @@ use core::to_bytes; use core::uint; use core::vec; use syntax::codemap::span; +use syntax::ast; #[deriving(Eq)] enum Constraint { @@ -1025,11 +1025,12 @@ pub impl RegionVarBindings { } priv impl RegionVarBindings { - fn is_subregion_of(&mut self, sub: Region, sup: Region) -> bool { - is_subregion_of(self.tcx.region_map, sub, sup) + fn is_subregion_of(&self, sub: Region, sup: Region) -> bool { + let rm = self.tcx.region_maps; + rm.is_subregion_of(sub, sup) } - fn lub_concrete_regions(&mut self, +a: Region, +b: Region) -> Region { + fn lub_concrete_regions(&self, +a: Region, +b: Region) -> Region { match (a, b) { (re_static, _) | (_, re_static) => { re_static // nothing lives longer than static @@ -1042,17 +1043,17 @@ priv impl RegionVarBindings { non-concrete regions: %?, %?", a, b)); } - (f @ re_free(f_id, _), re_scope(s_id)) | - (re_scope(s_id), f @ re_free(f_id, _)) => { + (f @ re_free(ref fr), re_scope(s_id)) | + (re_scope(s_id), f @ re_free(ref fr)) => { // A "free" region can be interpreted as "some region - // at least as big as the block f_id". So, we can + // at least as big as the block fr.scope_id". So, we can // reasonably compare free regions and scopes: - let rm = self.tcx.region_map; - match region::nearest_common_ancestor(rm, f_id, s_id) { - // if the free region's scope `f_id` is bigger than + let rm = self.tcx.region_maps; + match rm.nearest_common_ancestor(fr.scope_id, s_id) { + // if the free region's scope `fr.scope_id` is bigger than // the scope region `s_id`, then the LUB is the free // region itself: - Some(r_id) if r_id == f_id => f, + Some(r_id) if r_id == fr.scope_id => f, // otherwise, we don't know what the free region is, // so we must conservatively say the LUB is static: @@ -1064,32 +1065,67 @@ priv impl RegionVarBindings { // The region corresponding to an outer block is a // subtype of the region corresponding to an inner // block. - let rm = self.tcx.region_map; - match region::nearest_common_ancestor(rm, a_id, b_id) { + let rm = self.tcx.region_maps; + match rm.nearest_common_ancestor(a_id, b_id) { Some(r_id) => re_scope(r_id), _ => re_static } } + (re_free(ref a_fr), re_free(ref b_fr)) => { + self.lub_free_regions(a_fr, b_fr) + } + // For these types, we cannot define any additional // relationship: (re_infer(ReSkolemized(*)), _) | (_, re_infer(ReSkolemized(*))) | - (re_free(_, _), re_free(_, _)) | (re_bound(_), re_bound(_)) | - (re_bound(_), re_free(_, _)) | + (re_bound(_), re_free(_)) | (re_bound(_), re_scope(_)) | - (re_free(_, _), re_bound(_)) | + (re_free(_), re_bound(_)) | (re_scope(_), re_bound(_)) => { if a == b {a} else {re_static} } } } - fn glb_concrete_regions(&mut self, + fn lub_free_regions(&self, + a: &FreeRegion, + b: &FreeRegion) -> ty::Region + { + /*! + * Computes a region that encloses both free region arguments. + * Guarantee that if the same two regions are given as argument, + * in any order, a consistent result is returned. + */ + + return match a.cmp(b) { + Less => helper(self, a, b), + Greater => helper(self, b, a), + Equal => ty::re_free(*a) + }; + + fn helper(self: &RegionVarBindings, + a: &FreeRegion, + b: &FreeRegion) -> ty::Region + { + let rm = self.tcx.region_maps; + if rm.sub_free_region(*a, *b) { + ty::re_free(*b) + } else if rm.sub_free_region(*b, *a) { + ty::re_free(*a) + } else { + ty::re_static + } + } + } + + fn glb_concrete_regions(&self, +a: Region, +b: Region) -> cres { + debug!("glb_concrete_regions(%?, %?)", a, b); match (a, b) { (re_static, r) | (r, re_static) => { // static lives longer than everything else @@ -1104,37 +1140,26 @@ priv impl RegionVarBindings { non-concrete regions: %?, %?", a, b)); } - (re_free(f_id, _), s @ re_scope(s_id)) | - (s @ re_scope(s_id), re_free(f_id, _)) => { + (re_free(ref fr), s @ re_scope(s_id)) | + (s @ re_scope(s_id), re_free(ref fr)) => { // Free region is something "at least as big as - // `f_id`." If we find that the scope `f_id` is bigger + // `fr.scope_id`." If we find that the scope `fr.scope_id` is bigger // than the scope `s_id`, then we can say that the GLB // is the scope `s_id`. Otherwise, as we do not know // big the free region is precisely, the GLB is undefined. - let rm = self.tcx.region_map; - match region::nearest_common_ancestor(rm, f_id, s_id) { - Some(r_id) if r_id == f_id => Ok(s), + let rm = self.tcx.region_maps; + match rm.nearest_common_ancestor(fr.scope_id, s_id) { + Some(r_id) if r_id == fr.scope_id => Ok(s), _ => Err(ty::terr_regions_no_overlap(b, a)) } } - (re_scope(a_id), re_scope(b_id)) | - (re_free(a_id, _), re_free(b_id, _)) => { - if a == b { - // Same scope or same free identifier, easy case. - Ok(a) - } else { - // We want to generate the intersection of two - // scopes or two free regions. So, if one of - // these scopes is a subscope of the other, return - // it. Otherwise fail. - let rm = self.tcx.region_map; - match region::nearest_common_ancestor(rm, a_id, b_id) { - Some(r_id) if a_id == r_id => Ok(re_scope(b_id)), - Some(r_id) if b_id == r_id => Ok(re_scope(a_id)), - _ => Err(ty::terr_regions_no_overlap(b, a)) - } - } + (re_scope(a_id), re_scope(b_id)) => { + self.intersect_scopes(a, b, a_id, b_id) + } + + (re_free(ref a_fr), re_free(ref b_fr)) => { + self.glb_free_regions(a_fr, b_fr) } // For these types, we cannot define any additional @@ -1142,9 +1167,9 @@ priv impl RegionVarBindings { (re_infer(ReSkolemized(*)), _) | (_, re_infer(ReSkolemized(*))) | (re_bound(_), re_bound(_)) | - (re_bound(_), re_free(_, _)) | + (re_bound(_), re_free(_)) | (re_bound(_), re_scope(_)) | - (re_free(_, _), re_bound(_)) | + (re_free(_), re_bound(_)) | (re_scope(_), re_bound(_)) => { if a == b { Ok(a) @@ -1155,10 +1180,62 @@ priv impl RegionVarBindings { } } + fn glb_free_regions(&self, + a: &FreeRegion, + b: &FreeRegion) -> cres + { + /*! + * Computes a region that is enclosed by both free region arguments, + * if any. Guarantees that if the same two regions are given as argument, + * in any order, a consistent result is returned. + */ + + return match a.cmp(b) { + Less => helper(self, a, b), + Greater => helper(self, b, a), + Equal => Ok(ty::re_free(*a)) + }; + + fn helper(self: &RegionVarBindings, + a: &FreeRegion, + b: &FreeRegion) -> cres + { + let rm = self.tcx.region_maps; + if rm.sub_free_region(*a, *b) { + Ok(ty::re_free(*a)) + } else if rm.sub_free_region(*b, *a) { + Ok(ty::re_free(*b)) + } else { + self.intersect_scopes(ty::re_free(*a), ty::re_free(*b), + a.scope_id, b.scope_id) + } + } + } + fn report_type_error(&mut self, span: span, terr: &ty::type_err) { let terr_str = ty::type_err_to_str(self.tcx, terr); self.tcx.sess.span_err(span, terr_str); } + + fn intersect_scopes(&self, + region_a: ty::Region, + region_b: ty::Region, + scope_a: ast::node_id, + scope_b: ast::node_id) -> cres + { + // We want to generate the intersection of two + // scopes or two free regions. So, if one of + // these scopes is a subscope of the other, return + // it. Otherwise fail. + debug!("intersect_scopes(scope_a=%?, scope_b=%?, region_a=%?, region_b=%?)", + scope_a, scope_b, region_a, region_b); + let rm = self.tcx.region_maps; + match rm.nearest_common_ancestor(scope_a, scope_b) { + Some(r_id) if scope_a == r_id => Ok(re_scope(scope_b)), + Some(r_id) if scope_b == r_id => Ok(re_scope(scope_a)), + _ => Err(ty::terr_regions_no_overlap(region_a, region_b)) + } + } } // ______________________________________________________________________ diff --git a/src/librustc/middle/typeck/infer/test.rs b/src/librustc/middle/typeck/infer/test.rs index 52a0300c5a0..bf607e2c362 100644 --- a/src/librustc/middle/typeck/infer/test.rs +++ b/src/librustc/middle/typeck/infer/test.rs @@ -210,7 +210,9 @@ pub impl Env { } fn t_rptr_free(&self, nid: ast::node_id, id: uint) -> ty::t { - ty::mk_imm_rptr(self.tcx, ty::re_free(nid, ty::br_anon(id)), + ty::mk_imm_rptr(self.tcx, + ty::re_free(ty::FreeRegion {scope_id: nid, + bound_region: ty::br_anon(id)}), self.t_int()) } diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index f26eeeca446..f8877f76881 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -41,9 +41,9 @@ pub trait Repr { } pub fn note_and_explain_region(cx: ctxt, - prefix: ~str, + prefix: &str, region: ty::Region, - suffix: ~str) { + suffix: &str) { match explain_region_and_span(cx, region) { (ref str, Some(span)) => { cx.sess.span_note( @@ -98,23 +98,23 @@ pub fn explain_region_and_span(cx: ctxt, region: ty::Region) } } - re_free(id, br) => { - let prefix = match br { + re_free(ref fr) => { + let prefix = match fr.bound_region { br_anon(idx) => fmt!("the anonymous lifetime #%u defined on", idx + 1), br_fresh(_) => fmt!("an anonymous lifetime defined on"), _ => fmt!("the lifetime %s as defined on", - bound_region_to_str(cx, br)) + bound_region_to_str(cx, fr.bound_region)) }; - match cx.items.find(&id) { + match cx.items.find(&fr.scope_id) { Some(&ast_map::node_block(ref blk)) => { let (msg, opt_span) = explain_span(cx, "block", blk.span); (fmt!("%s %s", prefix, msg), opt_span) } Some(_) | None => { // this really should not happen - (fmt!("%s node %d", prefix, id), None) + (fmt!("%s node %d", prefix, fr.scope_id), None) } } } @@ -215,7 +215,7 @@ pub fn region_to_str_space(cx: ctxt, prefix: &str, region: Region) -> ~str { match region { re_scope(_) => prefix.to_str(), re_bound(br) => bound_region_to_str_space(cx, prefix, br), - re_free(_, br) => bound_region_to_str_space(cx, prefix, br), + re_free(ref fr) => bound_region_to_str_space(cx, prefix, fr.bound_region), re_infer(ReSkolemized(_, br)) => { bound_region_to_str_space(cx, prefix, br) } @@ -225,12 +225,16 @@ pub fn region_to_str_space(cx: ctxt, prefix: &str, region: Region) -> ~str { } pub fn mt_to_str(cx: ctxt, m: &mt) -> ~str { + mt_to_str_wrapped(cx, "", m, "") +} + +pub fn mt_to_str_wrapped(cx: ctxt, before: &str, m: &mt, after: &str) -> ~str { let mstr = match m.mutbl { ast::m_mutbl => "mut ", ast::m_imm => "", ast::m_const => "const " }; - return fmt!("%s%s", mstr, ty_to_str(cx, m.ty)); + return fmt!("%s%s%s%s", mstr, before, ty_to_str(cx, m.ty), after); } pub fn vstore_to_str(cx: ctxt, vs: ty::vstore) -> ~str { @@ -250,15 +254,14 @@ pub fn trait_store_to_str(cx: ctxt, s: ty::TraitStore) -> ~str { } } -pub fn vstore_ty_to_str(cx: ctxt, ty: ~str, vs: ty::vstore) -> ~str { +pub fn vstore_ty_to_str(cx: ctxt, mt: &mt, vs: ty::vstore) -> ~str { match vs { - ty::vstore_fixed(_) => { - fmt!("[%s, .. %s]", ty, vstore_to_str(cx, vs)) - } - ty::vstore_slice(_) => { - fmt!("%s %s", vstore_to_str(cx, vs), ty) - } - _ => fmt!("%s[%s]", vstore_to_str(cx, vs), ty) + ty::vstore_fixed(_) => { + fmt!("[%s, .. %s]", mt_to_str(cx, mt), vstore_to_str(cx, vs)) + } + _ => { + fmt!("%s%s", vstore_to_str(cx, vs), mt_to_str_wrapped(cx, "[", mt, "]")) + } } } @@ -460,7 +463,7 @@ pub fn ty_to_str(cx: ctxt, typ: t) -> ~str { fmt!("%s%s", trait_store_to_str(cx, s), ty) } ty_evec(ref mt, vs) => { - vstore_ty_to_str(cx, fmt!("%s", mt_to_str(cx, mt)), vs) + vstore_ty_to_str(cx, mt, vs) } ty_estr(vs) => fmt!("%s%s", vstore_to_str(cx, vs), ~"str"), ty_opaque_box => ~"@?", diff --git a/src/test/compile-fail/kindck-owned-trait-contains.rs b/src/test/compile-fail/kindck-owned-trait-contains.rs index f5153265308..54ee8bcc70e 100644 --- a/src/test/compile-fail/kindck-owned-trait-contains.rs +++ b/src/test/compile-fail/kindck-owned-trait-contains.rs @@ -27,5 +27,6 @@ fn main() { let x: &'blk int = &3; repeater(@x) }; - assert!(3 == *(y.get())); //~ ERROR reference is not valid + assert!(3 == *(y.get())); //~ ERROR dereference of reference outside its lifetime + //~^ ERROR reference is not valid outside of its lifetime } diff --git a/src/test/compile-fail/regions-escape-via-trait-or-not.rs b/src/test/compile-fail/regions-escape-via-trait-or-not.rs index fb9d963dc80..f7165784c79 100644 --- a/src/test/compile-fail/regions-escape-via-trait-or-not.rs +++ b/src/test/compile-fail/regions-escape-via-trait-or-not.rs @@ -24,9 +24,9 @@ fn with(f: &fn(x: &int) -> R) -> int { fn return_it() -> int { with(|o| o) - //~^ ERROR reference is not valid outside of its lifetime + //~^ ERROR cannot infer an appropriate lifetime due to conflicting requirements //~^^ ERROR reference is not valid outside of its lifetime - //~^^^ ERROR cannot infer an appropriate lifetime + //~^^^ ERROR reference is not valid outside of its lifetime } fn main() { diff --git a/src/test/compile-fail/regions-free-region-ordering-callee.rs b/src/test/compile-fail/regions-free-region-ordering-callee.rs new file mode 100644 index 00000000000..e5399fc7fa3 --- /dev/null +++ b/src/test/compile-fail/regions-free-region-ordering-callee.rs @@ -0,0 +1,37 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests that callees correctly infer an ordering between free regions +// that appear in their parameter list. See also +// regions-free-region-ordering-caller.rs + +fn ordering1<'a, 'b>(x: &'a &'b uint) -> &'a uint { + // It is safe to assume that 'a <= 'b due to the type of x + let y: &'b uint = &**x; + return y; +} + +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 +} + +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 due to conflicting requirements + fail!(); +} + +fn ordering4<'a, 'b>(a: &'a uint, b: &'b uint, x: &fn(&'a &'b uint)) { + let z: Option<&'a &'b uint> = None; +} + +fn main() {} \ No newline at end of file diff --git a/src/test/compile-fail/regions-free-region-ordering-caller.rs b/src/test/compile-fail/regions-free-region-ordering-caller.rs new file mode 100644 index 00000000000..d06dcd8aa86 --- /dev/null +++ b/src/test/compile-fail/regions-free-region-ordering-caller.rs @@ -0,0 +1,40 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test various ways to construct a pointer with a longer lifetime +// than the thing it points at and ensure that they result in +// errors. See also regions-free-region-ordering-callee.rs + +struct Paramd<'self> { x: &'self uint } + +fn call1<'a>(x: &'a uint) { + let y: uint = 3; + let z: &'a &'blk uint = &(&y); + //~^ ERROR pointer has a longer lifetime than the data it references +} + +fn call2<'a, 'b>(a: &'a uint, b: &'b uint) { + let z: Option<&'b &'a uint> = None; + //~^ ERROR pointer has a longer lifetime than the data it references +} + +fn call3<'a, 'b>(a: &'a uint, b: &'b uint) { + let y: Paramd<'a> = Paramd { x: a }; + let z: Option<&'b Paramd<'a>> = None; + //~^ ERROR pointer has a longer lifetime than the data it references +} + +fn call4<'a, 'b>(a: &'a uint, b: &'b uint) { + let z: Option<&fn(&'a &'b uint)> = None; + //~^ ERROR pointer has a longer lifetime than the data it references +} + + +fn main() {} \ No newline at end of file diff --git a/src/test/compile-fail/regions-trait-2.rs b/src/test/compile-fail/regions-trait-2.rs index 5811496cab4..9855a234618 100644 --- a/src/test/compile-fail/regions-trait-2.rs +++ b/src/test/compile-fail/regions-trait-2.rs @@ -8,6 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// xfail-test #5723 + +// Test that you cannot escape a borrowed pointer +// into a trait. + struct ctxt { v: uint } trait get_ctxt { @@ -24,8 +29,9 @@ fn make_gc() -> @get_ctxt { let ctxt = ctxt { v: 22u }; let hc = has_ctxt { c: &ctxt }; return @hc as @get_ctxt; + //^~ ERROR source contains borrowed pointer } fn main() { - make_gc().get_ctxt().v; //~ ERROR illegal borrow + make_gc().get_ctxt().v; } From 49de82cdca2064a909d3104f4e5eccacb0425fd0 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 10 Apr 2013 13:11:27 -0700 Subject: [PATCH 2/5] Issue #5656: Make &self not mean "&'self self" Fixes #5656. Fixes #5541. --- src/librustc/middle/kind.rs | 2 +- src/librustc/middle/region.rs | 29 +- src/librustc/middle/subst.rs | 47 +-- src/librustc/middle/ty.rs | 3 +- src/librustc/middle/typeck/check/method.rs | 283 ++++++++---------- src/librustc/middle/typeck/check/mod.rs | 71 ++--- .../middle/typeck/check/regionmanip.rs | 42 +-- src/librustc/middle/typeck/check/vtable.rs | 7 +- src/librustc/middle/typeck/coherence.rs | 2 +- src/librustc/middle/typeck/collect.rs | 99 ++++-- src/librustc/middle/typeck/rscope.rs | 5 +- src/librustc/util/ppaux.rs | 6 + src/libsyntax/ast.rs | 9 - src/libsyntax/ext/base.rs | 13 + src/libsyntax/opt_vec.rs | 11 + src/libsyntax/print/pprust.rs | 4 + 16 files changed, 337 insertions(+), 296 deletions(-) diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs index 1f8401c0d53..e5fc9f2d603 100644 --- a/src/librustc/middle/kind.rs +++ b/src/librustc/middle/kind.rs @@ -16,7 +16,7 @@ use middle::liveness; use middle::pat_util; use middle::ty; use middle::typeck; -use util::ppaux::{Repr, ty_to_str, tys_to_str}; +use util::ppaux::{Repr, ty_to_str}; use syntax::ast::*; use syntax::attr::attrs_contains_name; diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 79130097078..ecb9fc2cd08 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -544,10 +544,6 @@ pub struct DetermineRpCtxt { // see long discussion on region_is_relevant(). anon_implies_rp: bool, - // true when we are not within an &self method. - // see long discussion on region_is_relevant(). - self_implies_rp: bool, - // encodes the context of the current type; invariant if // mutable, covariant otherwise ambient_variance: region_variance, @@ -689,7 +685,7 @@ pub impl DetermineRpCtxt { false } Some(ref l) if l.ident == special_idents::self_ => { - self.self_implies_rp + true } Some(_) => { false @@ -700,23 +696,18 @@ pub impl DetermineRpCtxt { fn with(@mut self, item_id: ast::node_id, anon_implies_rp: bool, - self_implies_rp: bool, f: &fn()) { let old_item_id = self.item_id; let old_anon_implies_rp = self.anon_implies_rp; - let old_self_implies_rp = self.self_implies_rp; self.item_id = item_id; self.anon_implies_rp = anon_implies_rp; - self.self_implies_rp = self_implies_rp; - debug!("with_item_id(%d, %b, %b)", + debug!("with_item_id(%d, %b)", item_id, - anon_implies_rp, - self_implies_rp); + anon_implies_rp); let _i = ::util::common::indenter(); f(); self.item_id = old_item_id; self.anon_implies_rp = old_anon_implies_rp; - self.self_implies_rp = old_self_implies_rp; } fn with_ambient_variance(@mut self, variance: region_variance, f: &fn()) { @@ -730,7 +721,7 @@ pub impl DetermineRpCtxt { pub fn determine_rp_in_item(item: @ast::item, &&cx: @mut DetermineRpCtxt, visitor: visit::vt<@mut DetermineRpCtxt>) { - do cx.with(item.id, true, true) { + do cx.with(item.id, true) { visit::visit_item(item, cx, visitor); } } @@ -742,12 +733,7 @@ pub fn determine_rp_in_fn(fk: &visit::fn_kind, _: ast::node_id, &&cx: @mut DetermineRpCtxt, visitor: visit::vt<@mut DetermineRpCtxt>) { - let self_implies_rp = match fk { - &visit::fk_method(_, _, m) => !m.self_ty.node.is_borrowed(), - _ => true - }; - - do cx.with(cx.item_id, false, self_implies_rp) { + do cx.with(cx.item_id, false) { do cx.with_ambient_variance(rv_contravariant) { for decl.inputs.each |a| { (visitor.visit_ty)(a.ty, cx, visitor); @@ -763,7 +749,7 @@ pub fn determine_rp_in_fn(fk: &visit::fn_kind, pub fn determine_rp_in_ty_method(ty_m: &ast::ty_method, &&cx: @mut DetermineRpCtxt, visitor: visit::vt<@mut DetermineRpCtxt>) { - do cx.with(cx.item_id, false, !ty_m.self_ty.node.is_borrowed()) { + do cx.with(cx.item_id, false) { visit::visit_ty_method(ty_m, cx, visitor); } } @@ -868,7 +854,7 @@ pub fn determine_rp_in_ty(ty: @ast::Ty, ast::ty_bare_fn(@ast::TyBareFn {decl: ref decl, _}) => { // fn() binds the & region, so do not consider &T types that // appear *inside* a fn() type to affect the enclosing item: - do cx.with(cx.item_id, false, true) { + do cx.with(cx.item_id, false) { // parameters are contravariant do cx.with_ambient_variance(rv_contravariant) { for decl.inputs.each |a| { @@ -929,7 +915,6 @@ pub fn determine_rp_in_crate(sess: Session, worklist: ~[], item_id: 0, anon_implies_rp: false, - self_implies_rp: true, ambient_variance: rv_covariant }; diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index a754f93f010..35257f9574c 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -62,22 +62,7 @@ impl EffectfulSubst for ty::t { _ => { ty::fold_regions_and_ty( tcx, *self, - |r| match r { - ty::re_bound(ty::br_self) => { - match substs.self_r { - None => { - tcx.sess.bug( - fmt!("ty::subst: \ - Reference to self region when \ - given substs with no self region, \ - ty = %s", - self.repr(tcx))); - } - Some(self_r) => self_r - } - } - _ => r - }, + |r| r.subst(tcx, substs), |t| t.effectfulSubst(tcx, substs), |t| t.effectfulSubst(tcx, substs)) } @@ -118,7 +103,7 @@ impl Subst for ty::TraitRef { impl Subst for ty::substs { fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::substs { ty::substs { - self_r: self.self_r, + self_r: self.self_r.subst(tcx, substs), self_ty: self.self_ty.map(|typ| typ.subst(tcx, substs)), tps: self.tps.map(|typ| typ.subst(tcx, substs)) } @@ -166,6 +151,34 @@ impl Subst for ty::Generics { } } +impl Subst for ty::Region { + fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::Region { + // Note: This routine only handles the self region, because it + // is only concerned with substitutions of regions that appear + // in types. Region substitution of the bound regions that + // appear in a function signature is done using the + // specialized routine + // `middle::typeck::check::regionmanip::replace_bound_regions_in_fn_sig()`. + // As we transition to the new region syntax this distinction + // will most likely disappear. + match self { + &ty::re_bound(ty::br_self) => { + match substs.self_r { + None => { + tcx.sess.bug( + fmt!("ty::Region#subst(): \ + Reference to self region when \ + given substs with no self region: %s", + substs.repr(tcx))); + } + Some(self_r) => self_r + } + } + _ => *self + } + } +} + impl Subst for ty::ty_param_bounds_and_ty { fn subst(&self, tcx: ty::ctxt, substs: &ty::substs) -> ty::ty_param_bounds_and_ty { ty::ty_param_bounds_and_ty { diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 3921764e6af..2b4a4235950 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -24,8 +24,7 @@ use middle::subst::Subst; use middle::typeck; use middle; use util::ppaux::{note_and_explain_region, bound_region_to_str}; -use util::ppaux::{region_to_str, vstore_to_str}; -use util::ppaux::{trait_store_to_str, ty_to_str, tys_to_str}; +use util::ppaux::{trait_store_to_str, ty_to_str, vstore_to_str}; use util::ppaux::Repr; use util::common::{indenter}; diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index 6a274e7f9eb..6b09133e73a 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -178,15 +178,6 @@ pub struct Candidate { origin: method_origin, } -/** - * How the self type should be transformed according to the form of explicit - * self provided by the method. - */ -pub enum TransformTypeFlag { - TransformTypeNormally, - TransformTypeForObject, -} - pub impl<'self> LookupContext<'self> { fn do_lookup(&self, self_ty: ty::t) -> Option { let mut self_ty = structurally_resolved_type(self.fcx, @@ -285,13 +276,13 @@ pub impl<'self> LookupContext<'self> { fn push_inherent_candidates(&self, self_ty: ty::t) { /*! - * * Collect all inherent candidates into * `self.inherent_candidates`. See comment at the start of * the file. To find the inherent candidates, we repeatedly * deref the self-ty to find the "base-type". So, for * example, if the receiver is @@C where `C` is a struct type, - * we'll want to find the inherent impls for `C`. */ + * we'll want to find the inherent impls for `C`. + */ let mut enum_dids = ~[]; let mut self_ty = self_ty; @@ -407,16 +398,9 @@ pub impl<'self> LookupContext<'self> { }; let method = trait_methods[pos]; - let (rcvr_ty, rcvr_substs) = - self.create_rcvr_ty_and_substs_for_method( - method.self_ty, - rcvr_ty, - copy bound_trait_ref.substs, - TransformTypeNormally); - let cand = Candidate { rcvr_ty: rcvr_ty, - rcvr_substs: rcvr_substs, + rcvr_substs: copy bound_trait_ref.substs, method_ty: method, origin: method_param( method_param { @@ -476,14 +460,8 @@ pub impl<'self> LookupContext<'self> { ../*bad*/copy *substs }; - let (rcvr_ty, rcvr_substs) = - self.create_rcvr_ty_and_substs_for_method(method.self_ty, - self_ty, - rcvr_substs, - TransformTypeForObject); - self.inherent_candidates.push(Candidate { - rcvr_ty: rcvr_ty, + rcvr_ty: self_ty, rcvr_substs: rcvr_substs, method_ty: method, origin: method_trait(did, index, store) @@ -538,19 +516,13 @@ pub impl<'self> LookupContext<'self> { // We've found a method -- return it let rcvr_substs = substs {self_ty: Some(self_ty), ..copy *substs }; - let (rcvr_ty, rcvr_substs) = - self.create_rcvr_ty_and_substs_for_method( - info.method_ty.self_ty, - self_ty, - rcvr_substs, - TransformTypeNormally); let origin = if did == info.trait_def_id { method_self(info.trait_def_id, info.index) } else { method_super(info.trait_def_id, info.index) }; self.inherent_candidates.push(Candidate { - rcvr_ty: rcvr_ty, + rcvr_ty: self_ty, rcvr_substs: rcvr_substs, method_ty: info.method_ty, origin: origin @@ -598,13 +570,6 @@ pub impl<'self> LookupContext<'self> { ty: impl_ty } = impl_self_ty(&vcx, location_info, impl_info.did); - let (impl_ty, impl_substs) = - self.create_rcvr_ty_and_substs_for_method( - method.self_ty, - impl_ty, - impl_substs, - TransformTypeNormally); - candidates.push(Candidate { rcvr_ty: impl_ty, rcvr_substs: impl_substs, @@ -639,69 +604,16 @@ pub impl<'self> LookupContext<'self> { self_ty: None, tps: ~[] }; - let (impl_ty, impl_substs) = - self.create_rcvr_ty_and_substs_for_method( - method.self_ty, - self_ty, - dummy_substs, - TransformTypeNormally); candidates.push(Candidate { - rcvr_ty: impl_ty, - rcvr_substs: impl_substs, + rcvr_ty: self_ty, + rcvr_substs: dummy_substs, method_ty: method, origin: method_static(provided_method_info.method_info.did) }); } } - fn create_rcvr_ty_and_substs_for_method(&self, - self_decl: ast::self_ty_, - self_ty: ty::t, - +self_substs: ty::substs, - transform_type: TransformTypeFlag) - -> (ty::t, ty::substs) { - // If the self type includes a region (like &self), we need to - // ensure that the receiver substitutions have a self region. - // If the receiver type does not itself contain borrowed - // pointers, there may not be one yet. - // - // FIXME(#3446)--this awkward situation comes about because - // the regions in the receiver are substituted before (and - // differently from) those in the argument types. This - // shouldn't really have to be. - let rcvr_substs = { - match self_decl { - sty_static | sty_value | - sty_box(_) | sty_uniq(_) => { - self_substs - } - sty_region(*) if self_substs.self_r.is_some() => { - // FIXME(#4846) ignoring expl lifetime here - self_substs - } - sty_region(*) => { - // FIXME(#4846) ignoring expl lifetime here - substs { - self_r: - Some(self.infcx().next_region_var( - self.expr.span, - self.expr.id)), - ..self_substs - } - } - } - }; - - let rcvr_ty = transform_self_type_for_method(self.tcx(), - rcvr_substs.self_r, - self_ty, - self_decl, - transform_type); - - (rcvr_ty, rcvr_substs) - } - // ______________________________________________________________________ // Candidate selection (see comment at start of file) @@ -1036,20 +948,34 @@ pub impl<'self> LookupContext<'self> { self.enforce_trait_instance_limitations(fty, candidate); self.enforce_drop_trait_limitations(candidate); - // before we only checked whether self_ty could be a subtype - // of rcvr_ty; now we actually make it so (this may cause - // variables to unify etc). Since we checked beforehand, and - // nothing has changed in the meantime, this unification - // should never fail. - match self.fcx.mk_subty(false, self.self_expr.span, - self_ty, candidate.rcvr_ty) { - result::Ok(_) => (), - result::Err(_) => { - self.bug(fmt!("%s was assignable to %s but now is not?", - self.ty_to_str(self_ty), - self.ty_to_str(candidate.rcvr_ty))); + // static methods should never have gotten this far: + assert!(candidate.method_ty.self_ty != sty_static); + + let transformed_self_ty = match candidate.origin { + method_trait(*) => { + match candidate.method_ty.self_ty { + sty_region(*) => { + // FIXME(#5762) again, preserving existing + // behavior here which (for &self) desires + // &@Trait where @Trait is the type of the + // receiver. Here we fetch the method's + // transformed_self_ty which will be something + // like &'a Self. We then perform a + // substitution which will replace Self with + // @Trait. + let t = candidate.method_ty.transformed_self_ty.get(); + ty::subst(tcx, &candidate.rcvr_substs, t) + } + _ => { + candidate.rcvr_ty + } + } } - } + _ => { + let t = candidate.method_ty.transformed_self_ty.get(); + ty::subst(tcx, &candidate.rcvr_substs, t) + } + }; // Determine the values for the type parameters of the method. // If they were not explicitly supplied, just construct fresh @@ -1100,16 +1026,32 @@ pub impl<'self> LookupContext<'self> { fmt!("Invoking method with non-bare-fn ty: %?", s)); } }; - let (_, _, fn_sig) = + let (_, opt_transformed_self_ty, fn_sig) = replace_bound_regions_in_fn_sig( - tcx, @Nil, None, &bare_fn_ty.sig, + tcx, @Nil, Some(transformed_self_ty), &bare_fn_ty.sig, |_br| self.fcx.infcx().next_region_var( self.expr.span, self.expr.id)); + let transformed_self_ty = opt_transformed_self_ty.get(); let fty = ty::mk_bare_fn(tcx, ty::BareFnTy {sig: fn_sig, ..bare_fn_ty}); debug!("after replacing bound regions, fty=%s", self.ty_to_str(fty)); let self_mode = get_mode_from_self_type(candidate.method_ty.self_ty); + // before we only checked whether self_ty could be a subtype + // of rcvr_ty; now we actually make it so (this may cause + // variables to unify etc). Since we checked beforehand, and + // nothing has changed in the meantime, this unification + // should never fail. + match self.fcx.mk_subty(false, self.self_expr.span, + self_ty, transformed_self_ty) { + result::Ok(_) => (), + result::Err(_) => { + self.bug(fmt!("%s was a subtype of %s but now is not?", + self.ty_to_str(self_ty), + self.ty_to_str(transformed_self_ty))); + } + } + self.fcx.write_ty(self.callee_id, fty); self.fcx.write_substs(self.callee_id, all_substs); method_map_entry { @@ -1180,7 +1122,87 @@ pub impl<'self> LookupContext<'self> { debug!("is_relevant(self_ty=%s, candidate=%s)", self.ty_to_str(self_ty), self.cand_to_str(candidate)); - self.fcx.can_mk_subty(self_ty, candidate.rcvr_ty).is_ok() + // Check for calls to object methods. We resolve these differently. + // + // FIXME(#5762)---we don't check that an @self method is only called + // on an @Trait object here and so forth + match candidate.origin { + method_trait(*) => { + match candidate.method_ty.self_ty { + sty_static | sty_value => { + return false; + } + sty_region(*) => { + // just echoing current behavior here, which treats + // an &self method on an @Trait object as requiring + // an &@Trait receiver (wacky) + } + sty_box(*) | sty_uniq(*) => { + return self.fcx.can_mk_subty(self_ty, + candidate.rcvr_ty).is_ok(); + } + }; + } + _ => {} + } + + return match candidate.method_ty.self_ty { + sty_static => { + false + } + + sty_value => { + self.fcx.can_mk_subty(self_ty, candidate.rcvr_ty).is_ok() + } + + sty_region(_, m) => { + match ty::get(self_ty).sty { + ty::ty_rptr(_, mt) => { + mutability_matches(mt.mutbl, m) && + self.fcx.can_mk_subty(mt.ty, candidate.rcvr_ty).is_ok() + } + + _ => false + } + } + + sty_box(m) => { + match ty::get(self_ty).sty { + ty::ty_box(mt) => { + mutability_matches(mt.mutbl, m) && + self.fcx.can_mk_subty(mt.ty, candidate.rcvr_ty).is_ok() + } + + _ => false + } + } + + sty_uniq(m) => { + match ty::get(self_ty).sty { + ty::ty_uniq(mt) => { + mutability_matches(mt.mutbl, m) && + self.fcx.can_mk_subty(mt.ty, candidate.rcvr_ty).is_ok() + } + + _ => false + } + } + }; + + fn mutability_matches(self_mutbl: ast::mutability, + candidate_mutbl: ast::mutability) -> bool { + //! True if `self_mutbl <: candidate_mutbl` + + match (self_mutbl, candidate_mutbl) { + (_, m_const) => true, + (m_mutbl, m_mutbl) => true, + (m_imm, m_imm) => true, + (m_mutbl, m_imm) => false, + (m_imm, m_mutbl) => false, + (m_const, m_imm) => false, + (m_const, m_mutbl) => false, + } + } } fn fn_ty_from_origin(&self, origin: &method_origin) -> ty::t { @@ -1281,45 +1303,6 @@ pub impl<'self> LookupContext<'self> { } } -pub fn transform_self_type_for_method(tcx: ty::ctxt, - self_region: Option, - impl_ty: ty::t, - self_type: ast::self_ty_, - flag: TransformTypeFlag) - -> ty::t { - match self_type { - sty_static => { - tcx.sess.bug(~"calling transform_self_type_for_method on \ - static method"); - } - sty_value => { - impl_ty - } - sty_region(_, mutability) => { - // FIXME(#4846) ignoring expl lifetime here - mk_rptr(tcx, - self_region.expect(~"self region missing for &self param"), - ty::mt { ty: impl_ty, mutbl: mutability }) - } - sty_box(mutability) => { - match flag { - TransformTypeNormally => { - mk_box(tcx, ty::mt { ty: impl_ty, mutbl: mutability }) - } - TransformTypeForObject => impl_ty - } - } - sty_uniq(mutability) => { - match flag { - TransformTypeNormally => { - mk_uniq(tcx, ty::mt { ty: impl_ty, mutbl: mutability }) - } - TransformTypeForObject => impl_ty - } - } - } -} - pub fn get_mode_from_self_type(self_type: ast::self_ty_) -> ast::rmode { match self_type { sty_value => by_copy, _ => by_ref } } diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 6bc1317d5f9..5c7a6d9f52a 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -93,7 +93,6 @@ use middle::typeck::check::method::{AutoderefReceiver}; use middle::typeck::check::method::{AutoderefReceiverFlag}; use middle::typeck::check::method::{CheckTraitsAndInherentMethods}; use middle::typeck::check::method::{CheckTraitsOnly, DontAutoderefReceiver}; -use middle::typeck::check::method::{TransformTypeNormally}; use middle::typeck::check::regionmanip::replace_bound_regions_in_fn_sig; use middle::typeck::check::regionmanip::relate_free_regions; use middle::typeck::check::vtable::{LocationInfo, VtableContext}; @@ -101,9 +100,8 @@ use middle::typeck::CrateCtxt; use middle::typeck::infer::{resolve_type, force_tvar}; use middle::typeck::infer; use middle::typeck::rscope::bound_self_region; -use middle::typeck::rscope::{RegionError, RegionParameterization}; +use middle::typeck::rscope::{RegionError}; use middle::typeck::rscope::region_scope; -use middle::typeck::rscope; use middle::typeck::{isr_alist, lookup_def_ccx}; use middle::typeck::no_params; use middle::typeck::{require_same_types, method_map, vtable_map}; @@ -280,7 +278,7 @@ pub fn check_bare_fn(ccx: @mut CrateCtxt, } pub fn check_fn(ccx: @mut CrateCtxt, - +self_info: Option, + opt_self_info: Option, purity: ast::purity, fn_sig: &ty::FnSig, decl: &ast::fn_decl, @@ -307,23 +305,28 @@ pub fn check_fn(ccx: @mut CrateCtxt, // First, we have to replace any bound regions in the fn and self // types with free ones. The free region references will be bound // the node_id of the body block. - - let (isr, self_info, fn_sig) = { - replace_bound_regions_in_fn_sig( - tcx, inherited_isr, self_info, fn_sig, - |br| ty::re_free(ty::FreeRegion {scope_id: body.node.id, - bound_region: br})) + let (isr, opt_self_info, fn_sig) = { + let opt_self_ty = opt_self_info.map(|i| i.self_ty); + let (isr, opt_self_ty, fn_sig) = + replace_bound_regions_in_fn_sig( + tcx, inherited_isr, opt_self_ty, fn_sig, + |br| ty::re_free(ty::FreeRegion {scope_id: body.node.id, + bound_region: br})); + let opt_self_info = + opt_self_info.map( + |si| SelfInfo {self_ty: opt_self_ty.get(), ..*si}); + (isr, opt_self_info, fn_sig) }; - relate_free_regions(tcx, self_info.map(|s| s.self_ty), &fn_sig); + relate_free_regions(tcx, opt_self_info.map(|s| s.self_ty), &fn_sig); let arg_tys = fn_sig.inputs.map(|a| a.ty); let ret_ty = fn_sig.output; - debug!("check_fn(arg_tys=%?, ret_ty=%?, self_info.self_ty=%?)", - arg_tys.map(|a| ppaux::ty_to_str(tcx, *a)), + debug!("check_fn(arg_tys=%?, ret_ty=%?, opt_self_ty=%?)", + arg_tys.map(|&a| ppaux::ty_to_str(tcx, a)), ppaux::ty_to_str(tcx, ret_ty), - self_info.map(|s| ppaux::ty_to_str(tcx, s.self_ty))); + opt_self_info.map(|si| ppaux::ty_to_str(tcx, si.self_ty))); // ______________________________________________________________________ // Create the function context. This is either derived from scratch or, @@ -348,7 +351,7 @@ pub fn check_fn(ccx: @mut CrateCtxt, } }; - gather_locals(fcx, decl, body, arg_tys, self_info); + gather_locals(fcx, decl, body, arg_tys, opt_self_info); check_block_with_expected(fcx, body, Some(ret_ty)); // We unify the tail expr's type with the @@ -366,7 +369,7 @@ pub fn check_fn(ccx: @mut CrateCtxt, None => () } - for self_info.each |self_info| { + for opt_self_info.each |self_info| { fcx.write_ty(self_info.self_id, self_info.self_ty); } for vec::each2(decl.inputs, arg_tys) |input, arg| { @@ -379,7 +382,7 @@ pub fn check_fn(ccx: @mut CrateCtxt, decl: &ast::fn_decl, body: &ast::blk, arg_tys: &[ty::t], - self_info: Option) { + opt_self_info: Option) { let tcx = fcx.ccx.tcx; let assign: @fn(ast::node_id, Option) = |nid, ty_opt| { @@ -398,7 +401,7 @@ pub fn check_fn(ccx: @mut CrateCtxt, }; // Add the self parameter - for self_info.each |self_info| { + for opt_self_info.each |self_info| { assign(self_info.self_id, Some(self_info.self_ty)); debug!("self is assigned to %s", fcx.infcx().ty_to_str( @@ -484,26 +487,22 @@ pub fn check_fn(ccx: @mut CrateCtxt, } pub fn check_method(ccx: @mut CrateCtxt, - method: @ast::method, - self_ty: ty::t) + method: @ast::method) { - let self_info = if method.self_ty.node == ast::sty_static {None} else { - let ty = method::transform_self_type_for_method( - ccx.tcx, - Some(ty::re_bound(ty::br_self)), - self_ty, - method.self_ty.node, - TransformTypeNormally); - Some(SelfInfo {self_ty: ty, self_id: method.self_id, - span: method.self_ty.span}) - }; + let method_def_id = local_def(method.id); + let method_ty = ty::method(ccx.tcx, method_def_id); + let opt_self_info = method_ty.transformed_self_ty.map(|&ty| { + SelfInfo {self_ty: ty, + self_id: method.self_id, + span: method.self_ty.span} + }); check_bare_fn( ccx, &method.decl, &method.body, method.id, - self_info + opt_self_info ); } @@ -575,15 +574,12 @@ pub fn check_item(ccx: @mut CrateCtxt, it: @ast::item) { ast::item_fn(ref decl, _, _, _, ref body) => { check_bare_fn(ccx, decl, body, it.id, None); } - ast::item_impl(ref generics, _, ty, ref ms) => { + ast::item_impl(_, _, _, ref ms) => { let rp = ccx.tcx.region_paramd_items.find(&it.id).map_consume(|x| *x); debug!("item_impl %s with id %d rp %?", *ccx.tcx.sess.str_of(it.ident), it.id, rp); - let rp = RegionParameterization::from_variance_and_generics( - rp, generics); - let self_ty = ccx.to_ty(&rscope::type_rscope(rp), ty); for ms.each |m| { - check_method(ccx, *m, self_ty); + check_method(ccx, *m); } } ast::item_trait(_, _, ref trait_methods) => { @@ -594,8 +590,7 @@ pub fn check_item(ccx: @mut CrateCtxt, it: @ast::item) { // bodies to check. } provided(m) => { - let self_ty = ty::mk_self(ccx.tcx, local_def(it.id)); - check_method(ccx, m, self_ty); + check_method(ccx, m); } } } diff --git a/src/librustc/middle/typeck/check/regionmanip.rs b/src/librustc/middle/typeck/check/regionmanip.rs index 359f938d0d9..1abcefeefac 100644 --- a/src/librustc/middle/typeck/check/regionmanip.rs +++ b/src/librustc/middle/typeck/check/regionmanip.rs @@ -13,7 +13,7 @@ use core::prelude::*; use middle::ty; -use middle::typeck::check::SelfInfo; + use middle::typeck::isr_alist; use util::common::indenter; use util::ppaux::region_to_str; @@ -26,29 +26,24 @@ use std::list::Cons; pub fn replace_bound_regions_in_fn_sig( tcx: ty::ctxt, isr: isr_alist, - self_info: Option, + opt_self_ty: Option, fn_sig: &ty::FnSig, mapf: &fn(ty::bound_region) -> ty::Region) - -> (isr_alist, Option, ty::FnSig) + -> (isr_alist, Option, ty::FnSig) { - // Take self_info apart; the self_ty part is the only one we want - // to update here. - let self_ty = self_info.map(|s| s.self_ty); - let rebuild_self_info = |t| self_info.map(|s| SelfInfo{self_ty: t, ..*s}); - let mut all_tys = ty::tys_in_fn_sig(fn_sig); - for self_info.each |self_info| { - all_tys.push(self_info.self_ty); + for opt_self_ty.each |&self_ty| { + all_tys.push(self_ty); } - for self_ty.each |t| { all_tys.push(*t) } + for opt_self_ty.each |&t| { all_tys.push(t) } - debug!("replace_bound_regions_in_fn_sig(self_info.self_ty=%?, fn_sig=%s, \ + debug!("replace_bound_regions_in_fn_sig(self_ty=%?, fn_sig=%s, \ all_tys=%?)", - self_ty.map(|t| ppaux::ty_to_str(tcx, *t)), + opt_self_ty.map(|&t| ppaux::ty_to_str(tcx, t)), ppaux::fn_sig_to_str(tcx, fn_sig), - all_tys.map(|t| ppaux::ty_to_str(tcx, *t))); + all_tys.map(|&t| ppaux::ty_to_str(tcx, t))); let _i = indenter(); let isr = do create_bound_region_mapping(tcx, isr, all_tys) |br| { @@ -58,20 +53,15 @@ pub fn replace_bound_regions_in_fn_sig( let new_fn_sig = ty::fold_sig(fn_sig, |t| { replace_bound_regions(tcx, isr, t) }); - let t_self = self_ty.map(|t| replace_bound_regions(tcx, isr, *t)); + let new_self_ty = opt_self_ty.map(|&t| replace_bound_regions(tcx, isr, t)); - debug!("result of replace_bound_regions_in_fn_sig: self_info.self_ty=%?, \ - fn_sig=%s", - t_self.map(|t| ppaux::ty_to_str(tcx, *t)), + debug!("result of replace_bound_regions_in_fn_sig: \ + new_self_ty=%?, \ + fn_sig=%s", + new_self_ty.map(|&t| ppaux::ty_to_str(tcx, t)), ppaux::fn_sig_to_str(tcx, &new_fn_sig)); - // Glue updated self_ty back together with its original def_id. - let new_self_info: Option = match t_self { - None => None, - Some(t) => rebuild_self_info(t) - }; - - return (isr, new_self_info, new_fn_sig); + return (isr, new_self_ty, new_fn_sig); // Takes `isr`, a (possibly empty) mapping from in-scope region // names ("isr"s) to their corresponding regions; `tys`, a list of @@ -288,4 +278,4 @@ pub fn relate_free_regions( } debug!("<< relate_free_regions"); -} \ No newline at end of file +} diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs index 6ea668605fd..8245dc88114 100644 --- a/src/librustc/middle/typeck/check/vtable.rs +++ b/src/librustc/middle/typeck/check/vtable.rs @@ -11,7 +11,7 @@ use core::prelude::*; use middle::resolve::Impl; -use middle::ty::{param_ty, substs}; +use middle::ty::{param_ty}; use middle::ty; use middle::typeck::check::{FnCtxt, impl_self_ty}; use middle::typeck::check::{structurally_resolved_type}; @@ -489,6 +489,8 @@ pub fn early_resolve_expr(ex: @ast::expr, match ex.node { ast::expr_path(*) => { for fcx.opt_node_ty_substs(ex.id) |substs| { + debug!("vtable resolution on parameter bounds for expr %s", + ex.repr(fcx.tcx())); let def = *cx.tcx.def_map.get(&ex.id); let did = ast_util::def_id_of_def(def); let item_ty = ty::lookup_item_type(cx.tcx, did); @@ -518,6 +520,8 @@ pub fn early_resolve_expr(ex: @ast::expr, ast::expr_index(*) | ast::expr_method_call(*) => { match ty::method_call_type_param_defs(cx.tcx, fcx.inh.method_map, ex.id) { Some(type_param_defs) => { + debug!("vtable resolution on parameter bounds for method call %s", + ex.repr(fcx.tcx())); if has_trait_bounds(*type_param_defs) { let callee_id = match ex.node { ast::expr_field(_, _, _) => ex.id, @@ -537,6 +541,7 @@ pub fn early_resolve_expr(ex: @ast::expr, } } ast::expr_cast(src, _) => { + debug!("vtable resolution on expr %s", ex.repr(fcx.tcx())); let target_ty = fcx.expr_ty(ex); match ty::get(target_ty).sty { ty::ty_trait(target_def_id, ref target_substs, store) => { diff --git a/src/librustc/middle/typeck/coherence.rs b/src/librustc/middle/typeck/coherence.rs index 174a20dd7f4..247b8eae2a8 100644 --- a/src/librustc/middle/typeck/coherence.rs +++ b/src/librustc/middle/typeck/coherence.rs @@ -24,7 +24,7 @@ use metadata::cstore::{CStore, iter_crate_data}; use metadata::decoder::{dl_def, dl_field, dl_impl}; use middle::resolve::{Impl, MethodInfo}; use middle::ty::{ProvidedMethodSource, ProvidedMethodInfo, bound_copy, get}; -use middle::ty::{lookup_item_type, param_bounds, subst}; +use middle::ty::{lookup_item_type, subst}; use middle::ty::{substs, t, ty_bool, ty_bot, ty_box, ty_enum, ty_err}; use middle::ty::{ty_estr, ty_evec, ty_float, ty_infer, ty_int, ty_nil}; use middle::ty::{ty_opaque_box, ty_param, ty_param_bounds_and_ty, ty_ptr}; diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index 0ef6e2512f3..59ea8ea039e 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -55,7 +55,7 @@ use syntax::ast_util::{local_def, split_trait_methods}; use syntax::ast_util; use syntax::codemap::span; use syntax::codemap; -use syntax::print::pprust::path_to_str; +use syntax::print::pprust::{path_to_str, self_ty_to_str}; use syntax::visit; use syntax::opt_vec::OptVec; use syntax::opt_vec; @@ -453,31 +453,35 @@ pub fn compare_impl_method(tcx: ty::ctxt, let impl_m = &cm.mty; - // FIXME(#2687)---this check is too strict. For example, a trait - // method with self type `&self` or `&mut self` should be - // implementable by an `&const self` method (the impl assumes less - // than the trait provides). - if impl_m.self_ty != trait_m.self_ty { - if impl_m.self_ty == ast::sty_static { - // Needs to be a fatal error because otherwise, - // method::transform_self_type_for_method ICEs - tcx.sess.span_fatal(cm.span, - fmt!("method `%s` is declared as \ - static in its impl, but not in \ - its trait", *tcx.sess.str_of(impl_m.ident))); - } - else if trait_m.self_ty == ast::sty_static { - tcx.sess.span_fatal(cm.span, - fmt!("method `%s` is declared as \ - static in its trait, but not in \ - its impl", *tcx.sess.str_of(impl_m.ident))); - } - else { + // Try to give more informative error messages about self typing + // mismatches. Note that any mismatch will also be detected + // below, where we construct a canonical function type that + // includes the self parameter as a normal parameter. It's just + // that the error messages you get out of this code are a bit more + // inscrutable, particularly for cases where one method has no + // self. + match (&trait_m.self_ty, &impl_m.self_ty) { + (&ast::sty_static, &ast::sty_static) => {} + (&ast::sty_static, _) => { tcx.sess.span_err( cm.span, - fmt!("method `%s`'s self type does \ - not match the trait method's \ - self type", *tcx.sess.str_of(impl_m.ident))); + fmt!("method `%s` has a `%s` declaration in the impl, \ + but not in the trait", + *tcx.sess.str_of(trait_m.ident), + self_ty_to_str(impl_m.self_ty, tcx.sess.intr()))); + return; + } + (_, &ast::sty_static) => { + tcx.sess.span_err( + cm.span, + fmt!("method `%s` has a `%s` declaration in the trait, \ + but not in the impl", + *tcx.sess.str_of(trait_m.ident), + self_ty_to_str(trait_m.self_ty, tcx.sess.intr()))); + return; + } + _ => { + // Let the type checker catch other errors below } } @@ -539,6 +543,51 @@ pub fn compare_impl_method(tcx: ty::ctxt, bound_region: ty::br_self}); let self_ty = replace_bound_self(tcx, self_ty, dummy_self_r); + // We are going to create a synthetic fn type that includes + // both the method's self argument and its normal arguments. + // So a method like `fn(&self, a: uint)` would be converted + // into a function `fn(self: &T, a: uint)`. + let mut trait_fn_args = ~[]; + let mut impl_fn_args = ~[]; + + // For both the trait and the impl, create an argument to + // represent the self argument (unless this is a static method). + // This argument will have the *transformed* self type. + for trait_m.transformed_self_ty.each |&t| { + trait_fn_args.push(ty::arg {mode: ast::expl(ast::by_copy), ty: t}); + } + for impl_m.transformed_self_ty.each |&t| { + impl_fn_args.push(ty::arg {mode: ast::expl(ast::by_copy), ty: t}); + } + + // Add in the normal arguments. + trait_fn_args.push_all(trait_m.fty.sig.inputs); + impl_fn_args.push_all(impl_m.fty.sig.inputs); + + // Create a bare fn type for trait/impl that includes self argument + let trait_fty = + ty::mk_bare_fn( + tcx, + ty::BareFnTy {purity: trait_m.fty.purity, + abis: trait_m.fty.abis, + sig: ty::FnSig { + bound_lifetime_names: + copy trait_m.fty.sig.bound_lifetime_names, + inputs: trait_fn_args, + output: trait_m.fty.sig.output + }}); + let impl_fty = + ty::mk_bare_fn( + tcx, + ty::BareFnTy {purity: impl_m.fty.purity, + abis: impl_m.fty.abis, + sig: ty::FnSig { + bound_lifetime_names: + copy impl_m.fty.sig.bound_lifetime_names, + inputs: impl_fn_args, + output: impl_m.fty.sig.output + }}); + // Perform substitutions so that the trait/impl methods are expressed // in terms of the same set of type/region parameters: // - replace trait type parameters with those from `trait_substs`, @@ -547,7 +596,6 @@ pub fn compare_impl_method(tcx: ty::ctxt, // that correspond to the parameters we will find on the impl // - replace self region with a fresh, dummy region let impl_fty = { - let impl_fty = ty::mk_bare_fn(tcx, copy impl_m.fty); debug!("impl_fty (pre-subst): %s", ppaux::ty_to_str(tcx, impl_fty)); replace_bound_self(tcx, impl_fty, dummy_self_r) }; @@ -565,7 +613,6 @@ pub fn compare_impl_method(tcx: ty::ctxt, self_ty: Some(self_ty), tps: vec::append(trait_tps, dummy_tps) }; - let trait_fty = ty::mk_bare_fn(tcx, copy trait_m.fty); debug!("trait_fty (pre-subst): %s substs=%s", trait_fty.repr(tcx), substs.repr(tcx)); ty::subst(tcx, &substs, trait_fty) diff --git a/src/librustc/middle/typeck/rscope.rs b/src/librustc/middle/typeck/rscope.rs index eeca90dbecd..3ff36a409a7 100644 --- a/src/librustc/middle/typeck/rscope.rs +++ b/src/librustc/middle/typeck/rscope.rs @@ -180,12 +180,11 @@ impl region_scope for MethodRscope { }) } fn self_region(&self, _span: span) -> Result { - assert!(self.variance.is_some() || self.self_ty.is_borrowed()); + assert!(self.variance.is_some()); match self.variance { None => {} // must be borrowed self, so this is OK Some(_) => { - if !self.self_ty.is_borrowed() && - !self.region_param_names.has_self() { + if !self.region_param_names.has_self() { return Err(RegionError { msg: ~"the `self` lifetime must be declared", replacement: ty::re_bound(ty::br_self) diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index f8877f76881..9b9e0e81b43 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -750,6 +750,12 @@ impl Repr for ty::TraitStore { } } +impl Repr for ty::vstore { + fn repr(&self, tcx: ctxt) -> ~str { + vstore_to_str(tcx, *self) + } +} + // Local Variables: // mode: rust // fill-column: 78; diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index ec77b54a853..3b2df24e7d9 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1002,15 +1002,6 @@ pub enum self_ty_ { sty_uniq(mutability) // `~self` } -impl self_ty_ { - fn is_borrowed(&self) -> bool { - match *self { - sty_region(*) => true, - _ => false - } - } -} - pub type self_ty = spanned; #[auto_encode] diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 92f0c7c7679..886af694920 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -454,6 +454,7 @@ impl MapChain{ // ugh: can't get this to compile with mut because of the // lack of flow sensitivity. + #[cfg(stage0)] fn get_map(&self) -> &'self HashMap { match *self { BaseMapChain (~ref map) => map, @@ -461,6 +462,18 @@ impl MapChain{ } } + // ugh: can't get this to compile with mut because of the + // lack of flow sensitivity. + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn get_map<'a>(&'a self) -> &'a HashMap { + match *self { + BaseMapChain (~ref map) => map, + ConsMapChain (~ref map,_) => map + } + } + // traits just don't work anywhere...? //pub impl Map for MapChain { diff --git a/src/libsyntax/opt_vec.rs b/src/libsyntax/opt_vec.rs index fd54746f3dc..1604c40f917 100644 --- a/src/libsyntax/opt_vec.rs +++ b/src/libsyntax/opt_vec.rs @@ -61,6 +61,7 @@ impl OptVec { } } + #[cfg(stage0)] fn get(&self, i: uint) -> &'self T { match *self { Empty => fail!(fmt!("Invalid index %u", i)), @@ -68,6 +69,16 @@ impl OptVec { } } + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn get<'a>(&'a self, i: uint) -> &'a T { + match *self { + Empty => fail!(fmt!("Invalid index %u", i)), + Vec(ref v) => &v[i] + } + } + fn is_empty(&self) -> bool { self.len() == 0 } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 20fc99baf21..36cd7c06842 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1633,6 +1633,10 @@ pub fn print_pat(s: @ps, &&pat: @ast::pat, refutable: bool) { (s.ann.post)(ann_node); } +pub fn self_ty_to_str(self_ty: ast::self_ty_, intr: @ident_interner) -> ~str { + to_str(self_ty, |a, b| { print_self_ty(a, b); () }, intr) +} + // Returns whether it printed anything pub fn print_self_ty(s: @ps, self_ty: ast::self_ty_) -> bool { match self_ty { From 61b9e0ebfa7c96886c45a461c6d8edb22f8153da Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 10 Apr 2013 13:11:35 -0700 Subject: [PATCH 3/5] core: changes in response to #5656 --- src/libcore/condition.rs | 2 +- src/libcore/container.rs | 36 ++++++++ src/libcore/hashmap.rs | 173 +++++++++++++++++++++++++++++++++- src/libcore/option.rs | 104 +++++++++++++++++++++ src/libcore/result.rs | 7 ++ src/libcore/rt/rtio.rs | 5 + src/libcore/rt/sched.rs | 40 ++++++++ src/libcore/rt/uvio.rs | 16 ++++ src/libcore/task/mod.rs | 2 +- src/libcore/trie.rs | 97 ++++++++++++++++++- src/libcore/tuple.rs | 28 ++++++ src/libcore/vec.rs | 195 +++++++++++++++++++++++++++++++++++++++ 12 files changed, 700 insertions(+), 5 deletions(-) diff --git a/src/libcore/condition.rs b/src/libcore/condition.rs index ed94f2ef2c4..dc6c80228dd 100644 --- a/src/libcore/condition.rs +++ b/src/libcore/condition.rs @@ -28,7 +28,7 @@ pub struct Condition<'self, T, U> { } pub impl<'self, T, U> Condition<'self, T, U> { - fn trap(&self, h: &'self fn(T) -> U) -> Trap<'self, T, U> { + fn trap(&'self self, h: &'self fn(T) -> U) -> Trap<'self, T, U> { unsafe { let p : *RustClosure = ::cast::transmute(&h); let prev = task::local_data::local_data_get(self.key); diff --git a/src/libcore/container.rs b/src/libcore/container.rs index a1836d16fd7..88c78aebfc5 100644 --- a/src/libcore/container.rs +++ b/src/libcore/container.rs @@ -25,6 +25,7 @@ pub trait Mutable: Container { fn clear(&mut self); } +#[cfg(stage0)] pub trait Map: Mutable { /// Return true if the map contains a value for the specified key fn contains_key(&self, key: &K) -> bool; @@ -57,6 +58,41 @@ pub trait Map: Mutable { fn remove(&mut self, key: &K) -> bool; } +#[cfg(stage1)] +#[cfg(stage2)] +#[cfg(stage3)] +pub trait Map: Mutable { + /// Return true if the map contains a value for the specified key + fn contains_key(&self, key: &K) -> bool; + + // Visits all keys and values + fn each<'a>(&'a self, f: &fn(&K, &'a V) -> bool); + + /// Visit all keys + fn each_key(&self, f: &fn(&K) -> bool); + + /// Visit all values + fn each_value<'a>(&'a self, f: &fn(&'a V) -> bool); + + /// Iterate over the map and mutate the contained values + fn mutate_values(&mut self, f: &fn(&K, &mut V) -> bool); + + /// Return a reference to the value corresponding to the key + fn find<'a>(&'a self, key: &K) -> Option<&'a V>; + + /// Return a mutable reference to the value corresponding to the key + fn find_mut<'a>(&'a mut self, key: &K) -> Option<&'a mut V>; + + /// Insert a key-value pair into the map. An existing value for a + /// key is replaced by the new value. Return true if the key did + /// not already exist in the map. + fn insert(&mut self, key: K, value: V) -> bool; + + /// Remove a key-value pair from the map. Return true if the key + /// was present in the map, otherwise false. + fn remove(&mut self, key: &K) -> bool; +} + pub trait Set: Mutable { /// Return true if the set contains a value fn contains(&self, value: &T) -> bool; diff --git a/src/libcore/hashmap.rs b/src/libcore/hashmap.rs index d4af0ffe7fe..2869c198ca2 100644 --- a/src/libcore/hashmap.rs +++ b/src/libcore/hashmap.rs @@ -186,6 +186,7 @@ priv impl HashMap { } } + #[cfg(stage0)] #[inline(always)] fn value_for_bucket(&self, idx: uint) -> &'self V { match self.buckets[idx] { @@ -194,6 +195,18 @@ priv impl HashMap { } } + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + #[inline(always)] + fn value_for_bucket<'a>(&'a self, idx: uint) -> &'a V { + match self.buckets[idx] { + Some(ref bkt) => &bkt.value, + None => fail!(~"HashMap::find: internal logic error"), + } + } + + #[cfg(stage0)] #[inline(always)] fn mut_value_for_bucket(&mut self, idx: uint) -> &'self mut V { match self.buckets[idx] { @@ -202,6 +215,17 @@ priv impl HashMap { } } + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + #[inline(always)] + fn mut_value_for_bucket<'a>(&'a mut self, idx: uint) -> &'a mut V { + match self.buckets[idx] { + Some(ref mut bkt) => &mut bkt.value, + None => unreachable() + } + } + /// Inserts the key value pair into the buckets. /// Assumes that there will be a bucket. /// True if there was no previous entry with that key @@ -307,6 +331,7 @@ impl Map for HashMap { } /// Visit all key-value pairs + #[cfg(stage0)] fn each(&self, blk: &fn(&'self K, &'self V) -> bool) { for uint::range(0, self.buckets.len()) |i| { for self.buckets[i].each |bucket| { @@ -317,19 +342,41 @@ impl Map for HashMap { } } + /// Visit all key-value pairs + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn each<'a>(&'a self, blk: &fn(&'a K, &'a V) -> bool) { + for uint::range(0, self.buckets.len()) |i| { + for self.buckets[i].each |bucket| { + if !blk(&bucket.key, &bucket.value) { + return; + } + } + } + } + /// Visit all keys fn each_key(&self, blk: &fn(k: &K) -> bool) { self.each(|k, _| blk(k)) } /// Visit all values + #[cfg(stage0)] fn each_value(&self, blk: &fn(v: &V) -> bool) { self.each(|_, v| blk(v)) } + /// Visit all values + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn each_value<'a>(&'a self, blk: &fn(v: &'a V) -> bool) { + self.each(|_, v| blk(v)) + } + /// Iterate over the map and mutate the contained values - fn mutate_values(&mut self, blk: &fn(&'self K, - &'self mut V) -> bool) { + fn mutate_values(&mut self, blk: &fn(&K, &mut V) -> bool) { for uint::range(0, self.buckets.len()) |i| { match self.buckets[i] { Some(Bucket{key: ref key, value: ref mut value, _}) => { @@ -341,6 +388,7 @@ impl Map for HashMap { } /// Return a reference to the value corresponding to the key + #[cfg(stage0)] fn find(&self, k: &K) -> Option<&'self V> { match self.bucket_for_key(k) { FoundEntry(idx) => Some(self.value_for_bucket(idx)), @@ -348,7 +396,19 @@ impl Map for HashMap { } } + /// Return a reference to the value corresponding to the key + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn find<'a>(&'a self, k: &K) -> Option<&'a V> { + match self.bucket_for_key(k) { + FoundEntry(idx) => Some(self.value_for_bucket(idx)), + TableFull | FoundHole(_) => None, + } + } + /// Return a mutable reference to the value corresponding to the key + #[cfg(stage0)] fn find_mut(&mut self, k: &K) -> Option<&'self mut V> { let idx = match self.bucket_for_key(k) { FoundEntry(idx) => idx, @@ -359,6 +419,20 @@ impl Map for HashMap { } } + /// Return a mutable reference to the value corresponding to the key + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn find_mut<'a>(&'a mut self, k: &K) -> Option<&'a mut V> { + let idx = match self.bucket_for_key(k) { + FoundEntry(idx) => idx, + TableFull | FoundHole(_) => return None + }; + unsafe { // FIXME(#4903)---requires flow-sensitive borrow checker + Some(::cast::transmute_mut_region(self.mut_value_for_bucket(idx))) + } + } + /// Insert a key-value pair into the map. An existing value for a /// key is replaced by the new value. Return true if the key did /// not already exist in the map. @@ -431,6 +505,7 @@ pub impl HashMap { /// Return the value corresponding to the key in the map, or insert /// and return the value if it doesn't exist. + #[cfg(stage0)] fn find_or_insert(&mut self, k: K, v: V) -> &'self V { if self.size >= self.resize_at { // n.b.: We could also do this after searching, so @@ -459,8 +534,42 @@ pub impl HashMap { } } + /// Return the value corresponding to the key in the map, or insert + /// and return the value if it doesn't exist. + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn find_or_insert<'a>(&'a mut self, k: K, v: V) -> &'a V { + if self.size >= self.resize_at { + // n.b.: We could also do this after searching, so + // that we do not resize if this call to insert is + // simply going to update a key in place. My sense + // though is that it's worse to have to search through + // buckets to find the right spot twice than to just + // resize in this corner case. + self.expand(); + } + + let hash = k.hash_keyed(self.k0, self.k1) as uint; + let idx = match self.bucket_for_key_with_hash(hash, &k) { + TableFull => fail!(~"Internal logic error"), + FoundEntry(idx) => idx, + FoundHole(idx) => { + self.buckets[idx] = Some(Bucket{hash: hash, key: k, + value: v}); + self.size += 1; + idx + }, + }; + + unsafe { // FIXME(#4903)---requires flow-sensitive borrow checker + ::cast::transmute_region(self.value_for_bucket(idx)) + } + } + /// Return the value corresponding to the key in the map, or create, /// insert, and return a new value if it doesn't exist. + #[cfg(stage0)] fn find_or_insert_with(&mut self, k: K, f: &fn(&K) -> V) -> &'self V { if self.size >= self.resize_at { // n.b.: We could also do this after searching, so @@ -490,6 +599,40 @@ pub impl HashMap { } } + /// Return the value corresponding to the key in the map, or create, + /// insert, and return a new value if it doesn't exist. + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn find_or_insert_with<'a>(&'a mut self, k: K, f: &fn(&K) -> V) -> &'a V { + if self.size >= self.resize_at { + // n.b.: We could also do this after searching, so + // that we do not resize if this call to insert is + // simply going to update a key in place. My sense + // though is that it's worse to have to search through + // buckets to find the right spot twice than to just + // resize in this corner case. + self.expand(); + } + + let hash = k.hash_keyed(self.k0, self.k1) as uint; + let idx = match self.bucket_for_key_with_hash(hash, &k) { + TableFull => fail!(~"Internal logic error"), + FoundEntry(idx) => idx, + FoundHole(idx) => { + let v = f(&k); + self.buckets[idx] = Some(Bucket{hash: hash, key: k, + value: v}); + self.size += 1; + idx + }, + }; + + unsafe { // FIXME(#4903)---requires flow-sensitive borrow checker + ::cast::transmute_region(self.value_for_bucket(idx)) + } + } + fn consume(&mut self, f: &fn(K, V)) { let mut buckets = ~[]; self.buckets <-> buckets; @@ -506,6 +649,7 @@ pub impl HashMap { } } + #[cfg(stage0)] fn get(&self, k: &K) -> &'self V { match self.find(k) { Some(v) => v, @@ -513,6 +657,16 @@ pub impl HashMap { } } + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn get<'a>(&'a self, k: &K) -> &'a V { + match self.find(k) { + Some(v) => v, + None => fail!(fmt!("No entry found for key: %?", k)), + } + } + /// Return true if the map contains a value for the specified key, /// using equivalence fn contains_key_equiv>(&self, key: &Q) @@ -525,6 +679,7 @@ pub impl HashMap { /// Return the value corresponding to the key in the map, using /// equivalence + #[cfg(stage0)] fn find_equiv>(&self, k: &Q) -> Option<&'self V> { match self.bucket_for_key_equiv(k) { @@ -532,6 +687,20 @@ pub impl HashMap { TableFull | FoundHole(_) => None, } } + + /// Return the value corresponding to the key in the map, using + /// equivalence + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn find_equiv<'a, Q:Hash + IterBytes + Equiv>( + &'a self, k: &Q) -> Option<&'a V> + { + match self.bucket_for_key_equiv(k) { + FoundEntry(idx) => Some(self.value_for_bucket(idx)), + TableFull | FoundHole(_) => None, + } + } } impl Eq for HashMap { diff --git a/src/libcore/option.rs b/src/libcore/option.rs index de1482e2c39..9b7276879c1 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -101,11 +101,21 @@ impl> Add, Option> for Option { impl BaseIter for Option { /// Performs an operation on the contained value by reference + #[cfg(stage0)] #[inline(always)] fn each(&self, f: &fn(x: &'self T) -> bool) { match *self { None => (), Some(ref t) => { f(t); } } } + /// Performs an operation on the contained value by reference + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + #[inline(always)] + fn each<'a>(&'a self, f: &fn(x: &'a T) -> bool) { + match *self { None => (), Some(ref t) => { f(t); } } + } + #[inline(always)] fn size_hint(&self) -> Option { if self.is_some() { Some(1) } else { Some(0) } @@ -113,10 +123,19 @@ impl BaseIter for Option { } impl MutableIter for Option { + #[cfg(stage0)] #[inline(always)] fn each_mut(&mut self, f: &fn(&'self mut T) -> bool) { match *self { None => (), Some(ref mut t) => { f(t); } } } + + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + #[inline(always)] + fn each_mut<'a>(&'a mut self, f: &fn(&'a mut T) -> bool) { + match *self { None => (), Some(ref mut t) => { f(t); } } + } } impl ExtendedIter for Option { @@ -182,17 +201,40 @@ pub impl Option { * Update an optional value by optionally running its content by reference * through a function that returns an option. */ + #[cfg(stage0)] #[inline(always)] fn chain_ref(&self, f: &fn(x: &'self T) -> Option) -> Option { match *self { Some(ref x) => f(x), None => None } } + /** + * Update an optional value by optionally running its content by reference + * through a function that returns an option. + */ + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + #[inline(always)] + fn chain_ref<'a, U>(&'a self, f: &fn(x: &'a T) -> Option) -> Option { + match *self { Some(ref x) => f(x), None => None } + } + /// Maps a `some` value from one type to another by reference + #[cfg(stage0)] #[inline(always)] fn map(&self, f: &fn(&'self T) -> U) -> Option { match *self { Some(ref x) => Some(f(x)), None => None } } + /// Maps a `some` value from one type to another by reference + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + #[inline(always)] + fn map<'a, U>(&self, f: &fn(&'a T) -> U) -> Option { + match *self { Some(ref x) => Some(f(x)), None => None } + } + /// As `map`, but consumes the option and gives `f` ownership to avoid /// copying. #[inline(always)] @@ -201,11 +243,21 @@ pub impl Option { } /// Applies a function to the contained value or returns a default + #[cfg(stage0)] #[inline(always)] fn map_default(&self, def: U, f: &fn(&'self T) -> U) -> U { match *self { None => def, Some(ref t) => f(t) } } + /// Applies a function to the contained value or returns a default + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + #[inline(always)] + fn map_default<'a, U>(&'a self, def: U, f: &fn(&'a T) -> U) -> U { + match *self { None => def, Some(ref t) => f(t) } + } + /// As `map_default`, but consumes the option and gives `f` /// ownership to avoid copying. #[inline(always)] @@ -244,6 +296,7 @@ pub impl Option { case explicitly. */ #[inline(always)] + #[cfg(stage0)] fn get_ref(&self) -> &'self T { match *self { Some(ref x) => x, @@ -251,6 +304,31 @@ pub impl Option { } } + /** + Gets an immutable reference to the value inside an option. + + # Failure + + Fails if the value equals `None` + + # Safety note + + In general, because this function may fail, its use is discouraged + (calling `get` on `None` is akin to dereferencing a null pointer). + Instead, prefer to use pattern matching and handle the `None` + case explicitly. + */ + #[inline(always)] + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn get_ref<'a>(&'a self) -> &'a T { + match *self { + Some(ref x) => x, + None => fail!(~"option::get_ref none") + } + } + /** Gets a mutable reference to the value inside an option. @@ -266,6 +344,7 @@ pub impl Option { case explicitly. */ #[inline(always)] + #[cfg(stage0)] fn get_mut_ref(&mut self) -> &'self mut T { match *self { Some(ref mut x) => x, @@ -273,6 +352,31 @@ pub impl Option { } } + /** + Gets a mutable reference to the value inside an option. + + # Failure + + Fails if the value equals `None` + + # Safety note + + In general, because this function may fail, its use is discouraged + (calling `get` on `None` is akin to dereferencing a null pointer). + Instead, prefer to use pattern matching and handle the `None` + case explicitly. + */ + #[inline(always)] + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn get_mut_ref<'a>(&'a mut self) -> &'a mut T { + match *self { + Some(ref mut x) => x, + None => fail!(~"option::get_mut_ref none") + } + } + #[inline(always)] fn unwrap(self) -> T { /*! diff --git a/src/libcore/result.rs b/src/libcore/result.rs index 8fd81a20603..58e281c29c6 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -226,9 +226,16 @@ pub fn map_err(res: &Result, op: &fn(&E) -> F) } pub impl Result { + #[cfg(stage0)] #[inline(always)] fn get_ref(&self) -> &'self T { get_ref(self) } + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + #[inline(always)] + fn get_ref<'a>(&'a self) -> &'a T { get_ref(self) } + #[inline(always)] fn is_ok(&self) -> bool { is_ok(self) } diff --git a/src/libcore/rt/rtio.rs b/src/libcore/rt/rtio.rs index 55e062de85b..6a7c3970c00 100644 --- a/src/libcore/rt/rtio.rs +++ b/src/libcore/rt/rtio.rs @@ -22,7 +22,12 @@ pub trait EventLoop { fn run(&mut self); fn callback(&mut self, ~fn()); /// The asynchronous I/O services. Not all event loops may provide one + #[cfg(stage0)] fn io(&mut self) -> Option<&'self mut IoFactoryObject>; + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn io<'a>(&'a mut self) -> Option<&'a mut IoFactoryObject>; } pub trait IoFactory { diff --git a/src/libcore/rt/sched.rs b/src/libcore/rt/sched.rs index 46ea5713e2a..25f446fb86d 100644 --- a/src/libcore/rt/sched.rs +++ b/src/libcore/rt/sched.rs @@ -272,6 +272,7 @@ pub impl Scheduler { // XXX: Hack. This should return &'self mut but I don't know how to // make the borrowcheck happy + #[cfg(stage0)] fn task_from_last_cleanup_job(&mut self) -> &mut Task { assert!(!self.cleanup_jobs.is_empty()); let last_job: &'self mut CleanupJob = &mut self.cleanup_jobs[0]; @@ -285,6 +286,25 @@ pub impl Scheduler { // borrows return unsafe { transmute::<&Task, &mut Task>(last_task) }; } + + // XXX: Hack. This should return &'self mut but I don't know how to + // make the borrowcheck happy + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn task_from_last_cleanup_job<'a>(&'a mut self) -> &mut Task { + assert!(!self.cleanup_jobs.is_empty()); + let last_job: &'a mut CleanupJob = &mut self.cleanup_jobs[0]; + let last_task: &'a Task = match last_job { + &RescheduleTask(~ref task) => task, + &RecycleTask(~ref task) => task, + &GiveTask(~ref task, _) => task, + }; + // XXX: Pattern matching mutable pointers above doesn't work + // because borrowck thinks the three patterns are conflicting + // borrows + return unsafe { transmute::<&Task, &mut Task>(last_task) }; + } } static TASK_MIN_STACK_SIZE: uint = 10000000; // XXX: Too much stack @@ -354,6 +374,7 @@ impl ThreadLocalScheduler { } } + #[cfg(stage0)] fn get_scheduler(&mut self) -> &'self mut Scheduler { unsafe { let key = match self { &ThreadLocalScheduler(key) => key }; @@ -370,6 +391,25 @@ impl ThreadLocalScheduler { } } + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn get_scheduler<'a>(&'a mut self) -> &'a mut Scheduler { + unsafe { + let key = match self { &ThreadLocalScheduler(key) => key }; + let mut value: *mut c_void = tls::get(key); + assert!(value.is_not_null()); + { + let value_ptr = &mut value; + let sched: &mut ~Scheduler = { + transmute::<&mut *mut c_void, &mut ~Scheduler>(value_ptr) + }; + let sched: &mut Scheduler = &mut **sched; + return sched; + } + } + } + fn take_scheduler(&mut self) -> ~Scheduler { unsafe { let key = match self { &ThreadLocalScheduler(key) => key }; diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs index 37f29d1a5c0..7162ed27a9d 100644 --- a/src/libcore/rt/uvio.rs +++ b/src/libcore/rt/uvio.rs @@ -67,9 +67,17 @@ impl EventLoop for UvEventLoop { } } + #[cfg(stage0)] fn io(&mut self) -> Option<&'self mut IoFactoryObject> { Some(&mut self.uvio) } + + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn io<'a>(&'a mut self) -> Option<&'a mut IoFactoryObject> { + Some(&mut self.uvio) + } } #[test] @@ -89,9 +97,17 @@ fn test_callback_run_once() { pub struct UvIoFactory(Loop); pub impl UvIoFactory { + #[cfg(stage0)] fn uv_loop(&mut self) -> &'self mut Loop { match self { &UvIoFactory(ref mut ptr) => ptr } } + + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn uv_loop<'a>(&'a mut self) -> &'a mut Loop { + match self { &UvIoFactory(ref mut ptr) => ptr } + } } impl IoFactory for UvIoFactory { diff --git a/src/libcore/task/mod.rs b/src/libcore/task/mod.rs index 502efcf9dc6..e4ee430cdda 100644 --- a/src/libcore/task/mod.rs +++ b/src/libcore/task/mod.rs @@ -39,7 +39,7 @@ use result::Result; use comm::{stream, Chan, GenericChan, GenericPort, Port}; use prelude::*; use result; -use task::rt::{task_id, sched_id, rust_task}; +use task::rt::{task_id, sched_id}; use util; use util::replace; use unstable::finally::Finally; diff --git a/src/libcore/trie.rs b/src/libcore/trie.rs index f6a92a21385..f4e9ddbdd90 100644 --- a/src/libcore/trie.rs +++ b/src/libcore/trie.rs @@ -56,10 +56,20 @@ impl Map for TrieMap { /// Visit all key-value pairs in order #[inline(always)] + #[cfg(stage0)] fn each(&self, f: &fn(&uint, &'self T) -> bool) { self.root.each(f); } + /// Visit all key-value pairs in order + #[inline(always)] + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn each<'a>(&'a self, f: &fn(&uint, &'a T) -> bool) { + self.root.each(f); + } + /// Visit all keys in order #[inline(always)] fn each_key(&self, f: &fn(&uint) -> bool) { @@ -68,10 +78,20 @@ impl Map for TrieMap { /// Visit all values in order #[inline(always)] + #[cfg(stage0)] fn each_value(&self, f: &fn(&T) -> bool) { self.each(|_, v| f(v)) } + /// Visit all values in order + #[inline(always)] + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn each_value<'a>(&'a self, f: &fn(&'a T) -> bool) { + self.each(|_, v| f(v)) + } + /// Iterate over the map and mutate the contained values #[inline(always)] fn mutate_values(&mut self, f: &fn(&uint, &mut T) -> bool) { @@ -79,6 +99,7 @@ impl Map for TrieMap { } /// Return a reference to the value corresponding to the key + #[cfg(stage0)] #[inline(hint)] fn find(&self, key: &uint) -> Option<&'self T> { let mut node: &'self TrieNode = &self.root; @@ -99,12 +120,46 @@ impl Map for TrieMap { } } + /// Return a reference to the value corresponding to the key + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + #[inline(hint)] + fn find<'a>(&'a self, key: &uint) -> Option<&'a T> { + let mut node: &'a TrieNode = &self.root; + let mut idx = 0; + loop { + match node.children[chunk(*key, idx)] { + Internal(ref x) => node = &**x, + External(stored, ref value) => { + if stored == *key { + return Some(value) + } else { + return None + } + } + Nothing => return None + } + idx += 1; + } + } + /// Return a mutable reference to the value corresponding to the key + #[cfg(stage0)] #[inline(always)] fn find_mut(&mut self, key: &uint) -> Option<&'self mut T> { find_mut(&mut self.root.children[chunk(*key, 0)], *key, 1) } + /// Return a mutable reference to the value corresponding to the key + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + #[inline(always)] + fn find_mut<'a>(&'a mut self, key: &uint) -> Option<&'a mut T> { + find_mut(&mut self.root.children[chunk(*key, 0)], *key, 1) + } + /// Insert a key-value pair into the map. An existing value for a /// key is replaced by the new value. Return true if the key did /// not already exist in the map. @@ -138,10 +193,20 @@ pub impl TrieMap { /// Visit all key-value pairs in reverse order #[inline(always)] + #[cfg(stage0)] fn each_reverse(&self, f: &fn(&uint, &'self T) -> bool) { self.root.each_reverse(f); } + /// Visit all key-value pairs in reverse order + #[inline(always)] + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn each_reverse<'a>(&'a self, f: &fn(&uint, &'a T) -> bool) { + self.root.each_reverse(f); + } + /// Visit all keys in reverse order #[inline(always)] fn each_key_reverse(&self, f: &fn(&uint) -> bool) { @@ -233,6 +298,7 @@ impl TrieNode { } impl TrieNode { + #[cfg(stage0)] fn each(&self, f: &fn(&uint, &'self T) -> bool) -> bool { for uint::range(0, self.children.len()) |idx| { match self.children[idx] { @@ -244,6 +310,21 @@ impl TrieNode { true } + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn each<'a>(&'a self, f: &fn(&uint, &'a T) -> bool) -> bool { + for uint::range(0, self.children.len()) |idx| { + match self.children[idx] { + Internal(ref x) => if !x.each(f) { return false }, + External(k, ref v) => if !f(&k, v) { return false }, + Nothing => () + } + } + true + } + + #[cfg(stage0)] fn each_reverse(&self, f: &fn(&uint, &'self T) -> bool) -> bool { for uint::range_rev(self.children.len(), 0) |idx| { match self.children[idx - 1] { @@ -255,7 +336,21 @@ impl TrieNode { true } - fn mutate_values(&mut self, f: &fn(&uint, &mut T) -> bool) -> bool { + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn each_reverse<'a>(&'a self, f: &fn(&uint, &'a T) -> bool) -> bool { + for uint::range_rev(self.children.len(), 0) |idx| { + match self.children[idx - 1] { + Internal(ref x) => if !x.each_reverse(f) { return false }, + External(k, ref v) => if !f(&k, v) { return false }, + Nothing => () + } + } + true + } + + fn mutate_values<'a>(&'a mut self, f: &fn(&uint, &mut T) -> bool) -> bool { for vec::each_mut(self.children) |child| { match *child { Internal(ref mut x) => if !x.mutate_values(f) { diff --git a/src/libcore/tuple.rs b/src/libcore/tuple.rs index 8234129e254..8e908435f35 100644 --- a/src/libcore/tuple.rs +++ b/src/libcore/tuple.rs @@ -56,11 +56,13 @@ impl Clone for (T, U) { } } +#[cfg(stage0)] pub trait ImmutableTuple { fn first_ref(&self) -> &'self T; fn second_ref(&self) -> &'self U; } +#[cfg(stage0)] impl ImmutableTuple for (T, U) { #[inline(always)] fn first_ref(&self) -> &'self T { @@ -76,6 +78,32 @@ impl ImmutableTuple for (T, U) { } } +#[cfg(stage1)] +#[cfg(stage2)] +#[cfg(stage3)] +pub trait ImmutableTuple { + fn first_ref<'a>(&'a self) -> &'a T; + fn second_ref<'a>(&'a self) -> &'a U; +} + +#[cfg(stage1)] +#[cfg(stage2)] +#[cfg(stage3)] +impl ImmutableTuple for (T, U) { + #[inline(always)] + fn first_ref<'a>(&'a self) -> &'a T { + match *self { + (ref t, _) => t, + } + } + #[inline(always)] + fn second_ref<'a>(&'a self) -> &'a U { + match *self { + (_, ref u) => u, + } + } +} + pub trait ExtendedTupleOps { fn zip(&self) -> ~[(A, B)]; fn map(&self, f: &fn(a: &A, b: &B) -> C) -> ~[C]; diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs index 5b06591f9ec..7940502d27e 100644 --- a/src/libcore/vec.rs +++ b/src/libcore/vec.rs @@ -1763,6 +1763,7 @@ impl<'self,T:Copy> CopyableVector for &'self const [T] { } } +#[cfg(stage0)] pub trait ImmutableVector { fn slice(&self, start: uint, end: uint) -> &'self [T]; fn head(&self) -> &'self T; @@ -1785,6 +1786,7 @@ pub trait ImmutableVector { } /// Extension methods for vectors +#[cfg(stage0)] impl<'self,T> ImmutableVector for &'self [T] { /// Return a slice that points into another slice. #[inline] @@ -1893,6 +1895,142 @@ impl<'self,T> ImmutableVector for &'self [T] { } } +#[cfg(stage1)] +#[cfg(stage2)] +#[cfg(stage3)] +pub trait ImmutableVector<'self, T> { + fn slice(&self, start: uint, end: uint) -> &'self [T]; + fn head(&self) -> &'self T; + fn head_opt(&self) -> Option<&'self T>; + fn tail(&self) -> &'self [T]; + fn tailn(&self, n: uint) -> &'self [T]; + fn init(&self) -> &'self [T]; + fn initn(&self, n: uint) -> &'self [T]; + fn last(&self) -> &'self T; + fn last_opt(&self) -> Option<&'self T>; + fn each_reverse(&self, blk: &fn(&T) -> bool); + fn eachi_reverse(&self, blk: &fn(uint, &T) -> bool); + fn foldr(&self, z: U, p: &fn(t: &T, u: U) -> U) -> U; + fn map(&self, f: &fn(t: &T) -> U) -> ~[U]; + fn mapi(&self, f: &fn(uint, t: &T) -> U) -> ~[U]; + fn map_r(&self, f: &fn(x: &T) -> U) -> ~[U]; + fn alli(&self, f: &fn(uint, t: &T) -> bool) -> bool; + fn flat_map(&self, f: &fn(t: &T) -> ~[U]) -> ~[U]; + fn filter_mapped(&self, f: &fn(t: &T) -> Option) -> ~[U]; +} + +/// Extension methods for vectors +#[cfg(stage1)] +#[cfg(stage2)] +#[cfg(stage3)] +impl<'self,T> ImmutableVector<'self, T> for &'self [T] { + /// Return a slice that points into another slice. + #[inline] + fn slice(&self, start: uint, end: uint) -> &'self [T] { + slice(*self, start, end) + } + + /// Returns the first element of a vector, failing if the vector is empty. + #[inline] + fn head(&self) -> &'self T { head(*self) } + + /// Returns the first element of a vector + #[inline] + fn head_opt(&self) -> Option<&'self T> { head_opt(*self) } + + /// Returns all but the first element of a vector + #[inline] + fn tail(&self) -> &'self [T] { tail(*self) } + + /// Returns all but the first `n' elements of a vector + #[inline] + fn tailn(&self, n: uint) -> &'self [T] { tailn(*self, n) } + + /// Returns all but the last elemnt of a vector + #[inline] + fn init(&self) -> &'self [T] { init(*self) } + + /// Returns all but the last `n' elemnts of a vector + #[inline] + fn initn(&self, n: uint) -> &'self [T] { initn(*self, n) } + + /// Returns the last element of a `v`, failing if the vector is empty. + #[inline] + fn last(&self) -> &'self T { last(*self) } + + /// Returns the last element of a `v`, failing if the vector is empty. + #[inline] + fn last_opt(&self) -> Option<&'self T> { last_opt(*self) } + + /// Iterates over a vector's elements in reverse. + #[inline] + fn each_reverse(&self, blk: &fn(&T) -> bool) { + each_reverse(*self, blk) + } + + /// Iterates over a vector's elements and indices in reverse. + #[inline] + fn eachi_reverse(&self, blk: &fn(uint, &T) -> bool) { + eachi_reverse(*self, blk) + } + + /// Reduce a vector from right to left + #[inline] + fn foldr(&self, z: U, p: &fn(t: &T, u: U) -> U) -> U { + foldr(*self, z, p) + } + + /// Apply a function to each element of a vector and return the results + #[inline] + fn map(&self, f: &fn(t: &T) -> U) -> ~[U] { map(*self, f) } + + /** + * Apply a function to the index and value of each element in the vector + * and return the results + */ + fn mapi(&self, f: &fn(uint, t: &T) -> U) -> ~[U] { + mapi(*self, f) + } + + #[inline] + fn map_r(&self, f: &fn(x: &T) -> U) -> ~[U] { + let mut r = ~[]; + let mut i = 0; + while i < self.len() { + r.push(f(&self[i])); + i += 1; + } + r + } + + /** + * Returns true if the function returns true for all elements. + * + * If the vector is empty, true is returned. + */ + fn alli(&self, f: &fn(uint, t: &T) -> bool) -> bool { + alli(*self, f) + } + /** + * Apply a function to each element of a vector and return a concatenation + * of each result vector + */ + #[inline] + fn flat_map(&self, f: &fn(t: &T) -> ~[U]) -> ~[U] { + flat_map(*self, f) + } + /** + * Apply a function to each element of a vector and return the results + * + * If function `f` returns `none` then that element is excluded from + * the resulting vector. + */ + #[inline] + fn filter_mapped(&self, f: &fn(t: &T) -> Option) -> ~[U] { + filter_mapped(*self, f) + } +} + pub trait ImmutableEqVector { fn position(&self, f: &fn(t: &T) -> bool) -> Option; fn position_elem(&self, t: &T) -> Option; @@ -2353,6 +2491,7 @@ pub mod bytes { // ___________________________________________________________________________ // ITERATION TRAIT METHODS +#[cfg(stage0)] impl<'self,A> iter::BaseIter for &'self [A] { #[inline(always)] fn each(&self, blk: &fn(v: &'self A) -> bool) { each(*self, blk) } @@ -2360,7 +2499,18 @@ impl<'self,A> iter::BaseIter for &'self [A] { fn size_hint(&self) -> Option { Some(self.len()) } } +#[cfg(stage1)] +#[cfg(stage2)] +#[cfg(stage3)] +impl<'self,A> iter::BaseIter for &'self [A] { + #[inline(always)] + fn each<'a>(&'a self, blk: &fn(v: &'a A) -> bool) { each(*self, blk) } + #[inline(always)] + fn size_hint(&self) -> Option { Some(self.len()) } +} + // FIXME(#4148): This should be redundant +#[cfg(stage0)] impl iter::BaseIter for ~[A] { #[inline(always)] fn each(&self, blk: &fn(v: &'self A) -> bool) { each(*self, blk) } @@ -2369,6 +2519,18 @@ impl iter::BaseIter for ~[A] { } // FIXME(#4148): This should be redundant +#[cfg(stage1)] +#[cfg(stage2)] +#[cfg(stage3)] +impl iter::BaseIter for ~[A] { + #[inline(always)] + fn each<'a>(&'a self, blk: &fn(v: &'a A) -> bool) { each(*self, blk) } + #[inline(always)] + fn size_hint(&self) -> Option { Some(self.len()) } +} + +// FIXME(#4148): This should be redundant +#[cfg(stage0)] impl iter::BaseIter for @[A] { #[inline(always)] fn each(&self, blk: &fn(v: &'self A) -> bool) { each(*self, blk) } @@ -2376,6 +2538,18 @@ impl iter::BaseIter for @[A] { fn size_hint(&self) -> Option { Some(self.len()) } } +// FIXME(#4148): This should be redundant +#[cfg(stage1)] +#[cfg(stage2)] +#[cfg(stage3)] +impl iter::BaseIter for @[A] { + #[inline(always)] + fn each<'a>(&'a self, blk: &fn(v: &'a A) -> bool) { each(*self, blk) } + #[inline(always)] + fn size_hint(&self) -> Option { Some(self.len()) } +} + +#[cfg(stage0)] impl<'self,A> iter::MutableIter for &'self mut [A] { #[inline(always)] fn each_mut(&mut self, blk: &fn(v: &'self mut A) -> bool) { @@ -2383,7 +2557,18 @@ impl<'self,A> iter::MutableIter for &'self mut [A] { } } +#[cfg(stage1)] +#[cfg(stage2)] +#[cfg(stage3)] +impl<'self,A> iter::MutableIter for &'self mut [A] { + #[inline(always)] + fn each_mut<'a>(&'a mut self, blk: &fn(v: &'a mut A) -> bool) { + each_mut(*self, blk) + } +} + // FIXME(#4148): This should be redundant +#[cfg(stage0)] impl iter::MutableIter for ~[A] { #[inline(always)] fn each_mut(&mut self, blk: &fn(v: &'self mut A) -> bool) { @@ -2391,6 +2576,16 @@ impl iter::MutableIter for ~[A] { } } +#[cfg(stage1)] +#[cfg(stage2)] +#[cfg(stage3)] +impl iter::MutableIter for ~[A] { + #[inline(always)] + fn each_mut<'a>(&'a mut self, blk: &fn(v: &'a mut A) -> bool) { + each_mut(*self, blk) + } +} + // FIXME(#4148): This should be redundant impl iter::MutableIter for @mut [A] { #[inline(always)] From 03396473b879b37d68f26588d136c840280b0ab5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 10 Apr 2013 13:14:06 -0700 Subject: [PATCH 4/5] libstd: changes to in response to #5656 --- src/libstd/arena.rs | 68 +++++++++++++++++-- src/libstd/bitv.rs | 6 +- src/libstd/deque.rs | 122 +++++++++++++++++++++++++++++++++++ src/libstd/future.rs | 30 ++++++++- src/libstd/priority_queue.rs | 16 +++++ src/libstd/smallintmap.rs | 79 ++++++++++++++++++++++- src/libstd/treemap.rs | 83 +++++++++++++++++++++++- 7 files changed, 390 insertions(+), 14 deletions(-) diff --git a/src/libstd/arena.rs b/src/libstd/arena.rs index 81c28f94d9f..3d2c3ac70b6 100644 --- a/src/libstd/arena.rs +++ b/src/libstd/arena.rs @@ -171,7 +171,7 @@ unsafe fn un_bitpack_tydesc_ptr(p: uint) -> (*TypeDesc, bool) { pub impl Arena { // Functions for the POD part of the arena - fn alloc_pod_grow(&self, n_bytes: uint, align: uint) -> *u8 { + priv fn alloc_pod_grow(&self, n_bytes: uint, align: uint) -> *u8 { // Allocate a new chunk. let chunk_size = at_vec::capacity(self.pod_head.data); let new_min_chunk_size = uint::max(n_bytes, chunk_size); @@ -183,7 +183,7 @@ pub impl Arena { } #[inline(always)] - fn alloc_pod_inner(&self, n_bytes: uint, align: uint) -> *u8 { + priv fn alloc_pod_inner(&self, n_bytes: uint, align: uint) -> *u8 { let head = &mut self.pod_head; let start = round_up_to(head.fill, align); @@ -202,7 +202,22 @@ pub impl Arena { } #[inline(always)] - fn alloc_pod(&self, op: &fn() -> T) -> &'self T { + #[cfg(stage0)] + priv fn alloc_pod(&self, op: &fn() -> T) -> &'self T { + unsafe { + let tydesc = sys::get_type_desc::(); + let ptr = self.alloc_pod_inner((*tydesc).size, (*tydesc).align); + let ptr: *mut T = reinterpret_cast(&ptr); + rusti::move_val_init(&mut (*ptr), op()); + return reinterpret_cast(&ptr); + } + } + + #[inline(always)] + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + priv fn alloc_pod<'a, T>(&'a self, op: &fn() -> T) -> &'a T { unsafe { let tydesc = sys::get_type_desc::(); let ptr = self.alloc_pod_inner((*tydesc).size, (*tydesc).align); @@ -213,7 +228,7 @@ pub impl Arena { } // Functions for the non-POD part of the arena - fn alloc_nonpod_grow(&self, n_bytes: uint, align: uint) -> (*u8, *u8) { + priv fn alloc_nonpod_grow(&self, n_bytes: uint, align: uint) -> (*u8, *u8) { // Allocate a new chunk. let chunk_size = at_vec::capacity(self.head.data); let new_min_chunk_size = uint::max(n_bytes, chunk_size); @@ -225,7 +240,7 @@ pub impl Arena { } #[inline(always)] - fn alloc_nonpod_inner(&self, n_bytes: uint, align: uint) -> (*u8, *u8) { + priv fn alloc_nonpod_inner(&self, n_bytes: uint, align: uint) -> (*u8, *u8) { let head = &mut self.head; let tydesc_start = head.fill; @@ -247,7 +262,32 @@ pub impl Arena { } #[inline(always)] - fn alloc_nonpod(&self, op: &fn() -> T) -> &'self T { + #[cfg(stage0)] + priv fn alloc_nonpod(&self, op: &fn() -> T) -> &'self T { + unsafe { + let tydesc = sys::get_type_desc::(); + let (ty_ptr, ptr) = + self.alloc_nonpod_inner((*tydesc).size, (*tydesc).align); + let ty_ptr: *mut uint = reinterpret_cast(&ty_ptr); + let ptr: *mut T = reinterpret_cast(&ptr); + // Write in our tydesc along with a bit indicating that it + // has *not* been initialized yet. + *ty_ptr = reinterpret_cast(&tydesc); + // Actually initialize it + rusti::move_val_init(&mut(*ptr), op()); + // Now that we are done, update the tydesc to indicate that + // the object is there. + *ty_ptr = bitpack_tydesc_ptr(tydesc, true); + + return reinterpret_cast(&ptr); + } + } + + #[inline(always)] + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + priv fn alloc_nonpod<'a, T>(&'a self, op: &fn() -> T) -> &'a T { unsafe { let tydesc = sys::get_type_desc::(); let (ty_ptr, ptr) = @@ -269,6 +309,7 @@ pub impl Arena { // The external interface #[inline(always)] + #[cfg(stage0)] fn alloc(&self, op: &fn() -> T) -> &'self T { unsafe { if !rusti::needs_drop::() { @@ -278,6 +319,21 @@ pub impl Arena { } } } + + // The external interface + #[inline(always)] + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn alloc<'a, T>(&'a self, op: &fn() -> T) -> &'a T { + unsafe { + if !rusti::needs_drop::() { + self.alloc_pod(op) + } else { + self.alloc_nonpod(op) + } + } + } } #[test] diff --git a/src/libstd/bitv.rs b/src/libstd/bitv.rs index f69e2130e71..632a38e8ca2 100644 --- a/src/libstd/bitv.rs +++ b/src/libstd/bitv.rs @@ -437,8 +437,7 @@ pub impl Bitv { if offset >= bitv.nbits { 0 } else { - // NOTE cannot use bitv[offset] until snapshot - bitv.index(&offset) as u8 << (7 - bit) + bitv[offset] as u8 << (7 - bit) } } @@ -460,8 +459,7 @@ pub impl Bitv { * Transform self into a [bool] by turning each bit into a bool */ fn to_bools(&self) -> ~[bool] { - // NOTE cannot use self[i] until snapshot - vec::from_fn(self.nbits, |i| self.index(&i)) + vec::from_fn(self.nbits, |i| self[i]) } /** diff --git a/src/libstd/deque.rs b/src/libstd/deque.rs index e7ec86963ee..a88d13fda66 100644 --- a/src/libstd/deque.rs +++ b/src/libstd/deque.rs @@ -41,6 +41,7 @@ impl Mutable for Deque { } } +#[cfg(stage0)] pub impl Deque { /// Create an empty Deque fn new() -> Deque { @@ -51,21 +52,142 @@ pub impl Deque { /// Return a reference to the first element in the deque /// /// Fails if the deque is empty + #[cfg(stage0)] fn peek_front(&self) -> &'self T { get(self.elts, self.lo) } + /// Return a reference to the first element in the deque + /// + /// Fails if the deque is empty + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn peek_front<'a>(&'a self) -> &'a T { get(self.elts, self.lo) } + /// Return a reference to the last element in the deque /// /// Fails if the deque is empty + #[cfg(stage0)] fn peek_back(&self) -> &'self T { get(self.elts, self.hi - 1u) } + /// Return a reference to the last element in the deque + /// + /// Fails if the deque is empty + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn peek_back<'a>(&'a self) -> &'a T { get(self.elts, self.hi - 1u) } + /// Retrieve an element in the deque by index /// /// Fails if there is no element with the given index + #[cfg(stage0)] fn get(&self, i: int) -> &'self T { let idx = (self.lo + (i as uint)) % self.elts.len(); get(self.elts, idx) } + /// Retrieve an element in the deque by index + /// + /// Fails if there is no element with the given index + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn get<'a>(&'a self, i: int) -> &'a T { + let idx = (self.lo + (i as uint)) % self.elts.len(); + get(self.elts, idx) + } + + /// Iterate over the elements in the deque + fn each(&self, f: &fn(&T) -> bool) { + self.eachi(|_i, e| f(e)) + } + + /// Iterate over the elements in the deque by index + fn eachi(&self, f: &fn(uint, &T) -> bool) { + for uint::range(0, self.nelts) |i| { + if !f(i, self.get(i as int)) { return; } + } + } + + /// Remove and return the first element in the deque + /// + /// Fails if the deque is empty + fn pop_front(&mut self) -> T { + let mut result = self.elts[self.lo].swap_unwrap(); + self.lo = (self.lo + 1u) % self.elts.len(); + self.nelts -= 1u; + result + } + + /// Remove and return the last element in the deque + /// + /// Fails if the deque is empty + fn pop_back(&mut self) -> T { + if self.hi == 0u { + self.hi = self.elts.len() - 1u; + } else { self.hi -= 1u; } + let mut result = self.elts[self.hi].swap_unwrap(); + self.elts[self.hi] = None; + self.nelts -= 1u; + result + } + + /// Prepend an element to the deque + fn add_front(&mut self, t: T) { + let oldlo = self.lo; + if self.lo == 0u { + self.lo = self.elts.len() - 1u; + } else { self.lo -= 1u; } + if self.lo == self.hi { + self.elts = grow(self.nelts, oldlo, self.elts); + self.lo = self.elts.len() - 1u; + self.hi = self.nelts; + } + self.elts[self.lo] = Some(t); + self.nelts += 1u; + } + + /// Append an element to the deque + fn add_back(&mut self, t: T) { + if self.lo == self.hi && self.nelts != 0u { + self.elts = grow(self.nelts, self.lo, self.elts); + self.lo = 0u; + self.hi = self.nelts; + } + self.elts[self.hi] = Some(t); + self.hi = (self.hi + 1u) % self.elts.len(); + self.nelts += 1u; + } +} + +#[cfg(stage1)] +#[cfg(stage2)] +#[cfg(stage3)] +pub impl Deque { + /// Create an empty Deque + fn new() -> Deque { + Deque{nelts: 0, lo: 0, hi: 0, + elts: vec::from_fn(initial_capacity, |_| None)} + } + + /// Return a reference to the first element in the deque + /// + /// Fails if the deque is empty + fn peek_front<'a>(&'a self) -> &'a T { get(self.elts, self.lo) } + + /// Return a reference to the last element in the deque + /// + /// Fails if the deque is empty + fn peek_back<'a>(&'a self) -> &'a T { get(self.elts, self.hi - 1u) } + + /// Retrieve an element in the deque by index + /// + /// Fails if there is no element with the given index + fn get<'a>(&'a self, i: int) -> &'a T { + let idx = (self.lo + (i as uint)) % self.elts.len(); + get(self.elts, idx) + } + /// Iterate over the elements in the deque fn each(&self, f: &fn(&T) -> bool) { self.eachi(|_i, e| f(e)) diff --git a/src/libstd/future.rs b/src/libstd/future.rs index a4887306d2a..feea8fb4fcd 100644 --- a/src/libstd/future.rs +++ b/src/libstd/future.rs @@ -55,7 +55,7 @@ pub impl Future { } pub impl Future { - + #[cfg(stage0)] fn get_ref(&self) -> &'self A { /*! * Executes the future's closure and then returns a borrowed @@ -80,6 +80,34 @@ pub impl Future { } } } + + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn get_ref<'a>(&'a self) -> &'a A { + /*! + * Executes the future's closure and then returns a borrowed + * pointer to the result. The borrowed pointer lasts as long as + * the future. + */ + unsafe { + match self.state { + Forced(ref mut v) => { return cast::transmute(v); } + Evaluating => fail!(~"Recursive forcing of future!"), + Pending(_) => {} + } + + let mut state = Evaluating; + self.state <-> state; + match state { + Forced(_) | Evaluating => fail!(~"Logic error."), + Pending(f) => { + self.state = Forced(f()); + self.get_ref() + } + } + } + } } pub fn from_value(val: A) -> Future { diff --git a/src/libstd/priority_queue.rs b/src/libstd/priority_queue.rs index dd56e413595..c8d250f90f6 100644 --- a/src/libstd/priority_queue.rs +++ b/src/libstd/priority_queue.rs @@ -50,13 +50,29 @@ impl Mutable for PriorityQueue { pub impl PriorityQueue { /// Returns the greatest item in the queue - fails if empty + #[cfg(stage0)] fn top(&self) -> &'self T { &self.data[0] } + /// Returns the greatest item in the queue - fails if empty + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn top<'a>(&'a self) -> &'a T { &self.data[0] } + /// Returns the greatest item in the queue - None if empty + #[cfg(stage0)] fn maybe_top(&self) -> Option<&'self T> { if self.is_empty() { None } else { Some(self.top()) } } + /// Returns the greatest item in the queue - None if empty + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn maybe_top<'a>(&'a self) -> Option<&'a T> { + if self.is_empty() { None } else { Some(self.top()) } + } + /// Returns the number of elements the queue can hold without reallocating fn capacity(&self) -> uint { vec::capacity(&self.data) } diff --git a/src/libstd/smallintmap.rs b/src/libstd/smallintmap.rs index 811cd710a62..d50804ba47b 100644 --- a/src/libstd/smallintmap.rs +++ b/src/libstd/smallintmap.rs @@ -51,6 +51,7 @@ impl Map for SmallIntMap { } /// Visit all key-value pairs in order + #[cfg(stage0)] fn each(&self, it: &fn(&uint, &'self V) -> bool) { for uint::range(0, self.v.len()) |i| { match self.v[i] { @@ -60,18 +61,40 @@ impl Map for SmallIntMap { } } + /// Visit all key-value pairs in order + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn each<'a>(&'a self, it: &fn(&uint, &'a V) -> bool) { + for uint::range(0, self.v.len()) |i| { + match self.v[i] { + Some(ref elt) => if !it(&i, elt) { break }, + None => () + } + } + } + /// Visit all keys in order fn each_key(&self, blk: &fn(key: &uint) -> bool) { self.each(|k, _| blk(k)) } /// Visit all values in order + #[cfg(stage0)] fn each_value(&self, blk: &fn(value: &V) -> bool) { self.each(|_, v| blk(v)) } + /// Visit all values in order + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn each_value<'a>(&'a self, blk: &fn(value: &'a V) -> bool) { + self.each(|_, v| blk(v)) + } + /// Iterate over the map and mutate the contained values - fn mutate_values(&mut self, it: &fn(&uint, &'self mut V) -> bool) { + fn mutate_values(&mut self, it: &fn(&uint, &mut V) -> bool) { for uint::range(0, self.v.len()) |i| { match self.v[i] { Some(ref mut elt) => if !it(&i, elt) { break }, @@ -81,6 +104,7 @@ impl Map for SmallIntMap { } /// Return a reference to the value corresponding to the key + #[cfg(stage0)] fn find(&self, key: &uint) -> Option<&'self V> { if *key < self.v.len() { match self.v[*key] { @@ -92,7 +116,23 @@ impl Map for SmallIntMap { } } + /// Return a reference to the value corresponding to the key + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn find<'a>(&'a self, key: &uint) -> Option<&'a V> { + if *key < self.v.len() { + match self.v[*key] { + Some(ref value) => Some(value), + None => None + } + } else { + None + } + } + /// Return a mutable reference to the value corresponding to the key + #[cfg(stage0)] fn find_mut(&mut self, key: &uint) -> Option<&'self mut V> { if *key < self.v.len() { match self.v[*key] { @@ -104,6 +144,21 @@ impl Map for SmallIntMap { } } + /// Return a mutable reference to the value corresponding to the key + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn find_mut<'a>(&'a mut self, key: &uint) -> Option<&'a mut V> { + if *key < self.v.len() { + match self.v[*key] { + Some(ref mut value) => Some(value), + None => None + } + } else { + None + } + } + /// Insert a key-value pair into the map. An existing value for a /// key is replaced by the new value. Return true if the key did /// not already exist in the map. @@ -134,6 +189,7 @@ pub impl SmallIntMap { fn new() -> SmallIntMap { SmallIntMap{v: ~[]} } /// Visit all key-value pairs in reverse order + #[cfg(stage0)] fn each_reverse(&self, it: &fn(uint, &'self V) -> bool) { for uint::range_rev(self.v.len(), 0) |i| { match self.v[i - 1] { @@ -143,9 +199,30 @@ pub impl SmallIntMap { } } + /// Visit all key-value pairs in reverse order + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn each_reverse<'a>(&'a self, it: &fn(uint, &'a V) -> bool) { + for uint::range_rev(self.v.len(), 0) |i| { + match self.v[i - 1] { + Some(ref elt) => if !it(i - 1, elt) { break }, + None => () + } + } + } + + #[cfg(stage0)] fn get(&self, key: &uint) -> &'self V { self.find(key).expect("key not present") } + + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn get<'a>(&'a self, key: &uint) -> &'a V { + self.find(key).expect("key not present") + } } pub impl SmallIntMap { diff --git a/src/libstd/treemap.rs b/src/libstd/treemap.rs index 041ea855cb3..006455c44e4 100644 --- a/src/libstd/treemap.rs +++ b/src/libstd/treemap.rs @@ -105,26 +105,45 @@ impl Map for TreeMap { } /// Visit all key-value pairs in order + #[cfg(stage0)] fn each(&self, f: &fn(&'self K, &'self V) -> bool) { each(&self.root, f) } + /// Visit all key-value pairs in order + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn each<'a>(&'a self, f: &fn(&'a K, &'a V) -> bool) { + each(&self.root, f) + } + /// Visit all keys in order fn each_key(&self, f: &fn(&K) -> bool) { self.each(|k, _| f(k)) } /// Visit all values in order + #[cfg(stage0)] fn each_value(&self, f: &fn(&V) -> bool) { self.each(|_, v| f(v)) } + /// Visit all values in order + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn each_value<'a>(&'a self, f: &fn(&'a V) -> bool) { + self.each(|_, v| f(v)) + } + /// Iterate over the map and mutate the contained values - fn mutate_values(&mut self, f: &fn(&'self K, &'self mut V) -> bool) { + fn mutate_values(&mut self, f: &fn(&K, &mut V) -> bool) { mutate_values(&mut self.root, f); } /// Return a reference to the value corresponding to the key + #[cfg(stage0)] fn find(&self, key: &K) -> Option<&'self V> { let mut current: &'self Option<~TreeNode> = &self.root; loop { @@ -141,12 +160,42 @@ impl Map for TreeMap { } } + /// Return a reference to the value corresponding to the key + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn find<'a>(&'a self, key: &K) -> Option<&'a V> { + let mut current: &'a Option<~TreeNode> = &self.root; + loop { + match *current { + Some(ref r) => { + match key.cmp(&r.key) { + Less => current = &r.left, + Greater => current = &r.right, + Equal => return Some(&r.value) + } + } + None => return None + } + } + } + /// Return a mutable reference to the value corresponding to the key #[inline(always)] + #[cfg(stage0)] fn find_mut(&mut self, key: &K) -> Option<&'self mut V> { find_mut(&mut self.root, key) } + /// Return a mutable reference to the value corresponding to the key + #[inline(always)] + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn find_mut<'a>(&'a mut self, key: &K) -> Option<&'a mut V> { + find_mut(&mut self.root, key) + } + /// Insert a key-value pair into the map. An existing value for a /// key is replaced by the new value. Return true if the key did /// not already exist in the map. @@ -170,7 +219,16 @@ pub impl TreeMap { fn new() -> TreeMap { TreeMap{root: None, length: 0} } /// Visit all key-value pairs in reverse order - fn each_reverse(&'self self, f: &fn(&'self K, &'self V) -> bool) { + #[cfg(stage0)] + fn each_reverse(&self, f: &fn(&'self K, &'self V) -> bool) { + each_reverse(&self.root, f); + } + + /// Visit all key-value pairs in reverse order + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn each_reverse<'a>(&'a self, f: &fn(&'a K, &'a V) -> bool) { each_reverse(&self.root, f); } @@ -186,9 +244,19 @@ pub impl TreeMap { /// Get a lazy iterator over the key-value pairs in the map. /// Requires that it be frozen (immutable). + #[cfg(stage0)] fn iter(&self) -> TreeMapIterator<'self, K, V> { TreeMapIterator{stack: ~[], node: &self.root} } + + /// Get a lazy iterator over the key-value pairs in the map. + /// Requires that it be frozen (immutable). + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn iter<'a>(&'a self) -> TreeMapIterator<'a, K, V> { + TreeMapIterator{stack: ~[], node: &self.root} + } } /// Lazy forward iterator over a map @@ -490,9 +558,20 @@ pub impl TreeSet { /// Get a lazy iterator over the values in the set. /// Requires that it be frozen (immutable). #[inline(always)] + #[cfg(stage0)] fn iter(&self) -> TreeSetIterator<'self, T> { TreeSetIterator{iter: self.map.iter()} } + + /// Get a lazy iterator over the values in the set. + /// Requires that it be frozen (immutable). + #[inline(always)] + #[cfg(stage1)] + #[cfg(stage2)] + #[cfg(stage3)] + fn iter<'a>(&'a self) -> TreeSetIterator<'a, T> { + TreeSetIterator{iter: self.map.iter()} + } } /// Lazy forward iterator over a set From c97c03cd6a5c4ac37f2e68226c9f8ec49c786fcf Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 8 Apr 2013 17:43:55 -0700 Subject: [PATCH 5/5] tests: changes in response to #5656 --- src/test/compile-fail/issue-3311.rs | 31 -------------- src/test/compile-fail/issue-3563.rs | 2 +- src/test/compile-fail/issue-3888.rs | 42 ------------------- src/test/compile-fail/issue-3969.rs | 2 +- .../regions-infer-paramd-indirect.rs | 4 +- src/test/compile-fail/staticness-mismatch.rs | 2 +- .../class-impl-very-parameterized-trait.rs | 10 ++--- src/test/run-pass/explicit-self.rs | 2 +- src/test/run-pass/issue-3860.rs | 2 +- ...-parameterization-self-types-issue-5224.rs | 38 ----------------- src/test/run-pass/regions-self-impls.rs | 2 +- src/test/run-pass/regions-trait.rs | 2 +- 12 files changed, 14 insertions(+), 125 deletions(-) delete mode 100644 src/test/compile-fail/issue-3311.rs delete mode 100644 src/test/compile-fail/issue-3888.rs delete mode 100644 src/test/run-pass/regions-parameterization-self-types-issue-5224.rs diff --git a/src/test/compile-fail/issue-3311.rs b/src/test/compile-fail/issue-3311.rs deleted file mode 100644 index 67059e4623e..00000000000 --- a/src/test/compile-fail/issue-3311.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#[legacy_mode] -struct Foo<'self> { - s: &'self str, - u: ~() -} - -pub impl<'self> Foo<'self> { - fn get_s(&self) -> &'self str { - self.s - } -} - -fn bar(s: &str, f: &fn(Option)) { - f(Some(Foo {s: s, u: ~()})); -} - -fn main() { - do bar(~"testing") |opt| { - io::println(opt.unwrap().get_s()); //~ ERROR illegal borrow: - }; -} diff --git a/src/test/compile-fail/issue-3563.rs b/src/test/compile-fail/issue-3563.rs index d56cb0d51d2..38f28bd79df 100644 --- a/src/test/compile-fail/issue-3563.rs +++ b/src/test/compile-fail/issue-3563.rs @@ -10,7 +10,7 @@ trait A { fn a(&self) { - || self.b() //~ ERROR type `&'self Self` does not implement any method in scope named `b` + || self.b() //~ ERROR type `&Self` does not implement any method in scope named `b` } } fn main() {} diff --git a/src/test/compile-fail/issue-3888.rs b/src/test/compile-fail/issue-3888.rs deleted file mode 100644 index 35f8557c32b..00000000000 --- a/src/test/compile-fail/issue-3888.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// n.b. This should be a run-pass test, but for now I'm testing -// that we don't see an "unknown scope" error. -fn vec_peek<'r, T>(v: &'r [T]) -> Option< (&'r T, &'r [T]) > { - if v.len() == 0 { - None - } else { - let vec_len = v.len(); - let head = &v[0]; - // note: this *shouldn't* be an illegal borrow! See #3888 - let tail = v.slice(1, vec_len); //~ ERROR illegal borrow: borrowed value does not live long enough - Some( (head, tail) ) - } -} - - -fn test_peek_empty_stack() { - let v : &[int] = &[]; - assert!((None == vec_peek(v))); -} - -fn test_peek_empty_unique() { - let v : ~[int] = ~[]; - assert!((None == vec_peek(v))); -} - -fn test_peek_empty_managed() { - let v : @[int] = @[]; - assert!((None == vec_peek(v))); -} - - -fn main() {} diff --git a/src/test/compile-fail/issue-3969.rs b/src/test/compile-fail/issue-3969.rs index 60991d40a54..b60a54a44bb 100644 --- a/src/test/compile-fail/issue-3969.rs +++ b/src/test/compile-fail/issue-3969.rs @@ -18,7 +18,7 @@ trait BikeMethods { impl BikeMethods for Bike { fn woops() -> ~str { ~"foo" } - //~^ ERROR method `woops` is declared as static in its impl, but not in its trait + //~^ ERROR has a `&const self` declaration in the trait, but not in the impl } pub 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 e4ad93bde17..e8d66ab297b 100644 --- a/src/test/compile-fail/regions-infer-paramd-indirect.rs +++ b/src/test/compile-fail/regions-infer-paramd-indirect.rs @@ -18,12 +18,12 @@ struct c<'self> { f: @b<'self> } -trait set_f { +trait set_f<'self> { fn set_f_ok(&self, b: @b<'self>); fn set_f_bad(&self, b: @b); } -impl<'self> set_f for c<'self> { +impl<'self> set_f<'self> for c<'self> { fn set_f_ok(&self, b: @b<'self>) { self.f = b; } diff --git a/src/test/compile-fail/staticness-mismatch.rs b/src/test/compile-fail/staticness-mismatch.rs index 719da233335..9bcf0777bbd 100644 --- a/src/test/compile-fail/staticness-mismatch.rs +++ b/src/test/compile-fail/staticness-mismatch.rs @@ -14,7 +14,7 @@ trait foo { } impl foo for int { - fn bar(&self) {} //~ ERROR method `bar` is declared as static in its trait, but not in its impl + fn bar(&self) {} //~ ERROR method `bar` has a `&self` declaration in the impl, but not in the trait } fn main() {} diff --git a/src/test/run-pass/class-impl-very-parameterized-trait.rs b/src/test/run-pass/class-impl-very-parameterized-trait.rs index cfde61d74b2..e4374e4d225 100644 --- a/src/test/run-pass/class-impl-very-parameterized-trait.rs +++ b/src/test/run-pass/class-impl-very-parameterized-trait.rs @@ -59,7 +59,7 @@ impl Mutable for cat { } impl Map for cat { - fn each(&self, f: &fn(&int, &T) -> bool) { + fn each<'a>(&'a self, f: &fn(&int, &'a T) -> bool) { let mut n = int::abs(self.meows); while n > 0 { if !f(&n, &self.name) { break; } @@ -73,7 +73,7 @@ impl Map for cat { for self.each |k, _| { if !f(k) { break; } loop;}; } - fn each_value(&self, f: &fn(v: &T) -> bool) { + fn each_value<'a>(&'a self, f: &fn(v: &'a T) -> bool) { for self.each |_, v| { if !f(v) { break; } loop;}; } @@ -86,7 +86,7 @@ impl Map for cat { true } - fn find(&self, k: &int) -> Option<&'self T> { + fn find<'a>(&'a self, k: &int) -> Option<&'a T> { if *k <= self.meows { Some(&self.name) } else { @@ -94,7 +94,7 @@ impl Map for cat { } } - fn find_mut(&mut self, _k: &int) -> Option<&'self mut T> { fail!() } + fn find_mut<'a>(&'a mut self, _k: &int) -> Option<&'a mut T> { fail!() } fn remove(&mut self, k: &int) -> bool { if self.find(k).is_some() { @@ -106,7 +106,7 @@ impl Map for cat { } pub impl cat { - fn get(&self, k: &int) -> &'self T { + fn get<'a>(&'a self, k: &int) -> &'a T { match self.find(k) { Some(v) => { v } None => { fail!(~"epic fail"); } diff --git a/src/test/run-pass/explicit-self.rs b/src/test/run-pass/explicit-self.rs index c5b5016572c..7e46bf22c4d 100644 --- a/src/test/run-pass/explicit-self.rs +++ b/src/test/run-pass/explicit-self.rs @@ -58,7 +58,7 @@ pub impl thing { fn foo(@self) -> int { *self.x.a } fn bar(~self) -> int { *self.x.a } fn quux(&self) -> int { *self.x.a } - fn baz(&self) -> &'self A { &self.x } + fn baz<'a>(&'a self) -> &'a A { &self.x } fn spam(self) -> int { *self.x.a } } diff --git a/src/test/run-pass/issue-3860.rs b/src/test/run-pass/issue-3860.rs index 18839fa3c7f..46aa7187c9a 100644 --- a/src/test/run-pass/issue-3860.rs +++ b/src/test/run-pass/issue-3860.rs @@ -11,7 +11,7 @@ struct Foo { x: int } pub impl Foo { - fn stuff(&mut self) -> &'self mut Foo { + fn stuff<'a>(&'a mut self) -> &'a mut Foo { return self; } } diff --git a/src/test/run-pass/regions-parameterization-self-types-issue-5224.rs b/src/test/run-pass/regions-parameterization-self-types-issue-5224.rs deleted file mode 100644 index 346a0fcfe07..00000000000 --- a/src/test/run-pass/regions-parameterization-self-types-issue-5224.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2013 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. - -// Test how region-parameterization inference -// interacts with explicit self types. -// -// Issue #5224. - -trait Getter { - // This trait does not need to be - // region-parameterized, because 'self - // is bound in the self type: - fn get(&self) -> &'self int; -} - -struct Foo { - field: int -} - -impl Getter for Foo { - fn get(&self) -> &'self int { &self.field } -} - -fn get_int(g: &G) -> int { - *g.get() -} - -pub fn main() { - let foo = Foo { field: 22 }; - assert!(get_int(&foo) == 22); -} diff --git a/src/test/run-pass/regions-self-impls.rs b/src/test/run-pass/regions-self-impls.rs index 16b6364093e..2f4eefe5243 100644 --- a/src/test/run-pass/regions-self-impls.rs +++ b/src/test/run-pass/regions-self-impls.rs @@ -16,7 +16,7 @@ trait get_chowder<'self> { fn get_chowder(&self) -> &'self int; } -impl<'self> get_chowder for Clam<'self> { +impl<'self> get_chowder<'self> for Clam<'self> { fn get_chowder(&self) -> &'self int { return self.chowder; } } diff --git a/src/test/run-pass/regions-trait.rs b/src/test/run-pass/regions-trait.rs index f4532720579..a2ed9da67f2 100644 --- a/src/test/run-pass/regions-trait.rs +++ b/src/test/run-pass/regions-trait.rs @@ -16,7 +16,7 @@ trait get_ctxt<'self> { struct HasCtxt<'self> { c: &'self Ctxt } -impl<'self> get_ctxt for HasCtxt<'self> { +impl<'self> get_ctxt<'self> for HasCtxt<'self> { fn get_ctxt(&self) -> &'self Ctxt { self.c }