about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorFelix S. Klock II <pnkfelix@pnkfx.org>2015-04-03 19:50:32 +0200
committerFelix S. Klock II <pnkfelix@pnkfx.org>2015-04-04 09:23:40 +0200
commit05b8a106e45b0c8381c6bc89e76e7bb94b03a84c (patch)
treef61eccebacd9d2a5ff64783ca5e0095e52d58d8a /src
parentd1835ae7cc175b9aff383432e934e7ad13825ebc (diff)
downloadrust-05b8a106e45b0c8381c6bc89e76e7bb94b03a84c.tar.gz
rust-05b8a106e45b0c8381c6bc89e76e7bb94b03a84c.zip
Encode more precise scoping rules for function params.
Function params which outlive everything in the body (incl
temporaries).  Thus if we assign them their own `CodeExtent`, the
region inference can properly show that it is sound to have
temporaries with destructors that reference the parameters (because
such temporaries will be dropped before the parameters are).

This allows us to address issue 23338 in a clean way.

As a drive-by, fix a mistake in the tyencode for
`CodeExtent::BlockRemainder`.
Diffstat (limited to 'src')
-rw-r--r--src/librustc/metadata/tydecode.rs13
-rw-r--r--src/librustc/metadata/tyencode.rs4
-rw-r--r--src/librustc/middle/region.rs39
-rw-r--r--src/librustc/util/ppaux.rs5
4 files changed, 54 insertions, 7 deletions
diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs
index 3fb128b1881..8030275ef30 100644
--- a/src/librustc/metadata/tydecode.rs
+++ b/src/librustc/metadata/tydecode.rs
@@ -373,6 +373,16 @@ fn parse_region_<F>(st: &mut PState, conv: &mut F) -> ty::Region where
 
 fn parse_scope(st: &mut PState) -> region::CodeExtent {
     match next(st) {
+        'P' => {
+            assert_eq!(next(st), '[');
+            let fn_id = parse_uint(st) as ast::NodeId;
+            assert_eq!(next(st), '|');
+            let body_id = parse_uint(st) as ast::NodeId;
+            assert_eq!(next(st), ']');
+            region::CodeExtent::ParameterScope {
+                fn_id: fn_id, body_id: body_id
+            }
+        }
         'M' => {
             let node_id = parse_uint(st) as ast::NodeId;
             region::CodeExtent::Misc(node_id)
@@ -382,8 +392,11 @@ fn parse_scope(st: &mut PState) -> region::CodeExtent {
             region::CodeExtent::DestructionScope(node_id)
         }
         'B' => {
+            assert_eq!(next(st), '[');
             let node_id = parse_uint(st) as ast::NodeId;
+            assert_eq!(next(st), '|');
             let first_stmt_index = parse_uint(st);
+            assert_eq!(next(st), ']');
             let block_remainder = region::BlockRemainder {
                 block: node_id, first_statement_index: first_stmt_index,
             };
diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs
index 7a2df496628..90a905f1840 100644
--- a/src/librustc/metadata/tyencode.rs
+++ b/src/librustc/metadata/tyencode.rs
@@ -275,9 +275,11 @@ pub fn enc_region(w: &mut Encoder, cx: &ctxt, r: ty::Region) {
 
 fn enc_scope(w: &mut Encoder, _cx: &ctxt, scope: region::CodeExtent) {
     match scope {
+        region::CodeExtent::ParameterScope {
+            fn_id, body_id } => mywrite!(w, "P[{}|{}]", fn_id, body_id),
         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),
+            block: b, first_statement_index: i }) => mywrite!(w, "B[{}|{}]", b, i),
         region::CodeExtent::DestructionScope(node_id) => mywrite!(w, "D{}", node_id),
     }
 }
diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs
index 652f6613252..727a1dcdfb3 100644
--- a/src/librustc/middle/region.rs
+++ b/src/librustc/middle/region.rs
@@ -95,7 +95,15 @@ use syntax::visit::{Visitor, FnKind};
            RustcDecodable, Debug, Copy)]
 pub enum CodeExtent {
     Misc(ast::NodeId),
-    DestructionScope(ast::NodeId), // extent of destructors for temporaries of node-id
+
+    // extent of parameters passed to a function or closure (they
+    // outlive its body)
+    ParameterScope { fn_id: ast::NodeId, body_id: ast::NodeId },
+
+    // extent of destructors for temporaries of node-id
+    DestructionScope(ast::NodeId),
+
+    // extent of code following a `let id = expr;` binding in a block
     Remainder(BlockRemainder)
 }
 
