Added DestructionScope variant to CodeExtent, representing the area

immediately surrounding a node that is a terminating_scope
(e.g. statements, looping forms) during which the destructors run (the
destructors for temporaries from the execution of that node, that is).

Introduced DestructionScopeData newtype wrapper around ast::NodeId, to
preserve invariant that FreeRegion and ScopeChain::BlockScope carry
destruction scopes (rather than arbitrary CodeExtents).

Insert DestructionScope and block Remainder into enclosing CodeExtents
hierarchy.

Add more doc for DestructionScope, complete with ASCII art.

Switch to constructing DestructionScope rather than Misc in a number
of places, mostly related to `ty::ReFree` creation, and use
destruction-scopes of node-ids at various calls to
liberate_late_bound_regions.

middle::resolve_lifetime: Map BlockScope to DestructionScope in `fn resolve_free_lifetime`.

Add the InnermostDeclaringBlock and InnermostEnclosingExpr enums that
are my attempt to clarify the region::Context structure, and that
later commmts build upon.

Improve the debug output for `CodeExtent` attached to `ty::Region::ReScope`.

Loosened an assertion in `rustc_trans::trans::cleanup` to account for
`DestructionScope`.  (Perhaps this should just be switched entirely
over to `DestructionScope`, rather than allowing for either `Misc` or
`DestructionScope`.)

----

Even though the DestructionScope is new, this particular commit should
not actually change the semantics of any current code.
This commit is contained in:
Felix S. Klock II 2014-11-25 17:02:20 +01:00
parent bdb9f3e266
commit 81383bd869
18 changed files with 253 additions and 73 deletions

View File

@ -349,7 +349,7 @@ fn parse_region_<F>(st: &mut PState, conv: &mut F) -> ty::Region where
} }
'f' => { 'f' => {
assert_eq!(next(st), '['); assert_eq!(next(st), '[');
let scope = parse_scope(st); let scope = parse_destruction_scope_data(st);
assert_eq!(next(st), '|'); assert_eq!(next(st), '|');
let br = parse_bound_region_(st, conv); let br = parse_bound_region_(st, conv);
assert_eq!(next(st), ']'); assert_eq!(next(st), ']');
@ -377,6 +377,10 @@ fn parse_scope(st: &mut PState) -> region::CodeExtent {
let node_id = parse_uint(st) as ast::NodeId; let node_id = parse_uint(st) as ast::NodeId;
region::CodeExtent::Misc(node_id) region::CodeExtent::Misc(node_id)
} }
'D' => {
let node_id = parse_uint(st) as ast::NodeId;
region::CodeExtent::DestructionScope(node_id)
}
'B' => { 'B' => {
let node_id = parse_uint(st) as ast::NodeId; let node_id = parse_uint(st) as ast::NodeId;
let first_stmt_index = parse_uint(st); let first_stmt_index = parse_uint(st);
@ -389,6 +393,11 @@ fn parse_scope(st: &mut PState) -> region::CodeExtent {
} }
} }
fn parse_destruction_scope_data(st: &mut PState) -> region::DestructionScopeData {
let node_id = parse_uint(st) as ast::NodeId;
region::DestructionScopeData::new(node_id)
}
fn parse_opt<'a, 'tcx, T, F>(st: &mut PState<'a, 'tcx>, f: F) -> Option<T> where fn parse_opt<'a, 'tcx, T, F>(st: &mut PState<'a, 'tcx>, f: F) -> Option<T> where
F: FnOnce(&mut PState<'a, 'tcx>) -> T, F: FnOnce(&mut PState<'a, 'tcx>) -> T,
{ {

View File

@ -251,7 +251,7 @@ pub fn enc_region(w: &mut SeekableMemWriter, cx: &ctxt, r: ty::Region) {
} }
ty::ReFree(ref fr) => { ty::ReFree(ref fr) => {
mywrite!(w, "f["); mywrite!(w, "f[");
enc_scope(w, cx, fr.scope); enc_destruction_scope_data(w, fr.scope);
mywrite!(w, "|"); mywrite!(w, "|");
enc_bound_region(w, cx, fr.bound_region); enc_bound_region(w, cx, fr.bound_region);
mywrite!(w, "]"); mywrite!(w, "]");
@ -279,9 +279,15 @@ fn enc_scope(w: &mut SeekableMemWriter, _cx: &ctxt, scope: region::CodeExtent) {
region::CodeExtent::Misc(node_id) => mywrite!(w, "M{}", node_id), region::CodeExtent::Misc(node_id) => mywrite!(w, "M{}", node_id),
region::CodeExtent::Remainder(region::BlockRemainder { region::CodeExtent::Remainder(region::BlockRemainder {
block: b, first_statement_index: i }) => mywrite!(w, "B{}{}", b, i), block: b, first_statement_index: i }) => mywrite!(w, "B{}{}", b, i),
region::CodeExtent::DestructionScope(node_id) => mywrite!(w, "D{}", node_id),
} }
} }
fn enc_destruction_scope_data(w: &mut SeekableMemWriter,
d: region::DestructionScopeData) {
mywrite!(w, "{}", d.node_id);
}
fn enc_bound_region(w: &mut SeekableMemWriter, cx: &ctxt, br: ty::BoundRegion) { fn enc_bound_region(w: &mut SeekableMemWriter, cx: &ctxt, br: ty::BoundRegion) {
match br { match br {
ty::BrAnon(idx) => { ty::BrAnon(idx) => {

View File

@ -499,6 +499,12 @@ impl tr for region::CodeExtent {
} }
} }
impl tr for region::DestructionScopeData {
fn tr(&self, dcx: &DecodeContext) -> region::DestructionScopeData {
region::DestructionScopeData { node_id: dcx.tr_id(self.node_id) }
}
}
impl tr for ty::BoundRegion { impl tr for ty::BoundRegion {
fn tr(&self, dcx: &DecodeContext) -> ty::BoundRegion { fn tr(&self, dcx: &DecodeContext) -> ty::BoundRegion {
match *self { match *self {

View File

@ -304,7 +304,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> {
return None return None
} }
assert!(fr1.scope == fr2.scope); assert!(fr1.scope == fr2.scope);
(fr1.scope.node_id(), fr1, fr2) (fr1.scope.node_id, fr1, fr2)
}, },
_ => return None _ => return None
}; };

View File

@ -760,11 +760,12 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
// 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 fr.scope_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:
match self.tcx.region_maps.nearest_common_ancestor(fr.scope, s_id) { let fr_scope = fr.scope.to_code_extent();
match self.tcx.region_maps.nearest_common_ancestor(fr_scope, s_id) {
// if the free region's scope `fr.scope_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 == fr.scope => f, Some(r_id) if r_id == fr_scope => 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:
@ -865,8 +866,9 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
// 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.
match self.tcx.region_maps.nearest_common_ancestor(fr.scope, s_id) { let fr_scope = fr.scope.to_code_extent();
Some(r_id) if r_id == fr.scope => Ok(s), match self.tcx.region_maps.nearest_common_ancestor(fr_scope, s_id) {
Some(r_id) if r_id == fr_scope => Ok(s),
_ => Err(ty::terr_regions_no_overlap(b, a)) _ => Err(ty::terr_regions_no_overlap(b, a))
} }
} }
@ -915,7 +917,8 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
Ok(ty::ReFree(*b)) Ok(ty::ReFree(*b))
} else { } else {
this.intersect_scopes(ty::ReFree(*a), ty::ReFree(*b), this.intersect_scopes(ty::ReFree(*a), ty::ReFree(*b),
a.scope, b.scope) a.scope.to_code_extent(),
b.scope.to_code_extent())
} }
} }
} }

