Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit d6bf04a

Browse files
committedJan 27, 2015
Add CodeExtent::Remainder variant; pre-req for new scoping/drop rules.
This new variant introduces finer-grain code extents, i.e. we now track that a binding lives only for a suffix of a block, and (importantly) will be dropped when it goes out of scope *before* the bindings that occurred earlier in the block. Both of these notions are neatly captured by marking the block (and each suffix) as an enclosing scope of the next suffix beneath it. This is work that is part of the foundation for issue rust-lang#8861. (It actually has been seen in earlier posted pull requests; I have just factored it out into its own PR to ease my own rebasing.) ---- These finer grained scopes do mean that some code is newly rejected by `rustc`; for example: ```rust let mut map : HashMap<u8, &u8> = HashMap::new(); let tmp = Box::new(2); map.insert(43, &*tmp); ``` This will now fail to compile with a message that `*tmp` does not live long enough, because the scope of `tmp` is now strictly smaller than that of `map`, and the use of `&u8` in map's type requires that the borrowed references are all to data that live at least as long as the map. The usual fix for a case like this is to move the binding for `tmp` up above that of `map`; note that you can still leave the initialization in the original spot, like so: ```rust let tmp; let mut map : HashMap<u8, &u8> = HashMap::new(); tmp = box 2; map.insert(43, &*tmp); ``` Similarly, one can encounter an analogous situation with `Vec`: one would need to rewrite: ```rust let mut vec = Vec::new(); let tmp = 'c'; vec.push(&tmp); ``` as: ``` let tmp; let mut vec = Vec::new(); tmp = 'c'; vec.push(&tmp); ``` ---- In some corner cases, it does not suffice to reorder the bindings; in particular, when the types for both bindings need to reflect exactly the *same* code extent, and a parent/child relationship between them does not work. In pnkfelix's experience this has arisen most often when mixing uses of cyclic data structures while also allowing a lifetime parameter `'a` to flow into a type parameter context where the type is *invariant* with respect to the type parameter. An important instance of this is `arena::TypedArena<T>`, which is invariant with respect to `T`. (The reason that variance is relevant is this: *if* `TypedArena` were covariant with respect to its type parameter, then we could assign it the longer lifetime when it is initialized, and then convert it to a subtype (via covariance) with a shorter lifetime when necessary. But `TypedArena` is invariant with respect to its type parameter, and thus if `S` is a subtype of `T` (in particular, if `S` has a lifetime parameter that is shorter than that of `T`), then a `TypedArena<S>` is unrelated to `TypedArena<T>`.) Concretely, consider code like this: ```rust struct Node<'a> { sibling: Option<&'a Node<'a>> } struct Context<'a> { // because of this field, `Context<'a>` is invariant with respect to `'a`. arena: &'a TypedArena<Node<'a>>, ... } fn new_ctxt<'a>(arena: &'a TypedArena<Node<'a>>) -> Context<'a> { ... } fn use_ctxt<'a>(fcx: &'a Context<'a>) { ... } let arena = TypedArena::new(); let ctxt = new_ctxt(&arena); use_ctxt(&ctxt); ``` In these situations, if you try to introduce two bindings via two distinct `let` statements, each is (with this commit) assigned a distinct extent, and the region inference system cannot find a single region to assign to the lifetime `'a` that works for both of the bindings. So you get an error that `ctxt` does not live long enough; but moving its binding up above that of `arena` just shifts the error so now the compiler complains that `arena` does not live long enough. SO: What to do? The easiest fix in this case is to ensure that the two bindings *do* get assigned the same static extent, by stuffing both bindings into the same let statement, like so: ```rust let (arena, ctxt): (TypedArena, Context); arena = TypedArena::new(); ctxt = new_ctxt(&arena); use_ctxt(&ctxt); ``` Due to the new code rejections outlined above, this is a ... [breaking-change]
1 parent 43d08f8 commit d6bf04a

File tree

6 files changed

+351
-81
lines changed

6 files changed

+351
-81
lines changed
 

