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:
parent
5606fc0c90
commit
3322595e89
@ -116,6 +116,19 @@ totalord_impl!(i64)
|
||||
totalord_impl!(int)
|
||||
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.
|
||||
*
|
||||
@ -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));
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -161,7 +161,6 @@ impl<A:Ord> Ord for (A,) {
|
||||
fn gt(&self, other: &(A,)) -> bool { other.lt(&(*self)) }
|
||||
}
|
||||
|
||||
|
||||
#[cfg(notest)]
|
||||
impl<A:Eq,B:Eq> Eq for (A, B) {
|
||||
#[inline(always)]
|
||||
|
@ -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);
|
||||
|
@ -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) => {
|
||||
|
@ -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)})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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) => {
|
||||
|
@ -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 {
|
||||
|
@ -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(*)) |
|
||||
|
@ -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| {
|
||||
|
@ -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.
|
||||
|
@ -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<ast::node_id>;
|
||||
|
||||
/**
|
||||
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<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 {
|
||||
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<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
|
||||
/// 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<ast::node_id> {
|
||||
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<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);
|
||||
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;
|
||||
}
|
||||
|
||||
// ___________________________________________________________________________
|
||||
|
@ -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<mt> {
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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::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");
|
||||
}
|
@ -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
|
||||
|
@ -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<Region> {
|
||||
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<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) {
|
||||
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<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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ______________________________________________________________________
|
||||
|
@ -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())
|
||||
}
|
||||
|
||||
|
@ -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 => ~"@?",
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -24,9 +24,9 @@ fn with<R:deref>(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() {
|
||||
|
37
src/test/compile-fail/regions-free-region-ordering-callee.rs
Normal file
37
src/test/compile-fail/regions-free-region-ordering-callee.rs
Normal 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() {}
|
40
src/test/compile-fail/regions-free-region-ordering-caller.rs
Normal file
40
src/test/compile-fail/regions-free-region-ordering-caller.rs
Normal 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() {}
|
@ -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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user