@@ -153,15 +161,19 @@ impl CodeExtent {
     pub fn node_id(&self) -> ast::NodeId {
         match *self {
             CodeExtent::Misc(node_id) => node_id,
+
+            // These cases all return rough approximations to the
+            // precise extent denoted by `self`.
             CodeExtent::Remainder(br) => br.block,
             CodeExtent::DestructionScope(node_id) => node_id,
+            CodeExtent::ParameterScope { fn_id: _, body_id } => body_id,
         }
     }
 
     /// Maps this scope to a potentially new one according to the
     /// NodeId transformer `f_id`.
     pub fn map_id<F>(&self, f_id: F) -> CodeExtent where
-        F: FnOnce(ast::NodeId) -> ast::NodeId,
+        F: Fn(ast::NodeId) -> ast::NodeId,
     {
         match *self {
             CodeExtent::Misc(node_id) => CodeExtent::Misc(f_id(node_id)),
@@ -170,6 +182,8 @@ impl CodeExtent {
                     block: f_id(br.block), first_statement_index: br.first_statement_index }),
             CodeExtent::DestructionScope(node_id) =>
                 CodeExtent::DestructionScope(f_id(node_id)),
+            CodeExtent::ParameterScope { fn_id, body_id } =>
+                CodeExtent::ParameterScope { fn_id: f_id(fn_id), body_id: f_id(body_id) },
         }
     }
 
@@ -180,6 +194,7 @@ impl CodeExtent {
         match ast_map.find(self.node_id()) {
             Some(ast_map::NodeBlock(ref blk)) => {
                 match *self {
+                    CodeExtent::ParameterScope { .. } |
                     CodeExtent::Misc(_) |
                     CodeExtent::DestructionScope(_) => Some(blk.span),
 
@@ -277,6 +292,7 @@ enum InnermostDeclaringBlock {
     Block(ast::NodeId),
     Statement(DeclaringStatementContext),
     Match(ast::NodeId),
+    FnDecl { fn_id: ast::NodeId, body_id: ast::NodeId },
 }
 
 impl InnermostDeclaringBlock {
@@ -285,6 +301,8 @@ impl InnermostDeclaringBlock {
             InnermostDeclaringBlock::None => {
                 return Option::None;
             }
+            InnermostDeclaringBlock::FnDecl { fn_id, body_id } =>
+                CodeExtent::ParameterScope { fn_id: fn_id, body_id: body_id },
             InnermostDeclaringBlock::Block(id) |
             InnermostDeclaringBlock::Match(id) => CodeExtent::from_node_id(id),
             InnermostDeclaringBlock::Statement(s) =>  s.to_code_extent(),
@@ -1198,13 +1216,20 @@ fn resolve_fn(visitor: &mut RegionResolutionVisitor,
            body.id,
            visitor.cx.parent);
 
+    // This scope covers the function body, which includes the
+    // bindings introduced by let statements as well as temporaries
+    // created by the fn's tail expression (if any). It does *not*
+    // include the fn parameters (see below).
     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 fn_decl_scope = CodeExtent::ParameterScope { fn_id: id, body_id: body.id };
+    visitor.region_maps.record_encl_scope(dtor_scope, fn_decl_scope);
+
+    record_superlifetime(visitor, fn_decl_scope, body.span);
 
     if let Some(root_id) = visitor.cx.root_id {
         visitor.region_maps.record_fn_parent(body.id, root_id);
@@ -1212,11 +1237,13 @@ fn resolve_fn(visitor: &mut RegionResolutionVisitor,
 
     let outer_cx = visitor.cx;
 
-    // The arguments and `self` are parented to the body of the fn.
+    // The arguments and `self` are parented to the fn.
     visitor.cx = Context {
         root_id: Some(body.id),
-        parent: InnermostEnclosingExpr::Some(body.id),
-        var_parent: InnermostDeclaringBlock::Block(body.id)
+        parent: InnermostEnclosingExpr::None,
+        var_parent: InnermostDeclaringBlock::FnDecl {
+            fn_id: id, body_id: body.id
+        },
     };
     visit::walk_fn_decl(visitor, decl);
 
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 60b422b3769..7358b4cc0f6 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -113,6 +113,9 @@ pub fn explain_region_and_span(cx: &ctxt, region: ty::Region)
         };
         let scope_decorated_tag = match scope {
             region::CodeExtent::Misc(_) => tag,
+            region::CodeExtent::ParameterScope { .. } => {
+                "scope of parameters for function"
+            }
             region::CodeExtent::DestructionScope(_) => {
                 new_string = format!("destruction scope surrounding {}", tag);
                 &*new_string
@@ -952,6 +955,8 @@ impl<'tcx> Repr<'tcx> for ty::FreeRegion {
 impl<'tcx> Repr<'tcx> for region::CodeExtent {
     fn repr(&self, _tcx: &ctxt) -> String {
         match *self {
+            region::CodeExtent::ParameterScope { fn_id, body_id } =>
+                format!("ParameterScope({}, {})", fn_id, body_id),
             region::CodeExtent::Misc(node_id) =>
                 format!("Misc({})", node_id),
             region::CodeExtent::DestructionScope(node_id) =>