‎src/librustc/metadata/tydecode.rs

+8
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,14 @@ fn parse_scope(st: &mut PState) -> region::CodeExtent {
377377
let node_id = parse_uint(st) as ast::NodeId;
378378
region::CodeExtent::Misc(node_id)
379379
}
380+
'B' => {
381+
let node_id = parse_uint(st) as ast::NodeId;
382+
let first_stmt_index = parse_uint(st);
383+
let block_remainder = region::BlockRemainder {
384+
block: node_id, first_statement_index: first_stmt_index,
385+
};
386+
region::CodeExtent::Remainder(block_remainder)
387+
}
380388
_ => panic!("parse_scope: bad input")
381389
}
382390
}

‎src/librustc/metadata/tyencode.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,9 @@ pub fn enc_region(w: &mut SeekableMemWriter, cx: &ctxt, r: ty::Region) {
276276

277277
fn enc_scope(w: &mut SeekableMemWriter, _cx: &ctxt, scope: region::CodeExtent) {
278278
match scope {
279-
region::CodeExtent::Misc(node_id) => mywrite!(w, "M{}", node_id)
279+
region::CodeExtent::Misc(node_id) => mywrite!(w, "M{}", node_id),
280+
region::CodeExtent::Remainder(region::BlockRemainder {
281+
block: b, first_statement_index: i }) => mywrite!(w, "B{}{}", b, i),
280282
}
281283
}
282284

‎src/librustc/middle/region.rs

+238-51
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use syntax::codemap::{self, Span};
2626
use syntax::{ast, visit};
2727
use syntax::ast::{Block, Item, FnDecl, NodeId, Arm, Pat, Stmt, Expr, Local};
2828
use syntax::ast_util::{stmt_id};
29+
use syntax::ast_map;
2930
use syntax::visit::{Visitor, FnKind};
3031

3132
/// CodeExtent represents a statically-describable extent that can be
@@ -38,7 +39,32 @@ use syntax::visit::{Visitor, FnKind};
3839
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, RustcEncodable,
3940
RustcDecodable, Show, Copy)]
4041
pub enum CodeExtent {
41-
Misc(ast::NodeId)
42+
Misc(ast::NodeId),
43+
Remainder(BlockRemainder),
44+
}
45+
46+
/// Represents a subscope of `block` for a binding that is introduced
47+
/// by `block.stmts[first_statement_index]`. Such subscopes represent
48+
/// a suffix of the block. Note that each subscope does not include
49+
/// the initializer expression, if any, for the statement indexed by
50+
/// `first_statement_index`.
51+
///
52+
/// For example, given `{ let (a, b) = EXPR_1; let c = EXPR_2; ... }`:
53+
///
54+
/// * the subscope with `first_statement_index == 0` is scope of both
55+
/// `a` and `b`; it does not include EXPR_1, but does include
56+
/// everything after that first `let`. (If you want a scope that
57+
/// includes EXPR_1 as well, then do not use `CodeExtent::Remainder`,
58+
/// but instead another `CodeExtent` that encompasses the whole block,
59+
/// e.g. `CodeExtent::Misc`.
60+
///
61+
/// * the subscope with `first_statement_index == 1` is scope of `c`,
62+
/// and thus does not include EXPR_2, but covers the `...`.
63+
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, RustcEncodable,
64+
RustcDecodable, Show, Copy)]
65+
pub struct BlockRemainder {
66+
pub block: ast::NodeId,
67+
pub first_statement_index: uint,
4268
}
4369