View File

@ -112,7 +112,7 @@ use self::VarKind::*;
use middle::def::*; use middle::def::*;
use middle::mem_categorization::Typer; use middle::mem_categorization::Typer;
use middle::pat_util; use middle::pat_util;
use middle::region::CodeExtent; use middle::region;
use middle::ty; use middle::ty;
use middle::ty::ClosureTyper; use middle::ty::ClosureTyper;
use lint; use lint;
@ -1514,7 +1514,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
let fn_ret = let fn_ret =
ty::liberate_late_bound_regions( ty::liberate_late_bound_regions(
self.ir.tcx, self.ir.tcx,
CodeExtent::from_node_id(body.id), region::DestructionScopeData::new(body.id),
&self.fn_ret(id)); &self.fn_ret(id));
match fn_ret { match fn_ret {

View File

@ -760,7 +760,10 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
// Region of environment pointer // Region of environment pointer
let env_region = ty::ReFree(ty::FreeRegion { let env_region = ty::ReFree(ty::FreeRegion {
scope: region::CodeExtent::from_node_id(fn_body_id), // The environment of a closure is guaranteed to
// outlive any bindings introduced in the body of the
// closure itself.
scope: region::DestructionScopeData::new(fn_body_id),
bound_region: ty::BrEnv bound_region: ty::BrEnv
}); });

View File

@ -27,11 +27,66 @@ use syntax::{ast, visit};
use syntax::ast::{Block, Item, FnDecl, NodeId, Arm, Pat, Stmt, Expr, Local}; use syntax::ast::{Block, Item, FnDecl, NodeId, Arm, Pat, Stmt, Expr, Local};
use syntax::ast_util::{stmt_id}; use syntax::ast_util::{stmt_id};
use syntax::ast_map; use syntax::ast_map;
use syntax::ptr::P;
use syntax::visit::{Visitor, FnKind}; use syntax::visit::{Visitor, FnKind};
/// CodeExtent represents a statically-describable extent that can be /// CodeExtent represents a statically-describable extent that can be
/// used to bound the lifetime/region for values. /// used to bound the lifetime/region for values.
/// ///
/// `Misc(node_id)`: Any AST node that has any extent at all has the
/// `Misc(node_id)` extent. Other variants represent special cases not
/// immediately derivable from the abstract syntax tree structure.
///
/// `DestructionScope(node_id)` represents the extent of destructors
/// implicitly-attached to `node_id` that run immediately after the
/// expression for `node_id` itself. Not every AST node carries a
/// `DestructionScope`, but those that are `terminating_scopes` do;
/// see discussion with `RegionMaps`.
///
/// `Remainder(BlockRemainder { block, statement_index })` represents
/// the extent of user code running immediately after the initializer
/// expression for the indexed statement, until the end of the block.
///
/// So: the following code can be broken down into the extents beneath:
/// ```
/// let a = f().g( 'b: { let x = d(); let y = d(); x.h(y) } ) ;
/// ```
///
/// +-+ (D12.)
/// +-+ (D11.)
/// +---------+ (R10.)
/// +-+ (D9.)
/// +----------+ (M8.)
/// +----------------------+ (R7.)
/// +-+ (D6.)
/// +----------+ (M5.)
/// +-----------------------------------+ (M4.)
/// +--------------------------------------------------+ (M3.)
/// +--+ (M2.)
/// +-----------------------------------------------------------+ (M1.)
///
/// (M1.): Misc extent of the whole `let a = ...;` statement.
/// (M2.): Misc extent of the `f()` expression.
/// (M3.): Misc extent of the `f().g(..)` expression.
/// (M4.): Misc extent of the block labelled `'b:`.
/// (M5.): Misc extent of the `let x = d();` statement
/// (D6.): DestructionScope for temporaries created during M5.
/// (R7.): Remainder extent for block `'b:`, stmt 0 (let x = ...).
/// (M8.): Misc Extent of the `let y = d();` statement.
/// (D9.): DestructionScope for temporaries created during M8.
/// (R10.): Remainder extent for block `'b:`, stmt 1 (let y = ...).
/// (D11.): DestructionScope for temporaries and bindings from block `'b:`.
/// (D12.): DestructionScope for temporaries created during M1 (e.g. f()).
///
/// Note that while the above picture shows the destruction scopes
/// as following their corresponding misc extents, in the internal
/// data structures of the compiler the destruction scopes are
/// represented as enclosing parents. This is sound because we use the
/// enclosing parent relationship just to ensure that referenced
/// values live long enough; phrased another way, the starting point
/// of each range is not really the important thing in the above
/// picture, but rather the ending point.
///
/// FIXME (pnkfelix): This currently derives `PartialOrd` and `Ord` to /// FIXME (pnkfelix): This currently derives `PartialOrd` and `Ord` to
/// placate the same deriving in `ty::FreeRegion`, but we may want to /// placate the same deriving in `ty::FreeRegion`, but we may want to
/// actually attach a more meaningful ordering to scopes than the one /// actually attach a more meaningful ordering to scopes than the one
@ -40,7 +95,24 @@ use syntax::visit::{Visitor, FnKind};
RustcDecodable, Debug, Copy)] RustcDecodable, Debug, Copy)]
pub enum CodeExtent { pub enum CodeExtent {
Misc(ast::NodeId), Misc(ast::NodeId),
Remainder(BlockRemainder), DestructionScope(ast::NodeId), // extent of destructors for temporaries of node-id
Remainder(BlockRemainder)
}
/// extent of destructors for temporaries of node-id
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, RustcEncodable,
RustcDecodable, Debug, Copy)]
pub struct DestructionScopeData {
pub node_id: ast::NodeId
}
impl DestructionScopeData {
pub fn new(node_id: ast::NodeId) -> DestructionScopeData {
DestructionScopeData { node_id: node_id }
}
pub fn to_code_extent(&self) -> CodeExtent {
CodeExtent::DestructionScope(self.node_id)
}
} }
/// Represents a subscope of `block` for a binding that is introduced /// Represents a subscope of `block` for a binding that is introduced
@ -82,6 +154,7 @@ impl CodeExtent {
match *self { match *self {
CodeExtent::Misc(node_id) => node_id, CodeExtent::Misc(node_id) => node_id,
CodeExtent::Remainder(br) => br.block, CodeExtent::Remainder(br) => br.block,
CodeExtent::DestructionScope(node_id) => node_id,
} }
} }
@ -95,6 +168,8 @@ impl CodeExtent {
CodeExtent::Remainder(br) => CodeExtent::Remainder(br) =>
CodeExtent::Remainder(BlockRemainder { CodeExtent::Remainder(BlockRemainder {
block: f_id(br.block), first_statement_index: br.first_statement_index }), block: f_id(br.block), first_statement_index: br.first_statement_index }),
CodeExtent::DestructionScope(node_id) =>
CodeExtent::DestructionScope(f_id(node_id)),
} }
} }
@ -105,7 +180,8 @@ impl CodeExtent {
match ast_map.find(self.node_id()) { match ast_map.find(self.node_id()) {
Some(ast_map::NodeBlock(ref blk)) => { Some(ast_map::NodeBlock(ref blk)) => {
match *self { match *self {
CodeExtent::Misc(_) => Some(blk.span), CodeExtent::Misc(_) |
CodeExtent::DestructionScope(_) => Some(blk.span),
CodeExtent::Remainder(r) => { CodeExtent::Remainder(r) => {
assert_eq!(r.block, blk.id); assert_eq!(r.block, blk.id);
@ -455,7 +531,7 @@ impl RegionMaps {
} }
(ty::ReScope(sub_scope), ty::ReFree(ref fr)) => { (ty::ReScope(sub_scope), ty::ReFree(ref fr)) => {
self.is_subscope_of(sub_scope, fr.scope) self.is_subscope_of(sub_scope, fr.scope.to_code_extent())
} }
(ty::ReFree(sub_fr), ty::ReFree(super_fr)) => { (ty::ReFree(sub_fr), ty::ReFree(super_fr)) => {
@ -567,7 +643,18 @@ fn resolve_block(visitor: &mut RegionResolutionVisitor, blk: &ast::Block) {
let prev_cx = visitor.cx; let prev_cx = visitor.cx;
let blk_scope = CodeExtent::Misc(blk.id); let blk_scope = CodeExtent::Misc(blk.id);
record_superlifetime(visitor, blk_scope, blk.span); // If block was previously marked as a terminating scope during
// the recursive visit of its parent node in the AST, then we need
// to account for the destruction scope representing the extent of
// the destructors that run immediately after the the block itself
// completes.
if visitor.region_maps.terminating_scopes.borrow().contains(&blk_scope) {
let dtor_scope = CodeExtent::DestructionScope(blk.id);
record_superlifetime(visitor, dtor_scope, blk.span);
visitor.region_maps.record_encl_scope(blk_scope, dtor_scope);
} else {
record_superlifetime(visitor, blk_scope, blk.span);
}
// We treat the tail expression in the block (if any) somewhat // We treat the tail expression in the block (if any) somewhat
// differently from the statements. The issue has to do with // differently from the statements. The issue has to do with
@ -675,7 +762,9 @@ fn resolve_stmt(visitor: &mut RegionResolutionVisitor, stmt: &ast::Stmt) {
// statement plus its destructors, and thus the extent for which // statement plus its destructors, and thus the extent for which
// regions referenced by the destructors need to survive. // regions referenced by the destructors need to survive.
visitor.region_maps.mark_as_terminating_scope(stmt_scope); visitor.region_maps.mark_as_terminating_scope(stmt_scope);
record_superlifetime(visitor, stmt_scope, stmt.span); let dtor_scope = CodeExtent::DestructionScope(stmt_id);
visitor.region_maps.record_encl_scope(stmt_scope, dtor_scope);
record_superlifetime(visitor, dtor_scope, stmt.span);
let prev_parent = visitor.cx.parent; let prev_parent = visitor.cx.parent;
visitor.cx.parent = InnermostEnclosingExpr::Some(stmt_id); visitor.cx.parent = InnermostEnclosingExpr::Some(stmt_id);
@ -687,15 +776,30 @@ fn resolve_expr(visitor: &mut RegionResolutionVisitor, expr: &ast::Expr) {
debug!("resolve_expr(expr.id={:?})", expr.id); debug!("resolve_expr(expr.id={:?})", expr.id);
let expr_scope = CodeExtent::Misc(expr.id); let expr_scope = CodeExtent::Misc(expr.id);
record_superlifetime(visitor, expr_scope, expr.span); // If expr was previously marked as a terminating scope during the
// recursive visit of its parent node in the AST, then we need to
// account for the destruction scope representing the extent of
// the destructors that run immediately after the the expression
// itself completes.
if visitor.region_maps.terminating_scopes.borrow().contains(&expr_scope) {
let dtor_scope = CodeExtent::DestructionScope(expr.id);
record_superlifetime(visitor, dtor_scope, expr.span);
visitor.region_maps.record_encl_scope(expr_scope, dtor_scope);
} else {
record_superlifetime(visitor, expr_scope, expr.span);
}
let prev_cx = visitor.cx; let prev_cx = visitor.cx;
visitor.cx.parent = InnermostEnclosingExpr::Some(expr.id); visitor.cx.parent = InnermostEnclosingExpr::Some(expr.id);
{ {
let region_maps = &mut visitor.region_maps; let region_maps = &mut visitor.region_maps;
let terminating = |id| { let terminating = |e: &P<ast::Expr>| {
let scope = CodeExtent::from_node_id(id); let scope = CodeExtent::from_node_id(e.id);
region_maps.mark_as_terminating_scope(scope)
};
let terminating_block = |b: &P<ast::Block>| {
let scope = CodeExtent::from_node_id(b.id);
region_maps.mark_as_terminating_scope(scope) region_maps.mark_as_terminating_scope(scope)
}; };
match expr.node { match expr.node {
@ -707,26 +811,26 @@ fn resolve_expr(visitor: &mut RegionResolutionVisitor, expr: &ast::Expr) {
ast::ExprBinary(codemap::Spanned { node: ast::BiOr, .. }, _, ref r) => { ast::ExprBinary(codemap::Spanned { node: ast::BiOr, .. }, _, ref r) => {
// For shortcircuiting operators, mark the RHS as a terminating // For shortcircuiting operators, mark the RHS as a terminating
// scope since it only executes conditionally. // scope since it only executes conditionally.
terminating(r.id); terminating(r);
} }
ast::ExprIf(_, ref then, Some(ref otherwise)) => { ast::ExprIf(_, ref then, Some(ref otherwise)) => {
terminating(then.id); terminating_block(then);
terminating(otherwise.id); terminating(otherwise);
} }
ast::ExprIf(ref expr, ref then, None) => { ast::ExprIf(ref expr, ref then, None) => {
terminating(expr.id); terminating(expr);
terminating(then.id); terminating_block(then);
} }
ast::ExprLoop(ref body, _) => { ast::ExprLoop(ref body, _) => {
terminating(body.id); terminating_block(body);
} }
ast::ExprWhile(ref expr, ref body, _) => { ast::ExprWhile(ref expr, ref body, _) => {
terminating(expr.id); terminating(expr);
terminating(body.id); terminating_block(body);
} }
ast::ExprMatch(..) => { ast::ExprMatch(..) => {
@ -1021,6 +1125,9 @@ fn resolve_fn(visitor: &mut RegionResolutionVisitor,
let body_scope = CodeExtent::from_node_id(body.id); let body_scope = CodeExtent::from_node_id(body.id);
visitor.region_maps.mark_as_terminating_scope(body_scope); visitor.region_maps.mark_as_terminating_scope(body_scope);
let dtor_scope = CodeExtent::DestructionScope(body.id);
visitor.region_maps.record_encl_scope(body_scope, dtor_scope);
record_superlifetime(visitor, dtor_scope, body.span);
let outer_cx = visitor.cx; let outer_cx = visitor.cx;

View File

@ -41,7 +41,7 @@ pub enum DefRegion {
/* lifetime decl */ ast::NodeId), /* lifetime decl */ ast::NodeId),
DefLateBoundRegion(ty::DebruijnIndex, DefLateBoundRegion(ty::DebruijnIndex,
/* lifetime decl */ ast::NodeId), /* lifetime decl */ ast::NodeId),
DefFreeRegion(/* block scope */ region::CodeExtent, DefFreeRegion(/* block scope */ region::DestructionScopeData,
/* lifetime decl */ ast::NodeId), /* lifetime decl */ ast::NodeId),
} }
@ -81,7 +81,7 @@ enum ScopeChain<'a> {
LateScope(&'a Vec<ast::LifetimeDef>, Scope<'a>), LateScope(&'a Vec<ast::LifetimeDef>, Scope<'a>),
/// lifetimes introduced by items within a code block are scoped /// lifetimes introduced by items within a code block are scoped
/// to that block. /// to that block.
BlockScope(region::CodeExtent, Scope<'a>), BlockScope(region::DestructionScopeData, Scope<'a>),
RootScope RootScope
} }
@ -191,7 +191,8 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> {
} }
fn visit_block(&mut self, b: &ast::Block) { fn visit_block(&mut self, b: &ast::Block) {
self.with(BlockScope(region::CodeExtent::from_node_id(b.id), self.scope), self.with(BlockScope(region::DestructionScopeData::new(b.id),
self.scope),
|_, this| visit::walk_block(this, b)); |_, this| visit::walk_block(this, b));
} }
@ -393,9 +394,13 @@ impl<'a> LifetimeContext<'a> {
} }
fn resolve_free_lifetime_ref(&mut self, fn resolve_free_lifetime_ref(&mut self,
scope_data: region::CodeExtent, scope_data: region::DestructionScopeData,
lifetime_ref: &ast::Lifetime, lifetime_ref: &ast::Lifetime,
scope: Scope) { scope: Scope) {
debug!("resolve_free_lifetime_ref \
scope_data: {:?} lifetime_ref: {:?} scope: {:?}",
scope_data, lifetime_ref, scope);
// Walk up the scope chain, tracking the outermost free scope, // Walk up the scope chain, tracking the outermost free scope,
// until we encounter a scope that contains the named lifetime // until we encounter a scope that contains the named lifetime
// or we run out of scopes. // or we run out of scopes.
@ -403,6 +408,9 @@ impl<'a> LifetimeContext<'a> {
let mut scope = scope; let mut scope = scope;
let mut search_result = None; let mut search_result = None;
loop { loop {
debug!("resolve_free_lifetime_ref \
scope_data: {:?} scope: {:?} search_result: {:?}",
scope_data, scope, search_result);
match *scope { match *scope {
BlockScope(blk_scope_data, s) => { BlockScope(blk_scope_data, s) => {
scope_data = blk_scope_data; scope_data = blk_scope_data;

View File

@ -1174,7 +1174,9 @@ pub enum Region {
/// region parameters. /// region parameters.
ReFree(FreeRegion), ReFree(FreeRegion),
/// A concrete region naming some expression within the current function. /// A concrete region naming some statically determined extent
/// (e.g. an expression or sequence of statements) within the
/// current function.
ReScope(region::CodeExtent), ReScope(region::CodeExtent),
/// Static data that has an "infinite" lifetime. Top in the region lattice. /// Static data that has an "infinite" lifetime. Top in the region lattice.
@ -1296,7 +1298,7 @@ impl Region {
/// A "free" region `fr` can be interpreted as "some region /// A "free" region `fr` can be interpreted as "some region
/// at least as big as the scope `fr.scope`". /// at least as big as the scope `fr.scope`".
pub struct FreeRegion { pub struct FreeRegion {
pub scope: region::CodeExtent, pub scope: region::DestructionScopeData,
pub bound_region: BoundRegion pub bound_region: BoundRegion
} }
@ -4192,12 +4194,16 @@ pub fn ty_region(tcx: &ctxt,
} }
} }
pub fn free_region_from_def(free_id: ast::NodeId, def: &RegionParameterDef) pub fn free_region_from_def(outlives_extent: region::DestructionScopeData,
def: &RegionParameterDef)
-> ty::Region -> ty::Region
{ {
ty::ReFree(ty::FreeRegion { scope: region::CodeExtent::from_node_id(free_id), let ret =
bound_region: ty::BrNamed(def.def_id, ty::ReFree(ty::FreeRegion { scope: outlives_extent,
def.name) }) bound_region: ty::BrNamed(def.def_id,
def.name) });
debug!("free_region_from_def returns {:?}", ret);
ret
} }
// Returns the type of a pattern as a monotype. Like @expr_ty, this function // Returns the type of a pattern as a monotype. Like @expr_ty, this function
@ -6252,9 +6258,11 @@ pub fn construct_free_substs<'a,'tcx>(
let mut types = VecPerParamSpace::empty(); let mut types = VecPerParamSpace::empty();
push_types_from_defs(tcx, &mut types, generics.types.as_slice()); push_types_from_defs(tcx, &mut types, generics.types.as_slice());
let free_id_outlive = region::DestructionScopeData::new(free_id);
// map bound 'a => free 'a // map bound 'a => free 'a
let mut regions = VecPerParamSpace::empty(); let mut regions = VecPerParamSpace::empty();
push_region_params(&mut regions, free_id, generics.regions.as_slice()); push_region_params(&mut regions, free_id_outlive, generics.regions.as_slice());
return Substs { return Substs {
types: types, types: types,
@ -6262,11 +6270,11 @@ pub fn construct_free_substs<'a,'tcx>(
}; };
fn push_region_params(regions: &mut VecPerParamSpace<ty::Region>, fn push_region_params(regions: &mut VecPerParamSpace<ty::Region>,
free_id: ast::NodeId, all_outlive_extent: region::DestructionScopeData,
region_params: &[RegionParameterDef]) region_params: &[RegionParameterDef])
{ {
for r in region_params { for r in region_params {
regions.push(r.space, ty::free_region_from_def(free_id, r)); regions.push(r.space, ty::free_region_from_def(all_outlive_extent, r));
} }
} }
@ -6295,14 +6303,14 @@ pub fn construct_parameter_environment<'a,'tcx>(
// //
let free_substs = construct_free_substs(tcx, generics, free_id); let free_substs = construct_free_substs(tcx, generics, free_id);
let free_id_scope = region::CodeExtent::from_node_id(free_id); let free_id_outlive = region::DestructionScopeData::new(free_id);
// //
// Compute the bounds on Self and the type parameters. // Compute the bounds on Self and the type parameters.
// //
let bounds = generics.to_bounds(tcx, &free_substs); let bounds = generics.to_bounds(tcx, &free_substs);
let bounds = liberate_late_bound_regions(tcx, free_id_scope, &ty::Binder(bounds)); let bounds = liberate_late_bound_regions(tcx, free_id_outlive, &ty::Binder(bounds));
let predicates = bounds.predicates.into_vec(); let predicates = bounds.predicates.into_vec();
// //
@ -6335,7 +6343,7 @@ pub fn construct_parameter_environment<'a,'tcx>(
let unnormalized_env = ty::ParameterEnvironment { let unnormalized_env = ty::ParameterEnvironment {
tcx: tcx, tcx: tcx,
free_substs: free_substs, free_substs: free_substs,
implicit_region_bound: ty::ReScope(free_id_scope), implicit_region_bound: ty::ReScope(free_id_outlive.to_code_extent()),
caller_bounds: predicates, caller_bounds: predicates,
selection_cache: traits::SelectionCache::new(), selection_cache: traits::SelectionCache::new(),
}; };
@ -6603,14 +6611,14 @@ impl<'tcx> AutoDerefRef<'tcx> {
/// `scope_id`. /// `scope_id`.
pub fn liberate_late_bound_regions<'tcx, T>( pub fn liberate_late_bound_regions<'tcx, T>(
tcx: &ty::ctxt<'tcx>, tcx: &ty::ctxt<'tcx>,
scope: region::CodeExtent, all_outlive_scope: region::DestructionScopeData,
value: &Binder<T>) value: &Binder<T>)
-> T -> T
where T : TypeFoldable<'tcx> + Repr<'tcx> where T : TypeFoldable<'tcx> + Repr<'tcx>
{ {
replace_late_bound_regions( replace_late_bound_regions(
tcx, value, tcx, value,
|br| ty::ReFree(ty::FreeRegion{scope: scope, bound_region: br})).0 |br| ty::ReFree(ty::FreeRegion{scope: all_outlive_scope, bound_region: br})).0
} }
pub fn count_late_bound_regions<'tcx, T>( pub fn count_late_bound_regions<'tcx, T>(

View File

@ -113,6 +113,10 @@ pub fn explain_region_and_span(cx: &ctxt, region: ty::Region)
}; };
let scope_decorated_tag = match scope { let scope_decorated_tag = match scope {
region::CodeExtent::Misc(_) => tag, region::CodeExtent::Misc(_) => tag,
region::CodeExtent::DestructionScope(_) => {
new_string = format!("destruction scope surrounding {}", tag);
new_string.as_slice()
}
region::CodeExtent::Remainder(r) => { region::CodeExtent::Remainder(r) => {
new_string = format!("block suffix following statement {}", new_string = format!("block suffix following statement {}",
r.first_statement_index); r.first_statement_index);
@ -135,7 +139,7 @@ pub fn explain_region_and_span(cx: &ctxt, region: ty::Region)
} }
}; };
match cx.map.find(fr.scope.node_id()) { match cx.map.find(fr.scope.node_id) {
Some(ast_map::NodeBlock(ref blk)) => { Some(ast_map::NodeBlock(ref blk)) => {
let (msg, opt_span) = explain_span(cx, "block", blk.span); let (msg, opt_span) = explain_span(cx, "block", blk.span);
(format!("{} {}", prefix, msg), opt_span) (format!("{} {}", prefix, msg), opt_span)
@ -921,7 +925,7 @@ impl<'tcx> UserString<'tcx> for ty::Region {
impl<'tcx> Repr<'tcx> for ty::FreeRegion { impl<'tcx> Repr<'tcx> for ty::FreeRegion {
fn repr(&self, tcx: &ctxt) -> String { fn repr(&self, tcx: &ctxt) -> String {
format!("ReFree({}, {})", format!("ReFree({}, {})",
self.scope.node_id(), self.scope.repr(tcx),
self.bound_region.repr(tcx)) self.bound_region.repr(tcx))
} }
} }
@ -931,12 +935,23 @@ impl<'tcx> Repr<'tcx> for region::CodeExtent {
match *self { match *self {
region::CodeExtent::Misc(node_id) => region::CodeExtent::Misc(node_id) =>
format!("Misc({})", node_id), format!("Misc({})", node_id),
region::CodeExtent::DestructionScope(node_id) =>
format!("DestructionScope({})", node_id),
region::CodeExtent::Remainder(rem) => region::CodeExtent::Remainder(rem) =>
format!("Remainder({}, {})", rem.block, rem.first_statement_index), format!("Remainder({}, {})", rem.block, rem.first_statement_index),
} }
} }
} }
impl<'tcx> Repr<'tcx> for region::DestructionScopeData {
fn repr(&self, _tcx: &ctxt) -> String {
match *self {
region::DestructionScopeData{ node_id } =>
format!("DestructionScopeData {{ node_id: {} }}", node_id),
}
}
}
impl<'tcx> Repr<'tcx> for ast::DefId { impl<'tcx> Repr<'tcx> for ast::DefId {
fn repr(&self, tcx: &ctxt) -> String { fn repr(&self, tcx: &ctxt) -> String {
// Unfortunately, there seems to be no way to attempt to print // Unfortunately, there seems to be no way to attempt to print

View File

@ -286,7 +286,7 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> {
let loan_scope = match loan_region { let loan_scope = match loan_region {
ty::ReScope(scope) => scope, ty::ReScope(scope) => scope,
ty::ReFree(ref fr) => fr.scope, ty::ReFree(ref fr) => fr.scope.to_code_extent(),
ty::ReStatic => { ty::ReStatic => {
// If we get here, an error must have been // If we get here, an error must have been

View File

@ -15,7 +15,7 @@ use diagnostic::Emitter;
use driver; use driver;
use rustc_resolve as resolve; use rustc_resolve as resolve;
use rustc_typeck::middle::lang_items; use rustc_typeck::middle::lang_items;
use rustc_typeck::middle::region::{self, CodeExtent}; use rustc_typeck::middle::region::{self, CodeExtent, DestructionScopeData};
use rustc_typeck::middle::resolve_lifetime; use rustc_typeck::middle::resolve_lifetime;
use rustc_typeck::middle::stability; use rustc_typeck::middle::stability;
use rustc_typeck::middle::subst; use rustc_typeck::middle::subst;
@ -325,7 +325,7 @@ impl<'a, 'tcx> Env<'a, 'tcx> {
} }
pub fn re_free(&self, nid: ast::NodeId, id: u32) -> ty::Region { pub fn re_free(&self, nid: ast::NodeId, id: u32) -> ty::Region {
ty::ReFree(ty::FreeRegion { scope: CodeExtent::from_node_id(nid), ty::ReFree(ty::FreeRegion { scope: DestructionScopeData::new(nid),
bound_region: ty::BrAnon(id)}) bound_region: ty::BrAnon(id)})
} }

View File

@ -130,12 +130,17 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
// this new AST scope had better be its immediate child. // this new AST scope had better be its immediate child.
let top_scope = self.top_ast_scope(); let top_scope = self.top_ast_scope();
if top_scope.is_some() { if top_scope.is_some() {
assert_eq!(self.ccx assert!((self.ccx
.tcx() .tcx()
.region_maps .region_maps
.opt_encl_scope(region::CodeExtent::from_node_id(debug_loc.id)) .opt_encl_scope(region::CodeExtent::from_node_id(debug_loc.id))
.map(|s|s.node_id()), .map(|s|s.node_id()) == top_scope)
top_scope); ||
(self.ccx
.tcx()
.region_maps
.opt_encl_scope(region::CodeExtent::DestructionScope(debug_loc.id))
.map(|s|s.node_id()) == top_scope));
} }
self.push_scope(CleanupScope::new(AstScopeKind(debug_loc.id), self.push_scope(CleanupScope::new(AstScopeKind(debug_loc.id),

View File

@ -13,7 +13,7 @@
use super::{check_fn, Expectation, FnCtxt}; use super::{check_fn, Expectation, FnCtxt};
use astconv; use astconv;
use middle::region::CodeExtent; use middle::region;
use middle::subst; use middle::subst;
use middle::ty::{self, ToPolyTraitRef, Ty}; use middle::ty::{self, ToPolyTraitRef, Ty};
use rscope::RegionScope; use rscope::RegionScope;
@ -78,7 +78,9 @@ fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
fcx.write_ty(expr.id, closure_type); fcx.write_ty(expr.id, closure_type);
let fn_sig = let fn_sig =
ty::liberate_late_bound_regions(fcx.tcx(), CodeExtent::from_node_id(body.id), &fn_ty.sig); ty::liberate_late_bound_regions(fcx.tcx(),
region::DestructionScopeData::new(body.id),
&fn_ty.sig);
check_fn(fcx.ccx, check_fn(fcx.ccx,
ast::Unsafety::Normal, ast::Unsafety::Normal,

View File

@ -90,7 +90,7 @@ use middle::infer;
use middle::mem_categorization as mc; use middle::mem_categorization as mc;
use middle::mem_categorization::McResult; use middle::mem_categorization::McResult;
use middle::pat_util::{self, pat_id_map}; use middle::pat_util::{self, pat_id_map};
use middle::region::CodeExtent; use middle::region::{self, CodeExtent};
use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace, TypeSpace}; use middle::subst::{self, Subst, Substs, VecPerParamSpace, ParamSpace, TypeSpace};
use middle::traits; use middle::traits;
use middle::ty::{FnSig, VariantInfo, TypeScheme}; use middle::ty::{FnSig, VariantInfo, TypeScheme};
@ -495,7 +495,9 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
let fn_sig = let fn_sig =
fn_ty.sig.subst(ccx.tcx, &inh.param_env.free_substs); fn_ty.sig.subst(ccx.tcx, &inh.param_env.free_substs);
let fn_sig = let fn_sig =
liberate_late_bound_regions(ccx.tcx, CodeExtent::from_node_id(body.id), &fn_sig); liberate_late_bound_regions(ccx.tcx,
region::DestructionScopeData::new(body.id),
&fn_sig);
let fn_sig = let fn_sig =
inh.normalize_associated_types_in(&inh.param_env, body.span, body.id, &fn_sig); inh.normalize_associated_types_in(&inh.param_env, body.span, body.id, &fn_sig);
@ -1686,7 +1688,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut bounds_checker = wf::BoundsChecker::new(self, let mut bounds_checker = wf::BoundsChecker::new(self,
ast_t.span, ast_t.span,
CodeExtent::from_node_id(self.body_id), self.body_id,
None); None);
bounds_checker.check_ty(t); bounds_checker.check_ty(t);

View File

@ -145,7 +145,7 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
let variants = lookup_fields(fcx); let variants = lookup_fields(fcx);
let mut bounds_checker = BoundsChecker::new(fcx, let mut bounds_checker = BoundsChecker::new(fcx,
item.span, item.span,
region::CodeExtent::from_node_id(item.id), item.id,
Some(&mut this.cache)); Some(&mut this.cache));
for variant in &variants { for variant in &variants {
for field in &variant.fields { for field in &variant.fields {
@ -180,7 +180,7 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
self.with_fcx(item, |this, fcx| { self.with_fcx(item, |this, fcx| {
let mut bounds_checker = BoundsChecker::new(fcx, let mut bounds_checker = BoundsChecker::new(fcx,
item.span, item.span,
region::CodeExtent::from_node_id(item.id), item.id,
Some(&mut this.cache)); Some(&mut this.cache));
let type_scheme = ty::lookup_item_type(fcx.tcx(), local_def(item.id)); let type_scheme = ty::lookup_item_type(fcx.tcx(), local_def(item.id));
@ -196,11 +196,9 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
item: &ast::Item) item: &ast::Item)
{ {
self.with_fcx(item, |this, fcx| { self.with_fcx(item, |this, fcx| {
let item_scope = region::CodeExtent::from_node_id(item.id);
let mut bounds_checker = BoundsChecker::new(fcx, let mut bounds_checker = BoundsChecker::new(fcx,
item.span, item.span,
item_scope, item.id,
Some(&mut this.cache)); Some(&mut this.cache));
// Find the impl self type as seen from the "inside" -- // Find the impl self type as seen from the "inside" --
@ -383,7 +381,12 @@ impl<'ccx, 'tcx, 'v> Visitor<'v> for CheckTypeWellFormedVisitor<'ccx, 'tcx> {
pub struct BoundsChecker<'cx,'tcx:'cx> { pub struct BoundsChecker<'cx,'tcx:'cx> {
fcx: &'cx FnCtxt<'cx,'tcx>, fcx: &'cx FnCtxt<'cx,'tcx>,
span: Span, span: Span,
scope: region::CodeExtent,
// This field is often attached to item impls; it is not clear
// that `CodeExtent` is well-defined for such nodes, so pnkfelix
// has left it as a NodeId rather than porting to CodeExtent.
scope: ast::NodeId,
binding_count: uint, binding_count: uint,
cache: Option<&'cx mut HashSet<Ty<'tcx>>>, cache: Option<&'cx mut HashSet<Ty<'tcx>>>,
} }
@ -391,7 +394,7 @@ pub struct BoundsChecker<'cx,'tcx:'cx> {
impl<'cx,'tcx> BoundsChecker<'cx,'tcx> { impl<'cx,'tcx> BoundsChecker<'cx,'tcx> {
pub fn new(fcx: &'cx FnCtxt<'cx,'tcx>, pub fn new(fcx: &'cx FnCtxt<'cx,'tcx>,
span: Span, span: Span,
scope: region::CodeExtent, scope: ast::NodeId,
cache: Option<&'cx mut HashSet<Ty<'tcx>>>) cache: Option<&'cx mut HashSet<Ty<'tcx>>>)
-> BoundsChecker<'cx,'tcx> { -> BoundsChecker<'cx,'tcx> {
BoundsChecker { fcx: fcx, span: span, scope: scope, BoundsChecker { fcx: fcx, span: span, scope: scope,
@ -446,9 +449,12 @@ impl<'cx,'tcx> TypeFolder<'tcx> for BoundsChecker<'cx,'tcx> {
where T : TypeFoldable<'tcx> + Repr<'tcx> where T : TypeFoldable<'tcx> + Repr<'tcx>
{ {
self.binding_count += 1; self.binding_count += 1;
let value = liberate_late_bound_regions(self.fcx.tcx(), self.scope, binder); let value = liberate_late_bound_regions(
debug!("BoundsChecker::fold_binder: late-bound regions replaced: {}", self.fcx.tcx(),
value.repr(self.tcx())); region::DestructionScopeData::new(self.scope),
binder);
debug!("BoundsChecker::fold_binder: late-bound regions replaced: {} at scope: {:?}",
value.repr(self.tcx()), self.scope);
let value = value.fold_with(self); let value = value.fold_with(self);
self.binding_count -= 1; self.binding_count -= 1;
ty::Binder(value) ty::Binder(value)

View File

@ -1564,7 +1564,7 @@ fn check_method_self_type<'a, 'tcx, RS:RegionScope>(
_ => typ, _ => typ,
}; };
let body_scope = region::CodeExtent::from_node_id(body_id); let body_scope = region::DestructionScopeData::new(body_id);
// "Required type" comes from the trait definition. It may // "Required type" comes from the trait definition. It may
// contain late-bound regions from the method, but not the // contain late-bound regions from the method, but not the
@ -1608,7 +1608,7 @@ fn check_method_self_type<'a, 'tcx, RS:RegionScope>(
fn liberate_early_bound_regions<'tcx,T>( fn liberate_early_bound_regions<'tcx,T>(
tcx: &ty::ctxt<'tcx>, tcx: &ty::ctxt<'tcx>,
scope: region::CodeExtent, scope: region::DestructionScopeData,
value: &T) value: &T)
-> T -> T
where T : TypeFoldable<'tcx> + Repr<'tcx> where T : TypeFoldable<'tcx> + Repr<'tcx>