From 81383bd8691c0915e66abaca845e508b6edc4851 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 25 Nov 2014 17:02:20 +0100 Subject: [PATCH] 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. --- src/librustc/metadata/tydecode.rs | 11 +- src/librustc/metadata/tyencode.rs | 8 +- src/librustc/middle/astencode.rs | 6 + src/librustc/middle/infer/error_reporting.rs | 2 +- .../middle/infer/region_inference/mod.rs | 13 +- src/librustc/middle/liveness.rs | 4 +- src/librustc/middle/mem_categorization.rs | 5 +- src/librustc/middle/region.rs | 139 ++++++++++++++++-- src/librustc/middle/resolve_lifetime.rs | 16 +- src/librustc/middle/ty.rs | 36 +++-- src/librustc/util/ppaux.rs | 19 ++- .../borrowck/gather_loans/mod.rs | 2 +- src/librustc_driver/test.rs | 4 +- src/librustc_trans/trans/cleanup.rs | 17 ++- src/librustc_typeck/check/closure.rs | 6 +- src/librustc_typeck/check/mod.rs | 8 +- src/librustc_typeck/check/wf.rs | 26 ++-- src/librustc_typeck/collect.rs | 4 +- 18 files changed, 253 insertions(+), 73 deletions(-) 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>