4470
impl CodeExtent {
@@ -55,6 +81,7 @@ impl CodeExtent {
5581
pub fn node_id(&self) -> ast::NodeId {
5682
match *self {
5783
CodeExtent::Misc(node_id) => node_id,
84+
CodeExtent::Remainder(br) => br.block,
5885
}
5986
}
6087

@@ -65,16 +92,50 @@ impl CodeExtent {
6592
{
6693
match *self {
6794
CodeExtent::Misc(node_id) => CodeExtent::Misc(f_id(node_id)),
95+
CodeExtent::Remainder(br) =>
96+
CodeExtent::Remainder(BlockRemainder {
97+
block: f_id(br.block), first_statement_index: br.first_statement_index }),
6898
}
6999
}
100+
101+
/// Returns the span of this CodeExtent. Note that in general the
102+
/// returned span may not correspond to the span of any node id in
103+
/// the AST.
104+
pub fn span(&self, ast_map: &ast_map::Map) -> Option<Span> {
105+
match ast_map.find(self.node_id()) {
106+
Some(ast_map::NodeBlock(ref blk)) => {
107+
match *self {
108+
CodeExtent::Misc(_) => Some(blk.span),
109+
110+
CodeExtent::Remainder(r) => {
111+
assert_eq!(r.block, blk.id);
112+
// Want span for extent starting after the
113+
// indexed statement and ending at end of
114+
// `blk`; reuse span of `blk` and shift `lo`
115+
// forward to end of indexed statement.
116+
//
117+
// (This is the special case aluded to in the
118+
// doc-comment for this method)
119+
let stmt_span = blk.stmts[r.first_statement_index].span;
120+
Some(Span { lo: stmt_span.hi, ..blk.span })
121+
}
122+
}
123+
}
124+
Some(ast_map::NodeExpr(ref expr)) => Some(expr.span),
125+
Some(ast_map::NodeStmt(ref stmt)) => Some(stmt.span),
126+
Some(ast_map::NodeItem(ref item)) => Some(item.span),
127+
Some(_) | None => None,
128+
}
129+
}
70130
}
71131

72132
/// The region maps encode information about region relationships.
73133
///
74134
/// - `scope_map` maps from a scope id to the enclosing scope id; this is
75135
/// usually corresponding to the lexical nesting, though in the case of
76136
/// closures the parent scope is the innermost conditional expression or repeating
77-
/// block
137+
/// block. (Note that the enclosing scope id for the block
138+
/// associated with a closure is the closure itself.)
78139
///
79140
/// - `var_map` maps from a variable or binding id to the block in which
80141
/// that variable is declared.
@@ -115,12 +176,77 @@ pub struct RegionMaps {
115176
terminating_scopes: RefCell<FnvHashSet<CodeExtent>>,
116177
}
117178

118-
#[derive(Copy)]
179+
/// Carries the node id for the innermost block or match expression,
180+
/// for building up the `var_map` which maps ids to the blocks in
181+
/// which they were declared.
182+
#[derive(PartialEq, Eq, Show, Copy)]
183+
enum InnermostDeclaringBlock {
184+
None,
185+
Block(ast::NodeId),
186+
Statement(DeclaringStatementContext),
187+
Match(ast::NodeId),
188+
}
189+
190+
impl InnermostDeclaringBlock {
191+
fn to_code_extent(&self) -> Option<CodeExtent> {
192+
let extent = match *self {
193+
InnermostDeclaringBlock::None => {
194+
return Option::None;
195+
}
196+
InnermostDeclaringBlock::Block(id) |
197+
InnermostDeclaringBlock::Match(id) => CodeExtent::from_node_id(id),
198+
InnermostDeclaringBlock::Statement(s) => s.to_code_extent(),
199+
};
200+
Option::Some(extent)
201+
}
202+
}
203+
204+
/// Contextual information for declarations introduced by a statement
205+
/// (i.e. `let`). It carries node-id's for statement and enclosing
206+
/// block both, as well as the statement's index within the block.
207+
#[derive(PartialEq, Eq, Show, Copy)]
208+
struct DeclaringStatementContext {
209+
stmt_id: ast::NodeId,
210+
block_id: ast::NodeId,
211+
stmt_index: uint,
212+
}
213+
214+
impl DeclaringStatementContext {
215+
fn to_code_extent(&self) -> CodeExtent {
216+
CodeExtent::Remainder(BlockRemainder {
217+
block: self.block_id,
218+
first_statement_index: self.stmt_index,
219+
})
220+
}
221+
}
222+
223+
#[derive(PartialEq, Eq, Show, Copy)]
224+
enum InnermostEnclosingExpr {
225+
None,
226+
Some(ast::NodeId),
227+
Statement(DeclaringStatementContext),
228+
}
229+
230+
impl InnermostEnclosingExpr {
231+
fn to_code_extent(&self) -> Option<CodeExtent> {
232+
let extent = match *self {
233+
InnermostEnclosingExpr::None => {
234+
return Option::None;
235+
}
236+
InnermostEnclosingExpr::Statement(s) =>
237+
s.to_code_extent(),
238+
InnermostEnclosingExpr::Some(parent_id) =>
239+
CodeExtent::from_node_id(parent_id),
240+
};
241+
Some(extent)
242+
}
243+
}
244+
245+
#[derive(Show, Copy)]
119246
pub struct Context {
120-
var_parent: Option<ast::NodeId>,
247+
var_parent: InnermostDeclaringBlock,
121248

122-
// Innermost enclosing expression
123-
parent: Option<ast::NodeId>,
249+
parent: InnermostEnclosingExpr,
124250
}
125251

126252
struct RegionResolutionVisitor<'a> {
@@ -381,16 +507,13 @@ impl RegionMaps {
381507
}
382508
}
383509

384-
/// Records the current parent (if any) as the parent of `child_id`.
510+
/// Records the current parent (if any) as the parent of `child_scope`.
385511
fn record_superlifetime(visitor: &mut RegionResolutionVisitor,
386-
child_id: ast::NodeId,
512+
child_scope: CodeExtent,
387513
_sp: Span) {
388-
match visitor.cx.parent {
389-
Some(parent_id) => {
390-
let child_scope = CodeExtent::from_node_id(child_id);
391-
let parent_scope = CodeExtent::from_node_id(parent_id);
392-
visitor.region_maps.record_encl_scope(child_scope, parent_scope);
393-
}
514+
match visitor.cx.parent.to_code_extent() {
515+
Some(parent_scope) =>
516+
visitor.region_maps.record_encl_scope(child_scope, parent_scope),
394517
None => {}
395518
}
396519
}
@@ -399,11 +522,9 @@ fn record_superlifetime(visitor: &mut RegionResolutionVisitor,
399522
fn record_var_lifetime(visitor: &mut RegionResolutionVisitor,
400523
var_id: ast::NodeId,
401524
_sp: Span) {
402-
match visitor.cx.var_parent {
403-
Some(parent_id) => {
404-
let parent_scope = CodeExtent::from_node_id(parent_id);
405-
visitor.region_maps.record_var_scope(var_id, parent_scope);
406-
}
525+
match visitor.cx.var_parent.to_code_extent() {
526+
Some(parent_scope) =>
527+
visitor.region_maps.record_var_scope(var_id, parent_scope),
407528
None => {
408529
// this can happen in extern fn declarations like
409530
//
@@ -415,21 +536,72 @@ fn record_var_lifetime(visitor: &mut RegionResolutionVisitor,
415536
fn resolve_block(visitor: &mut RegionResolutionVisitor, blk: &ast::Block) {
416537
debug!("resolve_block(blk.id={:?})", blk.id);
417538

418-
// Record the parent of this block.
419-
record_superlifetime(visitor, blk.id, blk.span);
539+
let prev_cx = visitor.cx;
540+
541+
let blk_scope = CodeExtent::Misc(blk.id);
542+
record_superlifetime(visitor, blk_scope, blk.span);
420543

421544
// We treat the tail expression in the block (if any) somewhat
422545
// differently from the statements. The issue has to do with
423-
// temporary lifetimes. If the user writes:
546+
// temporary lifetimes. Consider the following:
424547
//
425-
// {
426-
// ... (&foo()) ...
427-
// }
548+
// quux({
549+
// let inner = ... (&bar()) ...;
428550
//
551+
// (... (&foo()) ...) // (the tail expression)
552+
// }, other_argument());
553+
//
554+
// Each of the statements within the block is a terminating
555+
// scope, and thus a temporary (e.g. the result of calling
556+
// `bar()` in the initalizer expression for `let inner = ...;`)
557+
// will be cleaned up immediately after its corresponding
558+
// statement (i.e. `let inner = ...;`) executes.
559+
//
560+
// On the other hand, temporaries associated with evaluating the
561+
// tail expression for the block are assigned lifetimes so that
562+
// they will be cleaned up as part of the terminating scope
563+
// *surrounding* the block expression. Here, the terminating
564+
// scope for the block expression is the `quux(..)` call; so
565+
// those temporaries will only be cleaned up *after* both
566+
// `other_argument()` has run and also the call to `quux(..)`
567+
// itself has returned.
568+
569+
visitor.cx = Context {
570+
var_parent: InnermostDeclaringBlock::Block(blk.id),
571+
parent: InnermostEnclosingExpr::Some(blk.id),
572+
};
573+
574+
{
575+
// This block should be kept approximately in sync with
576+
// `visit::walk_block`. (We manually walk the block, rather
577+
// than call `walk_block`, in order to maintain precise
578+
// `InnermostDeclaringBlock` information.)
579+
580+
for (i, statement) in blk.stmts.iter().enumerate() {
581+
if let ast::StmtDecl(_, stmt_id) = statement.node {
582+
// Each StmtDecl introduces a subscope for bindings
583+
// introduced by the declaration; this subscope covers
584+
// a suffix of the block . Each subscope in a block
585+
// has the previous subscope in the block as a parent,
586+
// except for the first such subscope, which has the
587+
// block itself as a parent.
588+
let declaring = DeclaringStatementContext {
589+
stmt_id: stmt_id,
590+
block_id: blk.id,
591+
stmt_index: i,
592+
};
593+
record_superlifetime(
594+
visitor, declaring.to_code_extent(), statement.span);
595+
visitor.cx = Context {
596+
var_parent: InnermostDeclaringBlock::Statement(declaring),
597+
parent: InnermostEnclosingExpr::Statement(declaring),
598+
};
599+
}
600+
visitor.visit_stmt(&**statement)
601+
}
602+
visit::walk_expr_opt(visitor, &blk.expr)
603+
}
429604

430-
let prev_cx = visitor.cx;
431-
visitor.cx = Context {var_parent: Some(blk.id), parent: Some(blk.id)};
432-
visit::walk_block(visitor, blk);
433605
visitor.cx = prev_cx;
434606
}
435607

@@ -449,7 +621,7 @@ fn resolve_arm(visitor: &mut RegionResolutionVisitor, arm: &ast::Arm) {
449621
}
450622

451623
fn resolve_pat(visitor: &mut RegionResolutionVisitor, pat: &ast::Pat) {
452-
record_superlifetime(visitor, pat.id, pat.span);
624+
record_superlifetime(visitor, CodeExtent::from_node_id(pat.id), pat.span);
453625

454626
// If this is a binding (or maybe a binding, I'm too lazy to check
455627
// the def map) then record the lifetime of that binding.
@@ -468,22 +640,29 @@ fn resolve_stmt(visitor: &mut RegionResolutionVisitor, stmt: &ast::Stmt) {
468640
debug!("resolve_stmt(stmt.id={:?})", stmt_id);
469641

470642
let stmt_scope = CodeExtent::from_node_id(stmt_id);
643+
644+
// Every statement will clean up the temporaries created during
645+
// execution of that statement. Therefore each statement has an
646+
// associated destruction scope that represents the extent of the
647+
// statement plus its destructors, and thus the extent for which
648+
// regions referenced by the destructors need to survive.
471649
visitor.region_maps.mark_as_terminating_scope(stmt_scope);
472-
record_superlifetime(visitor, stmt_id, stmt.span);
650+
record_superlifetime(visitor, stmt_scope, stmt.span);
473651

474652
let prev_parent = visitor.cx.parent;
475-
visitor.cx.parent = Some(stmt_id);
653+
visitor.cx.parent = InnermostEnclosingExpr::Some(stmt_id);
476654
visit::walk_stmt(visitor, stmt);
477655
visitor.cx.parent = prev_parent;
478656
}
479657

480658
fn resolve_expr(visitor: &mut RegionResolutionVisitor, expr: &ast::Expr) {
481659
debug!("resolve_expr(expr.id={:?})", expr.id);
482660

483-
record_superlifetime(visitor, expr.id, expr.span);
661+
let expr_scope = CodeExtent::Misc(expr.id);
662+
record_superlifetime(visitor, expr_scope, expr.span);
484663

485664
let prev_cx = visitor.cx;
486-
visitor.cx.parent = Some(expr.id);
665+
visitor.cx.parent = InnermostEnclosingExpr::Some(expr.id);
487666

488667
{
489668
let region_maps = &mut visitor.region_maps;
@@ -527,11 +706,11 @@ fn resolve_expr(visitor: &mut RegionResolutionVisitor, expr: &ast::Expr) {
527706

528707
// The variable parent of everything inside (most importantly, the
529708
// pattern) is the body.
530-
visitor.cx.var_parent = Some(body.id);
709+
visitor.cx.var_parent = InnermostDeclaringBlock::Block(body.id);
531710
}
532711

533712
ast::ExprMatch(..) => {
534-
visitor.cx.var_parent = Some(expr.id);
713+
visitor.cx.var_parent = InnermostDeclaringBlock::Match(expr.id);
535714
}
536715

537716
ast::ExprAssignOp(..) | ast::ExprIndex(..) |
@@ -568,19 +747,13 @@ fn resolve_local(visitor: &mut RegionResolutionVisitor, local: &ast::Local) {
568747
debug!("resolve_local(local.id={:?},local.init={:?})",
569748
local.id,local.init.is_some());
570749

571-
let blk_id = match visitor.cx.var_parent {
572-
Some(id) => id,
573-
None => {
574-
visitor.sess.span_bug(
575-
local.span,
576-
"local without enclosing block");
577-
}
578-
};
579-
580750
// For convenience in trans, associate with the local-id the var
581751
// scope that will be used for any bindings declared in this
582752
// pattern.
583-
let blk_scope = CodeExtent::from_node_id(blk_id);
753+
let blk_scope = visitor.cx.var_parent.to_code_extent()
754+
.unwrap_or_else(|| visitor.sess.span_bug(
755+
local.span, "local without enclosing block"));
756+
584757
visitor.region_maps.record_var_scope(local.id, blk_scope);
585758

586759
// As an exception to the normal rules governing temporary
@@ -803,7 +976,10 @@ fn resolve_local(visitor: &mut RegionResolutionVisitor, local: &ast::Local) {
803976
fn resolve_item(visitor: &mut RegionResolutionVisitor, item: &ast::Item) {
804977
// Items create a new outer block scope as far as we're concerned.
805978
let prev_cx = visitor.cx;
806-
visitor.cx = Context {var_parent: None, parent: None};
979+
visitor.cx = Context {
980+
var_parent: InnermostDeclaringBlock::None,
981+
parent: InnermostEnclosingExpr::None
982+
};
807983
visit::walk_item(visitor, item);
808984
visitor.cx = prev_cx;
809985
}
@@ -829,15 +1005,20 @@ fn resolve_fn(visitor: &mut RegionResolutionVisitor,
8291005
let outer_cx = visitor.cx;
8301006

8311007
// The arguments and `self` are parented to the body of the fn.
832-
visitor.cx = Context { parent: Some(body.id),
833-
var_parent: Some(body.id) };
1008+
visitor.cx = Context {
1009+
parent: InnermostEnclosingExpr::Some(body.id),
1010+
var_parent: InnermostDeclaringBlock::Block(body.id)
1011+
};
8341012
visit::walk_fn_decl(visitor, decl);
8351013

8361014
// The body of the fn itself is either a root scope (top-level fn)
8371015
// or it continues with the inherited scope (closures).
8381016
match fk {
8391017
visit::FkItemFn(..) | visit::FkMethod(..) => {
840-
visitor.cx = Context { parent: None, var_parent: None };
1018+
visitor.cx = Context {
1019+
parent: InnermostEnclosingExpr::None,
1020+
var_parent: InnermostDeclaringBlock::None
1021+
};
8411022
visitor.visit_block(body);
8421023
visitor.cx = outer_cx;
8431024
}
@@ -898,7 +1079,10 @@ pub fn resolve_crate(sess: &Session, krate: &ast::Crate) -> RegionMaps {
8981079
let mut visitor = RegionResolutionVisitor {
8991080
sess: sess,
9001081
region_maps: &maps,
901-
cx: Context { parent: None, var_parent: None }
1082+
cx: Context {
1083+
parent: InnermostEnclosingExpr::None,
1084+
var_parent: InnermostDeclaringBlock::None,
1085+
}
9021086
};
9031087
visit::walk_crate(&mut visitor, krate);
9041088
}
@@ -911,7 +1095,10 @@ pub fn resolve_inlined_item(sess: &Session,
9111095
let mut visitor = RegionResolutionVisitor {
9121096
sess: sess,
9131097
region_maps: region_maps,
914-
cx: Context { parent: None, var_parent: None }
1098+
cx: Context {
1099+
parent: InnermostEnclosingExpr::None,
1100+
var_parent: InnermostDeclaringBlock::None
1101+
}
9151102
};
9161103
visit::walk_inlined_item(&mut visitor, item);
9171104
}

‎src/librustc/util/ppaux.rs

+44-28
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111

1212
use middle::def;
13+
use middle::region;
1314
use middle::subst::{VecPerParamSpace,Subst};
1415
use middle::subst;
1516
use middle::ty::{BoundRegion, BrAnon, BrNamed};
@@ -84,37 +85,41 @@ pub fn explain_region_and_span(cx: &ctxt, region: ty::Region)
8485
-> (String, Option<Span>) {
8586
return match region {
8687
ReScope(scope) => {
87-
match cx.map.find(scope.node_id()) {
88-
Some(ast_map::NodeBlock(ref blk)) => {
89-
explain_span(cx, "block", blk.span)
90-
}
91-
Some(ast_map::NodeExpr(expr)) => {
92-
match expr.node {
93-
ast::ExprCall(..) => explain_span(cx, "call", expr.span),
94-
ast::ExprMethodCall(..) => {
95-
explain_span(cx, "method call", expr.span)
96-
},
97-
ast::ExprMatch(_, _, ast::MatchSource::IfLetDesugar { .. }) =>
98-
explain_span(cx, "if let", expr.span),
99-
ast::ExprMatch(_, _, ast::MatchSource::WhileLetDesugar) => {
100-
explain_span(cx, "while let", expr.span)
101-
},
102-
ast::ExprMatch(..) => explain_span(cx, "match", expr.span),
103-
_ => explain_span(cx, "expression", expr.span)
104-
}
105-
}
106-
Some(ast_map::NodeStmt(stmt)) => {
107-
explain_span(cx, "statement", stmt.span)
108-
}
109-
Some(ast_map::NodeItem(it)) => {
110-
let tag = item_scope_tag(&*it);
111-
explain_span(cx, tag, it.span)
112-
}
88+
let new_string;
89+
let on_unknown_scope = |&:| {
90+
(format!("unknown scope: {:?}. Please report a bug.", scope), None)
91+
};
92+
let span = match scope.span(&cx.map) {
93+
Some(s) => s,
94+
None => return on_unknown_scope(),
95+
};
96+
let tag = match cx.map.find(scope.node_id()) {
97+
Some(ast_map::NodeBlock(_)) => "block",
98+
Some(ast_map::NodeExpr(expr)) => match expr.node {
99+
ast::ExprCall(..) => "call",
100+
ast::ExprMethodCall(..) => "method call",
101+
ast::ExprMatch(_, _, ast::MatchSource::IfLetDesugar { .. }) => "if let",
102+
ast::ExprMatch(_, _, ast::MatchSource::WhileLetDesugar) => "while let",
103+
ast::ExprMatch(..) => "match",
104+
_ => "expression",
105+
},
106+
Some(ast_map::NodeStmt(_)) => "statement",
107+
Some(ast_map::NodeItem(it)) => item_scope_tag(&*it),
113108
Some(_) | None => {
114109
// this really should not happen
115-
(format!("unknown scope: {:?}. Please report a bug.", scope), None)
110+
return on_unknown_scope();
116111
}
117-
}
112+
};
113+
let scope_decorated_tag = match scope {
114+
region::CodeExtent::Misc(_) => tag,
115+
region::CodeExtent::Remainder(r) => {
116+
new_string = format!("block suffix following statement {}",
117+
r.first_statement_index);
118+
new_string.as_slice()
119+
}
120+
};
121+
explain_span(cx, scope_decorated_tag, span)
122+
118123
}
119124

120125
ReFree(ref fr) => {
@@ -867,6 +872,17 @@ impl<'tcx> Repr<'tcx> for ty::FreeRegion {
867872
}
868873
}
869874

875+
impl<'tcx> Repr<'tcx> for region::CodeExtent {
876+
fn repr(&self, _tcx: &ctxt) -> String {
877+
match *self {
878+
region::CodeExtent::Misc(node_id) =>
879+
format!("Misc({})", node_id),
880+
region::CodeExtent::Remainder(rem) =>
881+
format!("Remainder({}, {})", rem.block, rem.first_statement_index),
882+
}
883+
}
884+
}
885+
870886
impl<'tcx> Repr<'tcx> for ast::DefId {
871887
fn repr(&self, tcx: &ctxt) -> String {
872888
// Unfortunately, there seems to be no way to attempt to print
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn f() {
12+
let old = ['o']; // statement 0
13+
let mut v1 = Vec::new(); // statement 1
14+
15+
let mut v2 = Vec::new(); // statement 2
16+
//~^ NOTE reference must be valid for the block suffix following statement 2
17+
18+
let young = ['y']; // statement 3
19+
//~^ NOTE ...but borrowed value is only valid for the block suffix following statement 3
20+
21+
v2.push(&young[0]); // statement 4
22+
//~^ ERROR `young[..]` does not live long enough
23+
24+
let mut v3 = Vec::new(); // statement 5
25+
//~^ NOTE reference must be valid for the block suffix following statement 5
26+
27+
v3.push(&'x'); // statement 6
28+
//~^ ERROR borrowed value does not live long enough
29+
//~| NOTE ...but borrowed value is only valid for the statement
30+
//~| HELP consider using a `let` binding to increase its lifetime
31+
32+
{
33+
34+
let mut v4 = Vec::new(); // (sub) statement 0
35+
//~^ NOTE reference must be valid for the block suffix following statement 0
36+
37+
v4.push(&'y');
38+
//~^ ERROR borrowed value does not live long enough
39+
//~| NOTE ...but borrowed value is only valid for the statement
40+
//~| HELP consider using a `let` binding to increase its lifetime
41+
42+
} // (statement 7)
43+
44+
let mut v5 = Vec::new(); // statement 8
45+
//~^ NOTE reference must be valid for the block suffix following statement 8
46+
47+
v5.push(&'z');
48+
//~^ ERROR borrowed value does not live long enough
49+
//~| NOTE ...but borrowed value is only valid for the statement
50+
//~| HELP consider using a `let` binding to increase its lifetime
51+
52+
v1.push(&old[0]);
53+
}
54+
55+
fn main() {
56+
f();
57+
}

‎src/test/compile-fail/borrowck-let-suggestion.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
fn f() {
1212
let x = [1is].iter(); //~ ERROR borrowed value does not live long enough
13-
//~^^ NOTE reference must be valid for the block
13+
//~^ NOTE reference must be valid for the block suffix following statement
1414
//~^^ HELP consider using a `let` binding to increase its lifetime
1515
}
1616

0 commit comments

Comments
 (0)
Please sign in to comment.