diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index 7cc7e49b6d2..9962f49dfcf 100644 --- a/src/librustc/metadata/tydecode.rs +++ b/src/librustc/metadata/tydecode.rs @@ -349,7 +349,7 @@ fn parse_region_(st: &mut PState, conv: &mut F) -> ty::Region where } 'f' => { assert_eq!(next(st), '['); - let scope = parse_scope(st); + let scope = parse_destruction_scope_data(st); assert_eq!(next(st), '|'); let br = parse_bound_region_(st, conv); 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; region::CodeExtent::Misc(node_id) } + 'D' => { + let node_id = parse_uint(st) as ast::NodeId; + region::CodeExtent::DestructionScope(node_id) + } 'B' => { let node_id = parse_uint(st) as ast::NodeId; 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 where F: FnOnce(&mut PState<'a, 'tcx>) -> T, { diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs index f8081e2c309..640b9649286 100644 --- a/src/librustc/metadata/tyencode.rs +++ b/src/librustc/metadata/tyencode.rs @@ -251,7 +251,7 @@ pub fn enc_region(w: &mut SeekableMemWriter, cx: &ctxt, r: ty::Region) { } ty::ReFree(ref fr) => { mywrite!(w, "f["); - enc_scope(w, cx, fr.scope); + enc_destruction_scope_data(w, fr.scope); mywrite!(w, "|"); enc_bound_region(w, cx, fr.bound_region); 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::Remainder(region::BlockRemainder { 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) { match br { ty::BrAnon(idx) => { diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index b0fe743b683..8e3bf0fa28d 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -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 { fn tr(&self, dcx: &DecodeContext) -> ty::BoundRegion { match *self { diff --git a/src/librustc/middle/infer/error_reporting.rs b/src/librustc/middle/infer/error_reporting.rs index 99896535442..6cacf46b2fe 100644 --- a/src/librustc/middle/infer/error_reporting.rs +++ b/src/librustc/middle/infer/error_reporting.rs @@ -304,7 +304,7 @@ impl<'a, 'tcx> ErrorReporting<'tcx> for InferCtxt<'a, 'tcx> { return None } assert!(fr1.scope == fr2.scope); - (fr1.scope.node_id(), fr1, fr2) + (fr1.scope.node_id, fr1, fr2) }, _ => return None }; diff --git a/src/librustc/middle/infer/region_inference/mod.rs b/src/librustc/middle/infer/region_inference/mod.rs index 1a7308a4f18..6b6887ba8e2 100644 --- a/src/librustc/middle/infer/region_inference/mod.rs +++ b/src/librustc/middle/infer/region_inference/mod.rs @@ -760,11 +760,12 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { // A "free" region can be interpreted as "some region // at least as big as the block fr.scope_id". So, we can // 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 // the scope region `s_id`, then the LUB is the free // 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, // 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 // is the scope `s_id`. Otherwise, as we do not know // big the free region is precisely, the GLB is undefined. - match self.tcx.region_maps.nearest_common_ancestor(fr.scope, s_id) { - Some(r_id) if r_id == fr.scope => Ok(s), + let fr_scope = fr.scope.to_code_extent(); + 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)) } } @@ -915,7 +917,8 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> { Ok(ty::ReFree(*b)) } else { this.intersect_scopes(ty::ReFree(*a), ty::ReFree(*b), - a.scope, b.scope) + a.scope.to_code_extent(), + b.scope.to_code_extent()) } } } diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index fcc5d70a7a5..d4fe0979313 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -112,7 +112,7 @@ use self::VarKind::*; use middle::def::*; use middle::mem_categorization::Typer; use middle::pat_util; -use middle::region::CodeExtent; +use middle::region; use middle::ty; use middle::ty::ClosureTyper; use lint; @@ -1514,7 +1514,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { let fn_ret = ty::liberate_late_bound_regions( self.ir.tcx, - CodeExtent::from_node_id(body.id), + region::DestructionScopeData::new(body.id), &self.fn_ret(id)); match fn_ret { diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 58492c817fd..fed2f7d9245 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -760,7 +760,10 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { // Region of environment pointer 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 }); diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 037b1c31a17..2f0462ab8c3 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -27,11 +27,66 @@ use syntax::{ast, visit}; use syntax::ast::{Block, Item, FnDecl, NodeId, Arm, Pat, Stmt, Expr, Local}; use syntax::ast_util::{stmt_id}; use syntax::ast_map; +use syntax::ptr::P; use syntax::visit::{Visitor, FnKind}; /// CodeExtent represents a statically-describable extent that can be /// 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 /// placate the same deriving in `ty::FreeRegion`, but we may want to /// actually attach a more meaningful ordering to scopes than the one @@ -40,7 +95,24 @@ use syntax::visit::{Visitor, FnKind}; RustcDecodable, Debug, Copy)] pub enum CodeExtent { 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 @@ -82,6 +154,7 @@ impl CodeExtent { match *self { CodeExtent::Misc(node_id) => node_id, CodeExtent::Remainder(br) => br.block, + CodeExtent::DestructionScope(node_id) => node_id, } } @@ -95,6 +168,8 @@ impl CodeExtent { CodeExtent::Remainder(br) => CodeExtent::Remainder(BlockRemainder { 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()) { Some(ast_map::NodeBlock(ref blk)) => { match *self { - CodeExtent::Misc(_) => Some(blk.span), + CodeExtent::Misc(_) | + CodeExtent::DestructionScope(_) => Some(blk.span), CodeExtent::Remainder(r) => { assert_eq!(r.block, blk.id); @@ -455,7 +531,7 @@ impl RegionMaps { } (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)) => { @@ -567,7 +643,18 @@ fn resolve_block(visitor: &mut RegionResolutionVisitor, blk: &ast::Block) { let prev_cx = visitor.cx; 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 // 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 // regions referenced by the destructors need to survive. 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; 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); 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; visitor.cx.parent = InnermostEnclosingExpr::Some(expr.id); { let region_maps = &mut visitor.region_maps; - let terminating = |id| { - let scope = CodeExtent::from_node_id(id); + let terminating = |e: &P| { + let scope = CodeExtent::from_node_id(e.id); + region_maps.mark_as_terminating_scope(scope) + }; + let terminating_block = |b: &P| { + let scope = CodeExtent::from_node_id(b.id); region_maps.mark_as_terminating_scope(scope) }; 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) => { // For shortcircuiting operators, mark the RHS as a terminating // scope since it only executes conditionally. - terminating(r.id); + terminating(r); } ast::ExprIf(_, ref then, Some(ref otherwise)) => { - terminating(then.id); - terminating(otherwise.id); + terminating_block(then); + terminating(otherwise); } ast::ExprIf(ref expr, ref then, None) => { - terminating(expr.id); - terminating(then.id); + terminating(expr); + terminating_block(then); } ast::ExprLoop(ref body, _) => { - terminating(body.id); + terminating_block(body); } ast::ExprWhile(ref expr, ref body, _) => { - terminating(expr.id); - terminating(body.id); + terminating(expr); + terminating_block(body); } ast::ExprMatch(..) => { @@ -1021,6 +1125,9 @@ fn resolve_fn(visitor: &mut RegionResolutionVisitor, let body_scope = CodeExtent::from_node_id(body.id); 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; diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index 6f38dc81064..e91d7d8c52c 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -41,7 +41,7 @@ pub enum DefRegion { /* lifetime decl */ ast::NodeId), DefLateBoundRegion(ty::DebruijnIndex, /* lifetime decl */ ast::NodeId), - DefFreeRegion(/* block scope */ region::CodeExtent, + DefFreeRegion(/* block scope */ region::DestructionScopeData, /* lifetime decl */ ast::NodeId), } @@ -81,7 +81,7 @@ enum ScopeChain<'a> { LateScope(&'a Vec, Scope<'a>), /// lifetimes introduced by items within a code block are scoped /// to that block. - BlockScope(region::CodeExtent, Scope<'a>), + BlockScope(region::DestructionScopeData, Scope<'a>), RootScope } @@ -191,7 +191,8 @@ impl<'a, 'v> Visitor<'v> for LifetimeContext<'a> { } 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)); } @@ -393,9 +394,13 @@ impl<'a> LifetimeContext<'a> { } fn resolve_free_lifetime_ref(&mut self, - scope_data: region::CodeExtent, + scope_data: region::DestructionScopeData, lifetime_ref: &ast::Lifetime, 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, // until we encounter a scope that contains the named lifetime // or we run out of scopes. @@ -403,6 +408,9 @@ impl<'a> LifetimeContext<'a> { let mut scope = scope; let mut search_result = None; loop { + debug!("resolve_free_lifetime_ref \ + scope_data: {:?} scope: {:?} search_result: {:?}", + scope_data, scope, search_result); match *scope { BlockScope(blk_scope_data, s) => { scope_data = blk_scope_data; diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 97e6517a615..bedcd74cfd7 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -1174,7 +1174,9 @@ pub enum Region { /// region parameters. 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), /// 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 /// at least as big as the scope `fr.scope`". pub struct FreeRegion { - pub scope: region::CodeExtent, + pub scope: region::DestructionScopeData, 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::ReFree(ty::FreeRegion { scope: region::CodeExtent::from_node_id(free_id), - bound_region: ty::BrNamed(def.def_id, - def.name) }) + let ret = + ty::ReFree(ty::FreeRegion { scope: outlives_extent, + 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 @@ -6252,9 +6258,11 @@ pub fn construct_free_substs<'a,'tcx>( let mut types = VecPerParamSpace::empty(); 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 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 { types: types, @@ -6262,11 +6270,11 @@ pub fn construct_free_substs<'a,'tcx>( }; fn push_region_params(regions: &mut VecPerParamSpace, - free_id: ast::NodeId, + all_outlive_extent: region::DestructionScopeData, region_params: &[RegionParameterDef]) { 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_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. // 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(); // @@ -6335,7 +6343,7 @@ pub fn construct_parameter_environment<'a,'tcx>( let unnormalized_env = ty::ParameterEnvironment { tcx: tcx, 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, selection_cache: traits::SelectionCache::new(), }; @@ -6603,14 +6611,14 @@ impl<'tcx> AutoDerefRef<'tcx> { /// `scope_id`. pub fn liberate_late_bound_regions<'tcx, T>( tcx: &ty::ctxt<'tcx>, - scope: region::CodeExtent, + all_outlive_scope: region::DestructionScopeData, value: &Binder) -> T where T : TypeFoldable<'tcx> + Repr<'tcx> { replace_late_bound_regions( 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>( diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index c72b7fdf7ad..397d27db3b9 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -113,6 +113,10 @@ pub fn explain_region_and_span(cx: &ctxt, region: ty::Region) }; let scope_decorated_tag = match scope { region::CodeExtent::Misc(_) => tag, + region::CodeExtent::DestructionScope(_) => { + new_string = format!("destruction scope surrounding {}", tag); + new_string.as_slice() + } region::CodeExtent::Remainder(r) => { new_string = format!("block suffix following statement {}", 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)) => { let (msg, opt_span) = explain_span(cx, "block", blk.span); (format!("{} {}", prefix, msg), opt_span) @@ -921,7 +925,7 @@ impl<'tcx> UserString<'tcx> for ty::Region { impl<'tcx> Repr<'tcx> for ty::FreeRegion { fn repr(&self, tcx: &ctxt) -> String { format!("ReFree({}, {})", - self.scope.node_id(), + self.scope.repr(tcx), self.bound_region.repr(tcx)) } } @@ -931,12 +935,23 @@ impl<'tcx> Repr<'tcx> for region::CodeExtent { match *self { region::CodeExtent::Misc(node_id) => format!("Misc({})", node_id), + region::CodeExtent::DestructionScope(node_id) => + format!("DestructionScope({})", node_id), region::CodeExtent::Remainder(rem) => 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 { fn repr(&self, tcx: &ctxt) -> String { // Unfortunately, there seems to be no way to attempt to print diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_borrowck/borrowck/gather_loans/mod.rs index b1cc3a65120..4e308c5809f 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/mod.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/mod.rs @@ -286,7 +286,7 @@ impl<'a, 'tcx> GatherLoanCtxt<'a, 'tcx> { let loan_scope = match loan_region { ty::ReScope(scope) => scope, - ty::ReFree(ref fr) => fr.scope, + ty::ReFree(ref fr) => fr.scope.to_code_extent(), ty::ReStatic => { // If we get here, an error must have been diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 9df90258462..7105a6cc488 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -15,7 +15,7 @@ use diagnostic::Emitter; use driver; use rustc_resolve as resolve; 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::stability; 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 { - ty::ReFree(ty::FreeRegion { scope: CodeExtent::from_node_id(nid), + ty::ReFree(ty::FreeRegion { scope: DestructionScopeData::new(nid), bound_region: ty::BrAnon(id)}) } diff --git a/src/librustc_trans/trans/cleanup.rs b/src/librustc_trans/trans/cleanup.rs index bebba151a0d..f7e37b6f633 100644 --- a/src/librustc_trans/trans/cleanup.rs +++ b/src/librustc_trans/trans/cleanup.rs @@ -130,12 +130,17 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> { // this new AST scope had better be its immediate child. let top_scope = self.top_ast_scope(); if top_scope.is_some() { - assert_eq!(self.ccx - .tcx() - .region_maps - .opt_encl_scope(region::CodeExtent::from_node_id(debug_loc.id)) - .map(|s|s.node_id()), - top_scope); + assert!((self.ccx + .tcx() + .region_maps + .opt_encl_scope(region::CodeExtent::from_node_id(debug_loc.id)) + .map(|s|s.node_id()) == 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), diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index b2a676e878e..0b7c5b04aaa 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -13,7 +13,7 @@ use super::{check_fn, Expectation, FnCtxt}; use astconv; -use middle::region::CodeExtent; +use middle::region; use middle::subst; use middle::ty::{self, ToPolyTraitRef, Ty}; use rscope::RegionScope; @@ -78,7 +78,9 @@ fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, fcx.write_ty(expr.id, closure_type); 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, ast::Unsafety::Normal, diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 9c0d6f7dae3..a081aabe559 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -90,7 +90,7 @@ use middle::infer; use middle::mem_categorization as mc; use middle::mem_categorization::McResult; 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::traits; use middle::ty::{FnSig, VariantInfo, TypeScheme}; @@ -495,7 +495,9 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let fn_sig = fn_ty.sig.subst(ccx.tcx, &inh.param_env.free_substs); 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 = 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, ast_t.span, - CodeExtent::from_node_id(self.body_id), + self.body_id, None); bounds_checker.check_ty(t); diff --git a/src/librustc_typeck/check/wf.rs b/src/librustc_typeck/check/wf.rs index 1079e87a48b..5122f9e2d38 100644 --- a/src/librustc_typeck/check/wf.rs +++ b/src/librustc_typeck/check/wf.rs @@ -145,7 +145,7 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> { let variants = lookup_fields(fcx); let mut bounds_checker = BoundsChecker::new(fcx, item.span, - region::CodeExtent::from_node_id(item.id), + item.id, Some(&mut this.cache)); for variant in &variants { for field in &variant.fields { @@ -180,7 +180,7 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> { self.with_fcx(item, |this, fcx| { let mut bounds_checker = BoundsChecker::new(fcx, item.span, - region::CodeExtent::from_node_id(item.id), + item.id, Some(&mut this.cache)); 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) { self.with_fcx(item, |this, fcx| { - let item_scope = region::CodeExtent::from_node_id(item.id); - let mut bounds_checker = BoundsChecker::new(fcx, item.span, - item_scope, + item.id, Some(&mut this.cache)); // 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> { fcx: &'cx FnCtxt<'cx,'tcx>, 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, cache: Option<&'cx mut HashSet>>, } @@ -391,7 +394,7 @@ pub struct BoundsChecker<'cx,'tcx:'cx> { impl<'cx,'tcx> BoundsChecker<'cx,'tcx> { pub fn new(fcx: &'cx FnCtxt<'cx,'tcx>, span: Span, - scope: region::CodeExtent, + scope: ast::NodeId, cache: Option<&'cx mut HashSet>>) -> BoundsChecker<'cx,'tcx> { 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> { self.binding_count += 1; - let value = liberate_late_bound_regions(self.fcx.tcx(), self.scope, binder); - debug!("BoundsChecker::fold_binder: late-bound regions replaced: {}", - value.repr(self.tcx())); + let value = liberate_late_bound_regions( + self.fcx.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); self.binding_count -= 1; ty::Binder(value) diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index ce26658cf4b..55fa47760bb 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1564,7 +1564,7 @@ fn check_method_self_type<'a, 'tcx, RS:RegionScope>( _ => 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 // 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>( tcx: &ty::ctxt<'tcx>, - scope: region::CodeExtent, + scope: region::DestructionScopeData, value: &T) -> T where T : TypeFoldable<'tcx> + Repr<'tcx>