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`.
This commit is contained in:
parent
d1835ae7cc
commit
05b8a106e4
4 changed files with 54 additions and 7 deletions
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) =>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue