1
Fork 0

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.
This commit is contained in:
Niko Matsakis 2013-04-01 22:32:37 -07:00
commit 3322595e89
27 changed files with 1027 additions and 347 deletions

View file

@ -116,6 +116,19 @@ totalord_impl!(i64)
totalord_impl!(int) totalord_impl!(int)
totalord_impl!(uint) totalord_impl!(uint)
pub fn cmp2<A:TotalOrd,B:TotalOrd>(
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. * Trait for values that can be compared for a sort-order.
* *
@ -193,6 +206,14 @@ mod test {
assert_eq!(12.cmp(-5), Greater); 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] #[test]
fn test_int_totaleq() { fn test_int_totaleq() {
assert!(5.equals(&5)); assert!(5.equals(&5));

View file

@ -301,7 +301,11 @@ pub fn slice_shift_char<'a>(s: &'a str) -> (char, &'a str) {
/// Prepend a char to a string /// Prepend a char to a string
pub fn unshift_char(s: &mut ~str, ch: char) { 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;
} }
/** /**

View file

@ -161,7 +161,6 @@ impl<A:Ord> Ord for (A,) {
fn gt(&self, other: &(A,)) -> bool { other.lt(&(*self)) } fn gt(&self, other: &(A,)) -> bool { other.lt(&(*self)) }
} }
#[cfg(notest)] #[cfg(notest)]
impl<A:Eq,B:Eq> Eq for (A, B) { impl<A:Eq,B:Eq> Eq for (A, B) {
#[inline(always)] #[inline(always)]

View file

@ -239,7 +239,8 @@ fn parse_region(st: @mut PState) -> ty::Region {
assert!(next(st) == '|'); assert!(next(st) == '|');
let br = parse_bound_region(st); let br = parse_bound_region(st);
assert!(next(st) == ']'); assert!(next(st) == ']');
ty::re_free(id, br) ty::re_free(ty::FreeRegion {scope_id: id,
bound_region: br})
} }
's' => { 's' => {
let id = parse_int(st); let id = parse_int(st);

View file

@ -146,12 +146,12 @@ fn enc_region(w: @io::Writer, cx: @ctxt, r: ty::Region) {
w.write_char('b'); w.write_char('b');
enc_bound_region(w, cx, br); enc_bound_region(w, cx, br);
} }
ty::re_free(id, br) => { ty::re_free(ref fr) => {
w.write_char('f'); w.write_char('f');
w.write_char('['); w.write_char('[');
w.write_int(id); w.write_int(fr.scope_id);
w.write_char('|'); w.write_char('|');
enc_bound_region(w, cx, br); enc_bound_region(w, cx, fr.bound_region);
w.write_char(']'); w.write_char(']');
} }
ty::re_scope(nid) => { ty::re_scope(nid) => {

View file

@ -475,9 +475,12 @@ impl tr for ty::Region {
fn tr(&self, xcx: @ExtendedDecodeContext) -> ty::Region { fn tr(&self, xcx: @ExtendedDecodeContext) -> ty::Region {
match *self { match *self {
ty::re_bound(br) => ty::re_bound(br.tr(xcx)), 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_scope(id) => ty::re_scope(xcx.tr_id(id)),
ty::re_static | ty::re_infer(*) => *self, 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)})
}
} }
} }
} }

View file

@ -128,9 +128,9 @@ pub impl CheckLoanCtxt {
Some(e) => return Some(pc_cmt(*e)) 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, 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, 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()); 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| { for self.walk_loans(par_scope_id) |old_loan| {
debug!("old_loan=%?", self.bccx.loan_to_repr(old_loan)); debug!("old_loan=%?", self.bccx.loan_to_repr(old_loan));

View file

@ -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 `a.b(...)`), the call where it's an argument
// (if used like `x(a.b)`), or the block (if used like `let x // (if used like `x(a.b)`), or the block (if used like `let x
// = a.b`). // = 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); let rcvr_cmt = self.bccx.cat_expr(rcvr);
self.guarantee_valid(rcvr_cmt, m_imm, scope_r); self.guarantee_valid(rcvr_cmt, m_imm, scope_r);
visit::visit_expr(ex, self, vt); visit::visit_expr(ex, self, vt);
@ -524,7 +524,10 @@ pub impl GatherLoanCtxt {
// immutable structures, this is just the converse I suppose) // immutable structures, this is just the converse I suppose)
let scope_id = match scope_r { 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( self.bccx.tcx.sess.span_bug(
cmt.span, cmt.span,

View file

@ -130,8 +130,8 @@ pub impl LoanContext {
} }
cat_local(local_id) | cat_arg(local_id) | cat_self(local_id) => { cat_local(local_id) | cat_arg(local_id) | cat_self(local_id) => {
// FIXME(#4903) // FIXME(#4903)
let local_scope_id = *self.bccx.tcx.region_map.get(&local_id); let local_region = self.bccx.tcx.region_maps.encl_region(local_id);
self.issue_loan(cmt, ty::re_scope(local_scope_id), loan_kind, self.issue_loan(cmt, local_region, loan_kind,
owns_lent_data) owns_lent_data)
} }
cat_stack_upvar(cmt) => { cat_stack_upvar(cmt) => {

View file

@ -227,7 +227,6 @@ Borrowck results in two maps.
use core::prelude::*; use core::prelude::*;
use middle::mem_categorization::*; use middle::mem_categorization::*;
use middle::region;
use middle::ty; use middle::ty;
use middle::typeck; use middle::typeck;
use middle::moves; use middle::moves;
@ -458,7 +457,7 @@ pub fn root_map() -> root_map {
pub impl BorrowckCtxt { pub impl BorrowckCtxt {
fn is_subregion_of(&self, r_sub: ty::Region, r_sup: ty::Region) -> bool { 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 { fn cat_expr(&self, expr: @ast::expr) -> cmt {

View file

@ -108,7 +108,7 @@ pub impl<'self> PreserveCtxt<'self> {
// Maybe if we pass in the parent instead here, // Maybe if we pass in the parent instead here,
// we can prevent the "scope not found" error // we can prevent the "scope not found" error
debug!("scope_region thing: %? ", cmt.id); 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) self.compare_scope(cmt, scope_region)
@ -128,27 +128,27 @@ pub impl<'self> PreserveCtxt<'self> {
cmt.span, cmt.span,
~"preserve() called with local and !root_managed_data"); ~"preserve() called with local and !root_managed_data");
} }
let local_scope_id = *self.tcx().region_map.get(&local_id); let local_region = self.tcx().region_maps.encl_region(local_id);
self.compare_scope(cmt, ty::re_scope(local_scope_id)) self.compare_scope(cmt, local_region)
} }
cat_binding(local_id) => { cat_binding(local_id) => {
// Bindings are these kind of weird implicit pointers (cc // Bindings are these kind of weird implicit pointers (cc
// #2329). We require (in gather_loans) that they be // #2329). We require (in gather_loans) that they be
// rooted in an immutable location. // rooted in an immutable location.
let local_scope_id = *self.tcx().region_map.get(&local_id); let local_region = self.tcx().region_maps.encl_region(local_id);
self.compare_scope(cmt, ty::re_scope(local_scope_id)) self.compare_scope(cmt, local_region)
} }
cat_arg(local_id) => { cat_arg(local_id) => {
// This can happen as not all args are lendable (e.g., && // This can happen as not all args are lendable (e.g., &&
// modes). In that case, the caller guarantees stability // modes). In that case, the caller guarantees stability
// for at least the scope of the fn. This is basically a // for at least the scope of the fn. This is basically a
// deref of a region ptr. // deref of a region ptr.
let local_scope_id = *self.tcx().region_map.get(&local_id); let local_region = self.tcx().region_maps.encl_region(local_id);
self.compare_scope(cmt, ty::re_scope(local_scope_id)) self.compare_scope(cmt, local_region)
} }
cat_self(local_id) => { cat_self(local_id) => {
let local_scope_id = *self.tcx().region_map.get(&local_id); let local_region = self.tcx().region_maps.encl_region(local_id);
self.compare_scope(cmt, ty::re_scope(local_scope_id)) self.compare_scope(cmt, local_region)
} }
cat_comp(cmt_base, comp_field(*)) | cat_comp(cmt_base, comp_field(*)) |
cat_comp(cmt_base, comp_index(*)) | cat_comp(cmt_base, comp_index(*)) |

View file

@ -596,8 +596,11 @@ pub fn specialize(cx: @MatchCheckCtxt,
class_id); class_id);
} }
_ => { _ => {
cx.tcx.sess.span_bug(pat_span, cx.tcx.sess.span_bug(
~"struct pattern didn't resolve to a struct"); 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| { let args = vec::map(class_fields, |class_field| {

View file

@ -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 /// This is rather subtle. When we are casting a value to a instantiated
/// instantiated trait like `a as trait<'r>`, regionck already ensures /// trait like `a as trait<'r>`, regionck already ensures that any borrowed
/// that any borrowed pointers that appear in the type of `a` are /// pointers that appear in the type of `a` are bounded by `'r` (ed.: modulo
/// bounded by `&r`. However, it is possible that there are *type /// FIXME(#5723)). However, it is possible that there are *type parameters*
/// parameters* in the type of `a`, and those *type parameters* may /// in the type of `a`, and those *type parameters* may have borrowed pointers
/// have borrowed pointers within them. We have to guarantee that the /// within them. We have to guarantee that the regions which appear in those
/// regions which appear in those type parameters are not obscured. /// type parameters are not obscured.
/// ///
/// Therefore, we ensure that one of three conditions holds: /// 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 /// (3) The type parameter is owned (and therefore does not contain
/// borrowed ptrs). /// borrowed ptrs).
///
/// FIXME(#5723)---This code should probably move into regionck.
pub fn check_cast_for_escaping_regions( pub fn check_cast_for_escaping_regions(
cx: Context, cx: Context,
source: @expr, 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 // Determine what type we are casting to; if it is not an trait, then no
// worries. // worries.
let target_ty = ty::expr_ty(cx.tcx, target); let target_ty = ty::expr_ty(cx.tcx, target);
let target_substs = match ty::get(target_ty).sty { match ty::get(target_ty).sty {
ty::ty_trait(_, ref substs, _) => {(/*bad*/copy *substs)} ty::ty_trait(*) => {}
_ => { return; /* not a cast to a 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 // Check, based on the region associated with the trait, whether it can
// possibly escape the enclosing fn item (note that all type parameters // possibly escape the enclosing fn item (note that all type parameters
// must have been declared on the enclosing fn item): // must have been declared on the enclosing fn item).
match target_substs.self_r { if target_regions.any(|r| is_re_scope(*r)) {
Some(ty::re_scope(*)) => { return; /* case (1) */ } 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));
}
} }
// Assuming the trait instance can escape, then ensure that each parameter // 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 target_params = ty::param_tys_in_type(target_ty);
let source_ty = ty::expr_ty(cx.tcx, source); let source_ty = ty::expr_ty(cx.tcx, source);
do ty::walk_ty(source_ty) |ty| { ty::walk_regions_and_ty(
match ty::get(ty).sty { cx.tcx,
ty::ty_param(source_param) => { source_ty,
if target_params.contains(&source_param) {
/* case (2) */ |_r| {
} else { // FIXME(#5723) --- turn this check on once &Objects are usable
check_durable(cx.tcx, ty, source.span); /* case (3) */ //
// 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. /// Ensures that values placed into a ~Trait are copyable and sendable.

View file

@ -11,7 +11,7 @@
/*! /*!
This file actually contains two passes related to regions. The first 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 the region hierarchy. The second pass infers which types must be
region parameterized. region parameterized.
@ -23,7 +23,7 @@ use driver::session::Session;
use metadata::csearch; use metadata::csearch;
use middle::resolve; use middle::resolve;
use middle::ty::{region_variance, rv_covariant, rv_invariant}; use middle::ty::{region_variance, rv_covariant, rv_invariant};
use middle::ty::{rv_contravariant}; use middle::ty::{rv_contravariant, FreeRegion};
use middle::ty; use middle::ty;
use core::hashmap::{HashMap, HashSet}; use core::hashmap::{HashMap, HashSet};
@ -37,23 +37,31 @@ use syntax::{ast, visit};
pub type parent = Option<ast::node_id>; pub type parent = Option<ast::node_id>;
/** /**
Encodes the bounding lifetime for a given AST node: The region maps encode information about region relationships.
- 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.
- `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<ast::node_id, ast::node_id>; pub struct RegionMaps {
priv scope_map: HashMap<ast::node_id, ast::node_id>,
priv free_region_map: HashMap<FreeRegion, ~[FreeRegion]>,
}
pub struct ctxt { pub struct ctxt {
sess: Session, sess: Session,
def_map: resolve::DefMap, def_map: resolve::DefMap,
// Generated maps: // Generated maps:
region_map: region_map, region_maps: @mut RegionMaps,
// Generally speaking, expressions are parented to their innermost // Generally speaking, expressions are parented to their innermost
// enclosing block. But some kinds of expressions serve as // enclosing block. But some kinds of expressions serve as
@ -98,94 +106,215 @@ pub struct ctxt {
parent: parent, parent: parent,
} }
/// Returns true if `subscope` is equal to or is lexically nested inside pub impl RegionMaps {
/// `superscope` and false otherwise. fn relate_free_regions(&mut self,
pub fn scope_contains(region_map: region_map, superscope: ast::node_id, sub: FreeRegion,
subscope: ast::node_id) -> bool { sup: FreeRegion)
let mut subscope = subscope; {
while superscope != subscope { match self.free_region_map.find_mut(&sub) {
match region_map.find(&subscope) { Some(sups) => {
None => return false, if !sups.contains(&sup) {
Some(&scope) => subscope = scope 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<ast::node_id>
{
//! 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 fn encl_region(&self,
/// intended to run *after inference* and sadly the logic is somewhat id: ast::node_id) -> ty::Region
/// duplicated with the code in infer.rs. {
pub fn is_subregion_of(region_map: region_map, //! Returns the narrowest scope region that encloses `id`, if any.
sub_region: ty::Region,
super_region: ty::Region) -> bool {
sub_region == super_region ||
match (sub_region, super_region) {
(_, ty::re_static) => {
true
}
(ty::re_scope(sub_scope), ty::re_scope(super_scope)) | ty::re_scope(self.encl_scope(id))
(ty::re_scope(sub_scope), ty::re_free(super_scope, _)) => { }
scope_contains(region_map, super_scope, sub_scope)
}
_ => { fn is_sub_scope(&self,
false 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 fn sub_free_region(&self,
/// is, finds the smallest scope which is greater than or equal to sub: FreeRegion,
/// both `scope_a` and `scope_b`. sup: FreeRegion) -> bool
pub fn nearest_common_ancestor(region_map: region_map, {
scope_a: ast::node_id, /*!
scope_b: ast::node_id) * Determines whether two free regions have a subregion relationship
-> Option<ast::node_id> { * 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) if sub == sup {
-> ~[ast::node_id] { return true;
let mut result = ~[scope]; }
let mut scope = scope;
loop { // Do a little breadth-first-search here. The `queue` list
match region_map.find(&scope) { // doubles as a way to detect if we've seen a particular FR
None => return result, // before. Note that we expect this graph to be an *extremely
Some(&superscope) => { // shallow* tree.
result.push(superscope); let mut queue = ~[sub];
scope = superscope; 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<ast::node_id>
{
/*!
* 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); if scope_a == scope_b { return Some(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;
// Here, ~[ab]_ancestors is a vector going from narrow to broad. let a_ancestors = ancestors_of(self, scope_a);
// The end of each vector will be the item where the scope is let b_ancestors = ancestors_of(self, scope_b);
// defined; if there are any common ancestors, then the tails of let mut a_index = vec::len(a_ancestors) - 1u;
// the vector will be the same. So basically we want to walk let mut b_index = vec::len(b_ancestors) - 1u;
// 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.
if a_ancestors[a_index] != b_ancestors[b_index] { // Here, ~[ab]_ancestors is a vector going from narrow to broad.
return None; // 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] { 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`. /// Records the current parent (if any) as the parent of `child_id`.
pub fn record_parent(cx: ctxt, child_id: ast::node_id) { pub fn record_parent(cx: ctxt, child_id: ast::node_id) {
for cx.parent.each |parent_id| { for cx.parent.each |parent_id| {
debug!("parent of node %d is node %d", child_id, *parent_id); cx.region_maps.record_parent(child_id, *parent_id);
cx.region_map.insert(child_id, *parent_id);
} }
} }
@ -328,7 +456,7 @@ pub fn resolve_fn(fk: &visit::fn_kind,
// Record the ID of `self`. // Record the ID of `self`.
match *fk { match *fk {
visit::fk_method(_, _, method) => { 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); body.node.id, cx.parent, fn_cx.parent);
for decl.inputs.each |input| { 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); 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, pub fn resolve_crate(sess: Session,
def_map: resolve::DefMap, def_map: resolve::DefMap,
crate: @ast::crate) crate: @ast::crate) -> @mut RegionMaps
-> region_map { {
let region_maps = @mut RegionMaps {
scope_map: HashMap::new(),
free_region_map: HashMap::new()
};
let cx: ctxt = ctxt {sess: sess, let cx: ctxt = ctxt {sess: sess,
def_map: def_map, def_map: def_map,
region_map: @mut HashMap::new(), region_maps: region_maps,
root_exprs: @mut HashSet::new(), root_exprs: @mut HashSet::new(),
parent: None}; parent: None};
let visitor = visit::mk_vt(@visit::Visitor { let visitor = visit::mk_vt(@visit::Visitor {
@ -365,7 +497,7 @@ pub fn resolve_crate(sess: Session,
.. *visit::default_visitor() .. *visit::default_visitor()
}); });
visit::visit_crate(*crate, cx, visitor); visit::visit_crate(*crate, cx, visitor);
return cx.region_map; return region_maps;
} }
// ___________________________________________________________________________ // ___________________________________________________________________________

View file

@ -240,7 +240,7 @@ struct ctxt_ {
sess: session::Session, sess: session::Session,
def_map: resolve::DefMap, def_map: resolve::DefMap,
region_map: middle::region::region_map, region_maps: @mut middle::region::RegionMaps,
region_paramd_items: middle::region::region_paramd_items, region_paramd_items: middle::region::region_paramd_items,
// Stores the types for various nodes in the AST. Note that this table // Stores the types for various nodes in the AST. Note that this table
@ -410,7 +410,7 @@ pub struct param_ty {
/// Representation of regions: /// Representation of regions:
#[auto_encode] #[auto_encode]
#[auto_decode] #[auto_decode]
#[deriving(Eq)] #[deriving(Eq, IterBytes)]
pub enum Region { pub enum Region {
/// Bound regions are found (primarily) in function types. They indicate /// Bound regions are found (primarily) in function types. They indicate
/// region parameters that have yet to be replaced with actual regions /// 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 /// 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 /// that refer to bound region parameters are modified to refer to free
/// region parameters. /// region parameters.
re_free(node_id, bound_region), re_free(FreeRegion),
/// A concrete region naming some expression within the current function. /// A concrete region naming some expression within the current function.
re_scope(node_id), re_scope(node_id),
@ -438,9 +438,26 @@ pub enum Region {
re_infer(InferRegion) re_infer(InferRegion)
} }
pub impl Region {
fn is_bound(&self) -> bool {
match self {
&re_bound(*) => true,
_ => false
}
}
}
#[auto_encode] #[auto_encode]
#[auto_decode] #[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 { pub enum bound_region {
/// The self region for structs, impls (&T in a type defn or &'self T) /// The self region for structs, impls (&T in a type defn or &'self T)
br_self, br_self,
@ -810,7 +827,7 @@ pub fn mk_ctxt(s: session::Session,
dm: resolve::DefMap, dm: resolve::DefMap,
amap: ast_map::map, amap: ast_map::map,
freevars: freevars::freevar_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, region_paramd_items: middle::region::region_paramd_items,
+lang_items: middle::lang_items::LanguageItems, +lang_items: middle::lang_items::LanguageItems,
crate: @ast::crate) crate: @ast::crate)
@ -837,7 +854,7 @@ pub fn mk_ctxt(s: session::Session,
cstore: s.cstore, cstore: s.cstore,
sess: s, sess: s,
def_map: dm, def_map: dm,
region_map: region_map, region_maps: region_maps,
region_paramd_items: region_paramd_items, region_paramd_items: region_paramd_items,
node_types: @mut SmallIntMap::new(), node_types: @mut SmallIntMap::new(),
node_type_substs: @mut HashMap::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)) { pub fn walk_ty(ty: t, f: &fn(t)) {
maybe_walk_ty(ty, |t| { f(t); true }); maybe_walk_ty(ty, |t| { f(t); true });
} }
@ -1308,8 +1316,8 @@ pub fn walk_regions_and_ty(
fold_regions_and_ty( fold_regions_and_ty(
cx, ty, cx, ty,
|r| { walkr(r); r }, |r| { walkr(r); r },
|t| { walkt(t); walk_regions_and_ty(cx, t, walkr, walkt); t }, |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 });
} }
} }
@ -2506,43 +2514,52 @@ pub fn index_sty(cx: ctxt, sty: &sty) -> Option<mt> {
} }
} }
impl to_bytes::IterBytes for bound_region { /**
fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) { * Enforces an arbitrary but consistent total ordering over
match *self { * free regions. This is needed for establishing a consistent
ty::br_self => 0u8.iter_bytes(lsb0, f), * 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) => impl cmp::TotalEq for FreeRegion {
to_bytes::iter_bytes_2(&1u8, idx, lsb0, f), 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) => (&ty::br_anon(ref a1), &ty::br_anon(ref a2)) => a1.cmp(a2),
to_bytes::iter_bytes_3(&3u8, id, br, lsb0, f), (&ty::br_anon(*), _) => cmp::Less,
ty::br_fresh(ref x) => (&ty::br_named(ref a1), &ty::br_named(ref a2)) => a1.repr.cmp(&a2.repr),
to_bytes::iter_bytes_2(&4u8, x, lsb0, f) (&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 { impl cmp::TotalEq for bound_region {
fn iter_bytes(&self, +lsb0: bool, f: to_bytes::Cb) { fn equals(&self, other: &bound_region) -> bool {
match *self { *self == *other
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)
}
} }
} }
@ -2856,8 +2873,17 @@ pub fn expr_ty_adjusted(cx: ctxt, expr: @ast::expr) -> t {
*/ */
let unadjusted_ty = expr_ty(cx, expr); 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, None => unadjusted_ty,
Some(&@AutoAddEnv(r, s)) => { 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; } Some(mt) => { adjusted_ty = mt.ty; }
None => { None => {
cx.sess.span_bug( cx.sess.span_bug(
expr.span, span,
fmt!("The %uth autoderef failed: %s", fmt!("The %uth autoderef failed: %s",
i, ty_to_str(cx, i, ty_to_str(cx,
adjusted_ty))); adjusted_ty)));
@ -2905,18 +2931,18 @@ pub fn expr_ty_adjusted(cx: ctxt, expr: @ast::expr) -> t {
} }
AutoBorrowVec => { AutoBorrowVec => {
borrow_vec(cx, expr, autoref, adjusted_ty) borrow_vec(cx, span, autoref, adjusted_ty)
} }
AutoBorrowVecRef => { AutoBorrowVecRef => {
adjusted_ty = borrow_vec(cx, expr, autoref, adjusted_ty = borrow_vec(cx, span, autoref,
adjusted_ty); adjusted_ty);
mk_rptr(cx, autoref.region, mk_rptr(cx, autoref.region,
mt {ty: adjusted_ty, mutbl: ast::m_imm}) mt {ty: adjusted_ty, mutbl: ast::m_imm})
} }
AutoBorrowFn => { 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 { autoref: &AutoRef, ty: ty::t) -> ty::t {
match get(ty).sty { match get(ty).sty {
ty_evec(mt, _) => { ty_evec(mt, _) => {
@ -2938,14 +2964,14 @@ pub fn expr_ty_adjusted(cx: ctxt, expr: @ast::expr) -> t {
ref s => { ref s => {
cx.sess.span_bug( cx.sess.span_bug(
expr.span, span,
fmt!("borrow-vec associated with bad sty: %?", fmt!("borrow-vec associated with bad sty: %?",
s)); s));
} }
} }
} }
fn borrow_fn(cx: ctxt, expr: @ast::expr, fn borrow_fn(cx: ctxt, span: span,
autoref: &AutoRef, ty: ty::t) -> ty::t { autoref: &AutoRef, ty: ty::t) -> ty::t {
match get(ty).sty { match get(ty).sty {
ty_closure(ref fty) => { ty_closure(ref fty) => {
@ -2958,7 +2984,7 @@ pub fn expr_ty_adjusted(cx: ctxt, expr: @ast::expr) -> t {
ref s => { ref s => {
cx.sess.span_bug( cx.sess.span_bug(
expr.span, span,
fmt!("borrow-fn associated with bad sty: %?", fmt!("borrow-fn associated with bad sty: %?",
s)); s));
} }

View file

@ -95,6 +95,7 @@ use middle::typeck::check::method::{CheckTraitsAndInherentMethods};
use middle::typeck::check::method::{CheckTraitsOnly, DontAutoderefReceiver}; use middle::typeck::check::method::{CheckTraitsOnly, DontAutoderefReceiver};
use middle::typeck::check::method::{TransformTypeNormally}; use middle::typeck::check::method::{TransformTypeNormally};
use middle::typeck::check::regionmanip::replace_bound_regions_in_fn_sig; 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::check::vtable::{LocationInfo, VtableContext};
use middle::typeck::CrateCtxt; use middle::typeck::CrateCtxt;
use middle::typeck::infer::{resolve_type, force_tvar}; 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. // the node_id of the body block.
let (isr, self_info, fn_sig) = { let (isr, self_info, fn_sig) = {
replace_bound_regions_in_fn_sig(tcx, inherited_isr, self_info, fn_sig, replace_bound_regions_in_fn_sig(
|br| ty::re_free(body.node.id, br)) 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 arg_tys = fn_sig.inputs.map(|a| a.ty);
let ret_ty = fn_sig.output; let ret_ty = fn_sig.output;
@ -2841,8 +2846,7 @@ pub fn check_decl_local(fcx: @mut FnCtxt, local: @ast::local) {
_ => {} _ => {}
} }
let region = let region = tcx.region_maps.encl_region(local.node.id);
ty::re_scope(*tcx.region_map.get(&local.node.id));
let pcx = pat_ctxt { let pcx = pat_ctxt {
fcx: fcx, fcx: fcx,
map: pat_id_map(tcx.def_map, local.node.pat), map: pat_id_map(tcx.def_map, local.node.pat),

View file

@ -31,13 +31,15 @@ use core::prelude::*;
use middle::freevars::get_freevars; use middle::freevars::get_freevars;
use middle::pat_util::pat_bindings; use middle::pat_util::pat_bindings;
use middle::ty::{encl_region, re_scope}; use middle::ty::{re_scope};
use middle::ty; use middle::ty;
use middle::typeck::check::FnCtxt; use middle::typeck::check::FnCtxt;
use middle::typeck::check::lookup_def; 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_and_force_all_but_regions;
use middle::typeck::infer::resolve_type; 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 core::result;
use syntax::ast::{ManagedSigil, OwnedSigil, BorrowedSigil}; use syntax::ast::{ManagedSigil, OwnedSigil, BorrowedSigil};
@ -53,12 +55,13 @@ pub struct Rcx {
pub type rvt = visit::vt<@mut 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(); let tcx = fcx.tcx();
match def { match def {
def_local(node_id, _) | def_arg(node_id, _, _) | def_local(node_id, _) | def_arg(node_id, _, _) |
def_self(node_id, _) | def_binding(node_id, _) => def_self(node_id, _) | def_binding(node_id, _) => {
return encl_region(tcx, node_id), tcx.region_maps.encl_region(node_id)
}
def_upvar(_, subdef, closure_id, body_id) => { def_upvar(_, subdef, closure_id, body_id) => {
match ty::ty_closure_sigil(fcx.node_ty(closure_id)) { match ty::ty_closure_sigil(fcx.node_ty(closure_id)) {
BorrowedSigil => encl_region_of_def(fcx, *subdef), 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 { fn resolve_node_type(@mut self, id: ast::node_id) -> ty::t {
self.resolve_type(self.fcx.node_ty(id)) 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) { 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(); fcx.infcx().resolve_regions();
} }
pub fn regionck_visitor() -> rvt { fn regionck_visitor() -> rvt {
visit::mk_vt(@visit::Visitor {visit_item: visit_item, visit::mk_vt(@visit::Visitor {visit_item: visit_item,
visit_stmt: visit_stmt, visit_stmt: visit_stmt,
visit_expr: visit_expr, visit_expr: visit_expr,
@ -138,11 +159,11 @@ pub fn regionck_visitor() -> rvt {
.. *visit::default_visitor()}) .. *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 // 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 // Check to make sure that the regions in all local variables are
// within scope. // 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); visit::visit_block(b, rcx, v);
} }
pub fn visit_expr(expr: @ast::expr, &&rcx: @mut Rcx, v: rvt) { fn visit_expr(expr: @ast::expr, &&rcx: @mut Rcx, v: rvt) {
debug!("visit_expr(e=%s)", rcx.fcx.expr_to_str(expr)); debug!("regionck::visit_expr(e=%s)", rcx.fcx.expr_to_str(expr));
for rcx.fcx.inh.adjustments.find(&expr.id).each |&adjustment| { for rcx.fcx.inh.adjustments.find(&expr.id).each |&adjustment| {
debug!("adjustment=%?", adjustment);
match *adjustment { match *adjustment {
@ty::AutoDerefRef( @ty::AutoDerefRef(
ty::AutoDerefRef { ty::AutoDerefRef {autoderefs: autoderefs, autoref: opt_autoref}) =>
autoderefs: autoderefs, autoref: Some(ref autoref)}) => { {
guarantor::for_autoref(rcx, expr, autoderefs, 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) => { ast::expr_addr_of(_, base) => {
guarantor::for_addr_of(rcx, expr, 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); 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); 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 * 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 // find the region where this expr evaluation is taking place
let tcx = fcx.ccx.tcx; 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. // Otherwise, look at the type and see if it is a region pointer.
constrain_regions_in_type_of_node(rcx, id, encl_region, span) 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 * 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 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) { match rcx.fcx.mk_subr(true, expr.span, encl_region, region) {
result::Ok(()) => {} result::Ok(()) => {}
result::Err(_) => { 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, rcx: @mut Rcx,
region: ty::Region, region: ty::Region,
expr: @ast::expr) { 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, rcx: @mut Rcx,
id: ast::node_id, id: ast::node_id,
encl_region: ty::Region, encl_region: ty::Region,
span: span) -> bool { span: span) -> bool
{
let tcx = rcx.fcx.tcx(); let tcx = rcx.fcx.tcx();
// Try to resolve the type. If we encounter an error, then typeck // Try to resolve the type. If we encounter an error, then typeck
// is going to fail anyway, so just stop here and let typeck // is going to fail anyway, so just stop here and let typeck
// report errors later on in the writeback phase. // 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(\ debug!("constrain_regions_in_type_of_node(\
ty=%s, id=%d, encl_region=%?)", ty=%s, ty0=%s, id=%d, encl_region=%?, adjustment=%?)",
ty_to_str(tcx, ty), id, encl_region); ty_to_str(tcx, ty), ty_to_str(tcx, ty0),
id, encl_region, adjustment);
constrain_regions_in_type(rcx, encl_region, span, ty) constrain_regions_in_type(rcx, encl_region, span, ty)
} }
pub fn constrain_regions_in_type( fn constrain_regions_in_type(
rcx: @mut Rcx, rcx: @mut Rcx,
encl_region: ty::Region, encl_region: ty::Region,
span: span, span: span,
ty: ty::t) -> bool { ty: ty::t) -> bool
{
/*! /*!
* *
* Requires that any regions which appear in `ty` must be * Requires that any regions which appear in `ty` must be
* superregions of `encl_region`. This prevents regions from * superregions of `encl_region`. Also enforces the constraint
* being used outside of the block in which they are valid. * that given a pointer type `&'r T`, T must not contain regions
* Recall that regions represent blocks of code or expressions: * that outlive 'r, as well as analogous constraints for other
* this requirement basically says "any place that uses or may use * lifetime'd types.
* a region R must be within the block of code that R corresponds *
* to." */ * 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; let e = rcx.errors_reported;
ty::walk_regions_and_ty( let tcx = rcx.fcx.ccx.tcx;
rcx.fcx.ccx.tcx, ty,
|r| constrain_region(rcx, encl_region, span, r),
|t| ty::type_has_regions(t));
return (e == rcx.errors_reported);
fn constrain_region(rcx: @mut Rcx, debug!("constrain_regions_in_type(encl_region=%s, ty=%s)",
encl_region: ty::Region, region_to_str(tcx, encl_region),
span: span, ty_to_str(tcx, ty));
region: ty::Region) {
let tcx = rcx.fcx.ccx.tcx;
debug!("constrain_region(encl_region=%?, region=%?)", do relate_nested_regions(tcx, Some(encl_region), ty) |r_sub, r_sup| {
encl_region, region); debug!("relate(r_sub=%s, r_sup=%s)",
region_to_str(tcx, r_sub),
region_to_str(tcx, r_sup));
match region { if r_sup.is_bound() || r_sub.is_bound() {
ty::re_bound(_) => {
// a bound region is one which appears inside an fn type. // a bound region is one which appears inside an fn type.
// (e.g., the `&` in `fn(&T)`). Such regions need not be // (e.g., the `&` in `fn(&T)`). Such regions need not be
// constrained by `encl_region` as they are placeholders // constrained by `encl_region` as they are placeholders
// for regions that are as-yet-unknown. // for regions that are as-yet-unknown.
return; } else {
} match rcx.fcx.mk_subr(true, span, r_sub, r_sup) {
_ => () result::Err(_) => {
} if r_sub == encl_region {
tcx.sess.span_err(
match rcx.fcx.mk_subr(true, span, encl_region, region) { span,
result::Err(_) => { fmt!("reference is not valid outside of its lifetime"));
tcx.sess.span_err( note_and_explain_region(
span, tcx,
fmt!("reference is not valid outside of its lifetime")); "the reference is only valid for ",
note_and_explain_region( r_sup,
tcx, "");
~"the reference is only valid for ", } else {
region, tcx.sess.span_err(
~""); span,
rcx.errors_reported += 1u; fmt!("in type `%s`, pointer has a longer lifetime than \
} the data it references",
result::Ok(()) => { 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 { pub mod guarantor {
@ -577,10 +741,12 @@ pub mod guarantor {
* region pointers. * 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 _i = ::util::common::indenter();
let mut expr_ct = categorize_unadjusted(rcx, expr); let mut expr_ct = categorize_unadjusted(rcx, expr);
debug!(" unadjusted cat=%?", expr_ct.cat);
expr_ct = apply_autoderefs( expr_ct = apply_autoderefs(
rcx, expr, autoderefs, expr_ct); rcx, expr, autoderefs, expr_ct);
@ -626,7 +792,7 @@ pub mod guarantor {
* to the lifetime of its guarantor (if any). * 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 { let bound = match guarantor {
None => { None => {
@ -860,8 +1026,6 @@ pub mod guarantor {
match closure_ty.sigil { match closure_ty.sigil {
ast::BorrowedSigil => BorrowedPointer(closure_ty.region), ast::BorrowedSigil => BorrowedPointer(closure_ty.region),
ast::OwnedSigil => OwnedPointer, ast::OwnedSigil => OwnedPointer,
// NOTE This is...not quite right. Deduce a test etc.
ast::ManagedSigil => OtherPointer, ast::ManagedSigil => OtherPointer,
} }
} }
@ -972,7 +1136,6 @@ pub fn infallibly_mk_subr(rcx: @mut Rcx,
a: ty::Region, a: ty::Region,
b: ty::Region) { b: ty::Region) {
/*! /*!
*
* Constrains `a` to be a subregion of `b`. In many cases, we * 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 * know that this can never yield an error due to the way that
* region inferencing works. Therefore just report a bug if we * region inferencing works. Therefore just report a bug if we

View file

@ -99,7 +99,7 @@ pub fn replace_bound_regions_in_fn_sig(
to_r: &fn(ty::bound_region) -> ty::Region, to_r: &fn(ty::bound_region) -> ty::Region,
r: ty::Region) -> isr_alist { r: ty::Region) -> isr_alist {
match r { match r {
ty::re_free(_, _) | ty::re_static | ty::re_scope(_) | ty::re_free(*) | ty::re_static | ty::re_scope(_) |
ty::re_infer(_) => { ty::re_infer(_) => {
isr isr
} }
@ -167,10 +167,125 @@ pub fn replace_bound_regions_in_fn_sig(
// Free regions like these just stay the same: // Free regions like these just stay the same:
ty::re_static | ty::re_static |
ty::re_scope(_) | ty::re_scope(_) |
ty::re_free(_, _) | ty::re_free(*) |
ty::re_infer(_) => r ty::re_infer(_) => r
}; };
r1 r1
} }
} }
} }
pub fn relate_nested_regions(
tcx: ty::ctxt,
opt_region: Option<ty::Region>,
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<ty::t>,
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");
}

View file

@ -535,7 +535,8 @@ pub fn compare_impl_method(tcx: ty::ctxt,
// a free region. So, for example, if the impl type is // a free region. So, for example, if the impl type is
// "&'self str", then this would replace the self type with a free // "&'self str", then this would replace the self type with a free
// region `self`. // 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); let self_ty = replace_bound_self(tcx, self_ty, dummy_self_r);
// Perform substitutions so that the trait/impl methods are expressed // Perform substitutions so that the trait/impl methods are expressed

View file

@ -538,10 +538,9 @@ more convincing in the future.
use core::prelude::*; use core::prelude::*;
use middle::region::is_subregion_of;
use middle::region;
use middle::ty; 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::ty::{re_scope, ReVar, ReSkolemized, br_fresh};
use middle::typeck::infer::cres; use middle::typeck::infer::cres;
use util::common::indenter; use util::common::indenter;
@ -554,6 +553,7 @@ use core::to_bytes;
use core::uint; use core::uint;
use core::vec; use core::vec;
use syntax::codemap::span; use syntax::codemap::span;
use syntax::ast;
#[deriving(Eq)] #[deriving(Eq)]
enum Constraint { enum Constraint {
@ -1025,11 +1025,12 @@ pub impl RegionVarBindings {
} }
priv impl RegionVarBindings { priv impl RegionVarBindings {
fn is_subregion_of(&mut self, sub: Region, sup: Region) -> bool { fn is_subregion_of(&self, sub: Region, sup: Region) -> bool {
is_subregion_of(self.tcx.region_map, sub, sup) 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) { match (a, b) {
(re_static, _) | (_, re_static) => { (re_static, _) | (_, re_static) => {
re_static // nothing lives longer than static re_static // nothing lives longer than static
@ -1042,17 +1043,17 @@ priv impl RegionVarBindings {
non-concrete regions: %?, %?", a, b)); non-concrete regions: %?, %?", a, b));
} }
(f @ re_free(f_id, _), re_scope(s_id)) | (f @ re_free(ref fr), re_scope(s_id)) |
(re_scope(s_id), f @ re_free(f_id, _)) => { (re_scope(s_id), f @ re_free(ref fr)) => {
// A "free" region can be interpreted as "some region // 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: // reasonably compare free regions and scopes:
let rm = self.tcx.region_map; let rm = self.tcx.region_maps;
match region::nearest_common_ancestor(rm, f_id, s_id) { match rm.nearest_common_ancestor(fr.scope_id, s_id) {
// if the free region's scope `f_id` is bigger than // if the free region's scope `fr.scope_id` is bigger than
// the scope region `s_id`, then the LUB is the free // the scope region `s_id`, then the LUB is the free
// region itself: // 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, // otherwise, we don't know what the free region is,
// so we must conservatively say the LUB is static: // 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 // The region corresponding to an outer block is a
// subtype of the region corresponding to an inner // subtype of the region corresponding to an inner
// block. // block.
let rm = self.tcx.region_map; let rm = self.tcx.region_maps;
match region::nearest_common_ancestor(rm, a_id, b_id) { match rm.nearest_common_ancestor(a_id, b_id) {
Some(r_id) => re_scope(r_id), Some(r_id) => re_scope(r_id),
_ => re_static _ => 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 // For these types, we cannot define any additional
// relationship: // relationship:
(re_infer(ReSkolemized(*)), _) | (re_infer(ReSkolemized(*)), _) |
(_, re_infer(ReSkolemized(*))) | (_, re_infer(ReSkolemized(*))) |
(re_free(_, _), re_free(_, _)) |
(re_bound(_), re_bound(_)) | (re_bound(_), re_bound(_)) |
(re_bound(_), re_free(_, _)) | (re_bound(_), re_free(_)) |
(re_bound(_), re_scope(_)) | (re_bound(_), re_scope(_)) |
(re_free(_, _), re_bound(_)) | (re_free(_), re_bound(_)) |
(re_scope(_), re_bound(_)) => { (re_scope(_), re_bound(_)) => {
if a == b {a} else {re_static} 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, +a: Region,
+b: Region) +b: Region)
-> cres<Region> { -> cres<Region> {
debug!("glb_concrete_regions(%?, %?)", a, b);
match (a, b) { match (a, b) {
(re_static, r) | (r, re_static) => { (re_static, r) | (r, re_static) => {
// static lives longer than everything else // static lives longer than everything else
@ -1104,37 +1140,26 @@ priv impl RegionVarBindings {
non-concrete regions: %?, %?", a, b)); non-concrete regions: %?, %?", a, b));
} }
(re_free(f_id, _), s @ re_scope(s_id)) | (re_free(ref fr), s @ re_scope(s_id)) |
(s @ re_scope(s_id), re_free(f_id, _)) => { (s @ re_scope(s_id), re_free(ref fr)) => {
// Free region is something "at least as big as // 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 // than the scope `s_id`, then we can say that the GLB
// is the scope `s_id`. Otherwise, as we do not know // is the scope `s_id`. Otherwise, as we do not know
// big the free region is precisely, the GLB is undefined. // big the free region is precisely, the GLB is undefined.
let rm = self.tcx.region_map; let rm = self.tcx.region_maps;
match region::nearest_common_ancestor(rm, f_id, s_id) { match rm.nearest_common_ancestor(fr.scope_id, s_id) {
Some(r_id) if r_id == f_id => Ok(s), Some(r_id) if r_id == fr.scope_id => Ok(s),
_ => Err(ty::terr_regions_no_overlap(b, a)) _ => Err(ty::terr_regions_no_overlap(b, a))
} }
} }
(re_scope(a_id), re_scope(b_id)) | (re_scope(a_id), re_scope(b_id)) => {
(re_free(a_id, _), re_free(b_id, _)) => { self.intersect_scopes(a, b, a_id, b_id)
if a == b { }
// Same scope or same free identifier, easy case.
Ok(a) (re_free(ref a_fr), re_free(ref b_fr)) => {
} else { self.glb_free_regions(a_fr, b_fr)
// 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))
}
}
} }
// For these types, we cannot define any additional // For these types, we cannot define any additional
@ -1142,9 +1167,9 @@ priv impl RegionVarBindings {
(re_infer(ReSkolemized(*)), _) | (re_infer(ReSkolemized(*)), _) |
(_, re_infer(ReSkolemized(*))) | (_, re_infer(ReSkolemized(*))) |
(re_bound(_), re_bound(_)) | (re_bound(_), re_bound(_)) |
(re_bound(_), re_free(_, _)) | (re_bound(_), re_free(_)) |
(re_bound(_), re_scope(_)) | (re_bound(_), re_scope(_)) |
(re_free(_, _), re_bound(_)) | (re_free(_), re_bound(_)) |
(re_scope(_), re_bound(_)) => { (re_scope(_), re_bound(_)) => {
if a == b { if a == b {
Ok(a) Ok(a)
@ -1155,10 +1180,62 @@ priv impl RegionVarBindings {
} }
} }
fn glb_free_regions(&self,
a: &FreeRegion,
b: &FreeRegion) -> cres<ty::Region>
{
/*!
* 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<ty::Region>
{
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) { fn report_type_error(&mut self, span: span, terr: &ty::type_err) {
let terr_str = ty::type_err_to_str(self.tcx, terr); let terr_str = ty::type_err_to_str(self.tcx, terr);
self.tcx.sess.span_err(span, terr_str); 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<Region>
{
// 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))
}
}
} }
// ______________________________________________________________________ // ______________________________________________________________________

View file

@ -210,7 +210,9 @@ pub impl Env {
} }
fn t_rptr_free(&self, nid: ast::node_id, id: uint) -> ty::t { 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()) self.t_int())
} }

View file

@ -41,9 +41,9 @@ pub trait Repr {
} }
pub fn note_and_explain_region(cx: ctxt, pub fn note_and_explain_region(cx: ctxt,
prefix: ~str, prefix: &str,
region: ty::Region, region: ty::Region,
suffix: ~str) { suffix: &str) {
match explain_region_and_span(cx, region) { match explain_region_and_span(cx, region) {
(ref str, Some(span)) => { (ref str, Some(span)) => {
cx.sess.span_note( cx.sess.span_note(
@ -98,23 +98,23 @@ pub fn explain_region_and_span(cx: ctxt, region: ty::Region)
} }
} }
re_free(id, br) => { re_free(ref fr) => {
let prefix = match br { let prefix = match fr.bound_region {
br_anon(idx) => fmt!("the anonymous lifetime #%u defined on", br_anon(idx) => fmt!("the anonymous lifetime #%u defined on",
idx + 1), idx + 1),
br_fresh(_) => fmt!("an anonymous lifetime defined on"), br_fresh(_) => fmt!("an anonymous lifetime defined on"),
_ => fmt!("the lifetime %s as 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)) => { Some(&ast_map::node_block(ref blk)) => {
let (msg, opt_span) = explain_span(cx, "block", blk.span); let (msg, opt_span) = explain_span(cx, "block", blk.span);
(fmt!("%s %s", prefix, msg), opt_span) (fmt!("%s %s", prefix, msg), opt_span)
} }
Some(_) | None => { Some(_) | None => {
// this really should not happen // 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 { match region {
re_scope(_) => prefix.to_str(), re_scope(_) => prefix.to_str(),
re_bound(br) => bound_region_to_str_space(cx, prefix, br), 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)) => { re_infer(ReSkolemized(_, br)) => {
bound_region_to_str_space(cx, prefix, 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 { 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 { let mstr = match m.mutbl {
ast::m_mutbl => "mut ", ast::m_mutbl => "mut ",
ast::m_imm => "", ast::m_imm => "",
ast::m_const => "const " 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 { 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 { match vs {
ty::vstore_fixed(_) => { ty::vstore_fixed(_) => {
fmt!("[%s, .. %s]", ty, vstore_to_str(cx, vs)) fmt!("[%s, .. %s]", mt_to_str(cx, mt), 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), mt_to_str_wrapped(cx, "[", mt, "]"))
} }
_ => fmt!("%s[%s]", vstore_to_str(cx, vs), ty)
} }
} }
@ -460,7 +463,7 @@ pub fn ty_to_str(cx: ctxt, typ: t) -> ~str {
fmt!("%s%s", trait_store_to_str(cx, s), ty) fmt!("%s%s", trait_store_to_str(cx, s), ty)
} }
ty_evec(ref mt, vs) => { 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_estr(vs) => fmt!("%s%s", vstore_to_str(cx, vs), ~"str"),
ty_opaque_box => ~"@?", ty_opaque_box => ~"@?",

View file

@ -27,5 +27,6 @@ fn main() {
let x: &'blk int = &3; let x: &'blk int = &3;
repeater(@x) 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
} }

View file

@ -24,9 +24,9 @@ fn with<R:deref>(f: &fn(x: &int) -> R) -> int {
fn return_it() -> int { fn return_it() -> int {
with(|o| o) 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 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() { fn main() {

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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() {}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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() {}

View file

@ -8,6 +8,11 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
// xfail-test #5723
// Test that you cannot escape a borrowed pointer
// into a trait.
struct ctxt { v: uint } struct ctxt { v: uint }
trait get_ctxt { trait get_ctxt {
@ -24,8 +29,9 @@ fn make_gc() -> @get_ctxt {
let ctxt = ctxt { v: 22u }; let ctxt = ctxt { v: 22u };
let hc = has_ctxt { c: &ctxt }; let hc = has_ctxt { c: &ctxt };
return @hc as @get_ctxt; return @hc as @get_ctxt;
//^~ ERROR source contains borrowed pointer
} }
fn main() { fn main() {
make_gc().get_ctxt().v; //~ ERROR illegal borrow make_gc().get_ctxt().v;
} }