Skip to content

Commit 05b8a10

Browse files
committed
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`.
1 parent d1835ae commit 05b8a10

File tree

4 files changed

+54
-7
lines changed

4 files changed

+54
-7
lines changed

src/librustc/metadata/tydecode.rs

+13
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,16 @@ fn parse_region_<F>(st: &mut PState, conv: &mut F) -> ty::Region where
373373

374374
fn parse_scope(st: &mut PState) -> region::CodeExtent {
375375
match next(st) {
376+
'P' => {
377+
assert_eq!(next(st), '[');
378+
let fn_id = parse_uint(st) as ast::NodeId;
379+
assert_eq!(next(st), '|');
380+
let body_id = parse_uint(st) as ast::NodeId;
381+
assert_eq!(next(st), ']');
382+
region::CodeExtent::ParameterScope {
383+
fn_id: fn_id, body_id: body_id
384+
}
385+
}
376386
'M' => {
377387
let node_id = parse_uint(st) as ast::NodeId;
378388
region::CodeExtent::Misc(node_id)
@@ -382,8 +392,11 @@ fn parse_scope(st: &mut PState) -> region::CodeExtent {
382392
region::CodeExtent::DestructionScope(node_id)
383393
}
384394
'B' => {
395+
assert_eq!(next(st), '[');
385396
let node_id = parse_uint(st) as ast::NodeId;
397+
assert_eq!(next(st), '|');
386398
let first_stmt_index = parse_uint(st);
399+
assert_eq!(next(st), ']');
387400
let block_remainder = region::BlockRemainder {
388401
block: node_id, first_statement_index: first_stmt_index,
389402
};

src/librustc/metadata/tyencode.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -275,9 +275,11 @@ pub fn enc_region(w: &mut Encoder, cx: &ctxt, r: ty::Region) {
275275

276276
fn enc_scope(w: &mut Encoder, _cx: &ctxt, scope: region::CodeExtent) {
277277
match scope {
278+
region::CodeExtent::ParameterScope {
279+
fn_id, body_id } => mywrite!(w, "P[{}|{}]", fn_id, body_id),
278280
region::CodeExtent::Misc(node_id) => mywrite!(w, "M{}", node_id),
279281
region::CodeExtent::Remainder(region::BlockRemainder {
280-
block: b, first_statement_index: i }) => mywrite!(w, "B{}{}", b, i),
282+
block: b, first_statement_index: i }) => mywrite!(w, "B[{}|{}]", b, i),
281283
region::CodeExtent::DestructionScope(node_id) => mywrite!(w, "D{}", node_id),
282284
}
283285
}

src/librustc/middle/region.rs

+33-6
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,15 @@ use syntax::visit::{Visitor, FnKind};
9595
RustcDecodable, Debug, Copy)]
9696
pub enum CodeExtent {
9797
Misc(ast::NodeId),
98-
DestructionScope(ast::NodeId), // extent of destructors for temporaries of node-id
98+
99+
// extent of parameters passed to a function or closure (they
100+
// outlive its body)
101+
ParameterScope { fn_id: ast::NodeId, body_id: ast::NodeId },
102+
103+
// extent of destructors for temporaries of node-id
104+
DestructionScope(ast::NodeId),
105+
106+
// extent of code following a `let id = expr;` binding in a block
99107
Remainder(BlockRemainder)
100108
}
101109

@@ -153,15 +161,19 @@ impl CodeExtent {
153161
pub fn node_id(&self) -> ast::NodeId {
154162
match *self {
155163
CodeExtent::Misc(node_id) => node_id,
164+
165+
// These cases all return rough approximations to the
166+
// precise extent denoted by `self`.
156167
CodeExtent::Remainder(br) => br.block,
157168
CodeExtent::DestructionScope(node_id) => node_id,
169+
CodeExtent::ParameterScope { fn_id: _, body_id } => body_id,
158170
}
159171
}
160172

161173
/// Maps this scope to a potentially new one according to the
162174
/// NodeId transformer `f_id`.
163175
pub fn map_id<F>(&self, f_id: F) -> CodeExtent where
164-
F: FnOnce(ast::NodeId) -> ast::NodeId,
176+
F: Fn(ast::NodeId) -> ast::NodeId,
165177
{
166178
match *self {
167179
CodeExtent::Misc(node_id) => CodeExtent::Misc(f_id(node_id)),
@@ -170,6 +182,8 @@ impl CodeExtent {
170182
block: f_id(br.block), first_statement_index: br.first_statement_index }),
171183
CodeExtent::DestructionScope(node_id) =>
172184
CodeExtent::DestructionScope(f_id(node_id)),
185+
CodeExtent::ParameterScope { fn_id, body_id } =>
186+
CodeExtent::ParameterScope { fn_id: f_id(fn_id), body_id: f_id(body_id) },
173187
}
174188
}
175189

@@ -180,6 +194,7 @@ impl CodeExtent {
180194
match ast_map.find(self.node_id()) {
181195
Some(ast_map::NodeBlock(ref blk)) => {
182196
match *self {
197+
CodeExtent::ParameterScope { .. } |
183198
CodeExtent::Misc(_) |
184199
CodeExtent::DestructionScope(_) => Some(blk.span),
185200

@@ -277,6 +292,7 @@ enum InnermostDeclaringBlock {
277292
Block(ast::NodeId),
278293
Statement(DeclaringStatementContext),
279294
Match(ast::NodeId),
295+
FnDecl { fn_id: ast::NodeId, body_id: ast::NodeId },
280296
}
281297

282298
impl InnermostDeclaringBlock {
@@ -285,6 +301,8 @@ impl InnermostDeclaringBlock {
285301
InnermostDeclaringBlock::None => {
286302
return Option::None;
287303
}
304+
InnermostDeclaringBlock::FnDecl { fn_id, body_id } =>
305+
CodeExtent::ParameterScope { fn_id: fn_id, body_id: body_id },
288306
InnermostDeclaringBlock::Block(id) |
289307
InnermostDeclaringBlock::Match(id) => CodeExtent::from_node_id(id),
290308
InnermostDeclaringBlock::Statement(s) => s.to_code_extent(),
@@ -1198,25 +1216,34 @@ fn resolve_fn(visitor: &mut RegionResolutionVisitor,
11981216
body.id,
11991217
visitor.cx.parent);
12001218

1219+
// This scope covers the function body, which includes the
1220+
// bindings introduced by let statements as well as temporaries
1221+
// created by the fn's tail expression (if any). It does *not*
1222+
// include the fn parameters (see below).
12011223
let body_scope = CodeExtent::from_node_id(body.id);
12021224
visitor.region_maps.mark_as_terminating_scope(body_scope);
12031225

12041226
let dtor_scope = CodeExtent::DestructionScope(body.id);
12051227
visitor.region_maps.record_encl_scope(body_scope, dtor_scope);
12061228

1207-
record_superlifetime(visitor, dtor_scope, body.span);
1229+
let fn_decl_scope = CodeExtent::ParameterScope { fn_id: id, body_id: body.id };
1230+
visitor.region_maps.record_encl_scope(dtor_scope, fn_decl_scope);
1231+
1232+
record_superlifetime(visitor, fn_decl_scope, body.span);
12081233

12091234
if let Some(root_id) = visitor.cx.root_id {
12101235
visitor.region_maps.record_fn_parent(body.id, root_id);
12111236
}
12121237

12131238
let outer_cx = visitor.cx;
12141239

1215-
// The arguments and `self` are parented to the body of the fn.
1240+
// The arguments and `self` are parented to the fn.
12161241
visitor.cx = Context {
12171242
root_id: Some(body.id),
1218-
parent: InnermostEnclosingExpr::Some(body.id),
1219-
var_parent: InnermostDeclaringBlock::Block(body.id)
1243+
parent: InnermostEnclosingExpr::None,
1244+
var_parent: InnermostDeclaringBlock::FnDecl {
1245+
fn_id: id, body_id: body.id
1246+
},
12201247
};
12211248
visit::walk_fn_decl(visitor, decl);
12221249

src/librustc/util/ppaux.rs

+5
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ pub fn explain_region_and_span(cx: &ctxt, region: ty::Region)
113113
};
114114
let scope_decorated_tag = match scope {
115115
region::CodeExtent::Misc(_) => tag,
116+
region::CodeExtent::ParameterScope { .. } => {
117+
"scope of parameters for function"
118+
}
116119
region::CodeExtent::DestructionScope(_) => {
117120
new_string = format!("destruction scope surrounding {}", tag);
118121
&*new_string
@@ -952,6 +955,8 @@ impl<'tcx> Repr<'tcx> for ty::FreeRegion {
952955
impl<'tcx> Repr<'tcx> for region::CodeExtent {
953956
fn repr(&self, _tcx: &ctxt) -> String {
954957
match *self {
958+
region::CodeExtent::ParameterScope { fn_id, body_id } =>
959+
format!("ParameterScope({}, {})", fn_id, body_id),
955960
region::CodeExtent::Misc(node_id) =>
956961
format!("Misc({})", node_id),
957962
region::CodeExtent::DestructionScope(node_id) =>

0 commit comments

Comments
 (0)