Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More precise spans for temps and their drops #55781

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 3 additions & 13 deletions src/librustc_mir/build/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {

let source_info = this.source_info(span);
for stmt in stmts {
let Stmt { kind, opt_destruction_scope } = this.hir.mirror(stmt);
let Stmt { kind, opt_destruction_scope, span: stmt_span } = this.hir.mirror(stmt);
match kind {
StmtKind::Expr { scope, expr } => {
this.block_context.push(BlockFrame::Statement { ignores_expr_result: true });
Expand All @@ -99,7 +99,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let si = (scope, source_info);
this.in_scope(si, LintLevel::Inherited, block, |this| {
let expr = this.hir.mirror(expr);
this.stmt_expr(block, expr)
this.stmt_expr(block, expr, Some(stmt_span))
})
}));
}
Expand Down Expand Up @@ -177,17 +177,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
let destination_ty = destination.ty(&this.local_decls, tcx).to_ty(tcx);
if let Some(expr) = expr {
let tail_result_is_ignored = destination_ty.is_unit() ||
match this.block_context.last() {
// no context: conservatively assume result is read
None => false,

// sub-expression: block result feeds into some computation
Some(BlockFrame::SubExpr) => false,

// otherwise: use accumualated is_ignored state.
Some(BlockFrame::TailExpr { tail_result_is_ignored: ignored }) |
Some(BlockFrame::Statement { ignores_expr_result: ignored }) => *ignored,
};
this.block_context.currently_ignores_tail_results();
this.block_context.push(BlockFrame::TailExpr { tail_result_is_ignored });

unpack!(block = this.into(destination, block, expr));
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/build/expr/as_rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
block.and(Rvalue::Aggregate(adt, fields))
}
ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
block = unpack!(this.stmt_expr(block, expr));
block = unpack!(this.stmt_expr(block, expr, None));
block.and(this.unit_rvalue())
}
ExprKind::Yield { value } => {
Expand Down
16 changes: 3 additions & 13 deletions src/librustc_mir/build/expr/as_temp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

//! See docs in build/expr/mod.rs

use build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
use build::{BlockAnd, BlockAndExtension, Builder};
use hair::*;
use rustc::middle::region;
use rustc::mir::*;
Expand Down Expand Up @@ -68,19 +68,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
debug!("creating temp {:?} with block_context: {:?}", local_decl, this.block_context);
// Find out whether this temp is being created within the
// tail expression of a block whose result is ignored.
for bf in this.block_context.iter().rev() {
match bf {
BlockFrame::SubExpr => continue,
BlockFrame::Statement { .. } => break,
&BlockFrame::TailExpr { tail_result_is_ignored } => {
local_decl = local_decl.block_tail(BlockTailInfo {
tail_result_is_ignored
});
break;
}
}
if let Some(tail_info) = this.block_context.currently_in_block_tail() {
local_decl = local_decl.block_tail(tail_info);
}

this.local_decls.push(local_decl)
};
if !expr_ty.is_never() {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/build/expr/into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
| ExprKind::Break { .. }
| ExprKind::InlineAsm { .. }
| ExprKind::Return { .. } => {
unpack!(block = this.stmt_expr(block, expr));
unpack!(block = this.stmt_expr(block, expr, None));
this.cfg.push_assign_unit(block, source_info, destination);
block.unit()
}
Expand Down
66 changes: 62 additions & 4 deletions src/librustc_mir/build/expr/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,18 @@ use hair::*;
use rustc::mir::*;

impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
pub fn stmt_expr(&mut self, mut block: BasicBlock, expr: Expr<'tcx>) -> BlockAnd<()> {
/// Builds a block of MIR statements to evaluate the HAIR `expr`.
/// If the original expression was an AST statement,
/// (e.g. `some().code(&here());`) then `opt_stmt_span` is the
/// span of that statement (including its semicolon, if any).
/// Diagnostics use this span (which may be larger than that of
/// `expr`) to identify when statement temporaries are dropped.
pub fn stmt_expr(&mut self,
mut block: BasicBlock,
expr: Expr<'tcx>,
opt_stmt_span: Option<StatementSpan>)
-> BlockAnd<()>
{
let this = self;
let expr_span = expr.span;
let source_info = this.source_info(expr.span);
Expand All @@ -29,7 +40,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
} => {
let value = this.hir.mirror(value);
this.in_scope((region_scope, source_info), lint_level, block, |this| {
this.stmt_expr(block, value)
this.stmt_expr(block, value, opt_stmt_span)
})
}
ExprKind::Assign { lhs, rhs } => {
Expand Down Expand Up @@ -190,9 +201,56 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
}
_ => {
let expr_ty = expr.ty;
let temp = this.temp(expr.ty.clone(), expr_span);

// Issue #54382: When creating temp for the value of
// expression like:
//
// `{ side_effects(); { let l = stuff(); the_value } }`
//
// it is usually better to focus on `the_value` rather
// than the entirety of block(s) surrounding it.
let mut temp_span = expr_span;
let mut temp_in_tail_of_block = false;
if let ExprKind::Block { body } = expr.kind {
if let Some(tail_expr) = &body.expr {
let mut expr = tail_expr;
while let rustc::hir::ExprKind::Block(subblock, _label) = &expr.node {
if let Some(subtail_expr) = &subblock.expr {
expr = subtail_expr
} else {
break;
}
}
temp_span = expr.span;
temp_in_tail_of_block = true;
}
}

let temp = {
let mut local_decl = LocalDecl::new_temp(expr.ty.clone(), temp_span);
if temp_in_tail_of_block {
if this.block_context.currently_ignores_tail_results() {
local_decl = local_decl.block_tail(BlockTailInfo {
tail_result_is_ignored: true
});
}
}
let temp = this.local_decls.push(local_decl);
let place = Place::Local(temp);
debug!("created temp {:?} for expr {:?} in block_context: {:?}",
temp, expr, this.block_context);
place
};
unpack!(block = this.into(&temp, block, expr));
unpack!(block = this.build_drop(block, expr_span, temp, expr_ty));

// Attribute drops of the statement's temps to the
// semicolon at the statement's end.
let drop_point = this.hir.tcx().sess.source_map().end_point(match opt_stmt_span {
None => expr_span,
Some(StatementSpan(span)) => span,
});

unpack!(block = this.build_drop(block, drop_point, temp, expr_ty));
block.unit()
}
}
Expand Down
56 changes: 54 additions & 2 deletions src/librustc_mir/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,9 @@ impl BlockFrame {
}
}

#[derive(Debug)]
struct BlockContext(Vec<BlockFrame>);

struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
hir: Cx<'a, 'gcx, 'tcx>,
cfg: CFG<'tcx>,
Expand All @@ -359,7 +362,7 @@ struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
/// start just throwing new entries onto that vector in order to
/// distinguish the context of EXPR1 from the context of EXPR2 in
/// `{ STMTS; EXPR1 } + EXPR2`
block_context: Vec<BlockFrame>,
block_context: BlockContext,

/// The current unsafe block in scope, even if it is hidden by
/// a PushUnsafeBlock
Expand Down Expand Up @@ -409,6 +412,55 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
}
}

impl BlockContext {
fn new() -> Self { BlockContext(vec![]) }
fn push(&mut self, bf: BlockFrame) { self.0.push(bf); }
fn pop(&mut self) -> Option<BlockFrame> { self.0.pop() }

/// Traverses the frames on the BlockContext, searching for either
/// the first block-tail expression frame with no intervening
/// statement frame.
///
/// Notably, this skips over `SubExpr` frames; this method is
/// meant to be used in the context of understanding the
/// relationship of a temp (created within some complicated
/// expression) with its containing expression, and whether the
/// value of that *containing expression* (not the temp!) is
/// ignored.
fn currently_in_block_tail(&self) -> Option<BlockTailInfo> {
for bf in self.0.iter().rev() {
match bf {
BlockFrame::SubExpr => continue,
BlockFrame::Statement { .. } => break,
&BlockFrame::TailExpr { tail_result_is_ignored } =>
return Some(BlockTailInfo { tail_result_is_ignored })
}
}

return None;
}

/// Looks at the topmost frame on the BlockContext and reports
/// whether its one that would discard a block tail result.
///
/// Unlike `currently_within_ignored_tail_expression`, this does
/// *not* skip over `SubExpr` frames: here, we want to know
/// whether the block result itself is discarded.
fn currently_ignores_tail_results(&self) -> bool {
match self.0.last() {
// no context: conservatively assume result is read
None => false,

// sub-expression: block result feeds into some computation
Some(BlockFrame::SubExpr) => false,

// otherwise: use accumulated is_ignored state.
Some(BlockFrame::TailExpr { tail_result_is_ignored: ignored }) |
Some(BlockFrame::Statement { ignores_expr_result: ignored }) => *ignored,
}
}
}

#[derive(Debug)]
enum LocalsForNode {
/// In the usual case, a node-id for an identifier maps to at most
Expand Down Expand Up @@ -764,7 +816,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
fn_span: span,
arg_count,
scopes: vec![],
block_context: vec![],
block_context: BlockContext::new(),
source_scopes: IndexVec::new(),
source_scope: OUTERMOST_SOURCE_SCOPE,
source_scope_local_data: IndexVec::new(),
Expand Down
3 changes: 3 additions & 0 deletions src/librustc_mir/hair/cx/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
for (index, stmt) in stmts.iter().enumerate() {
let hir_id = cx.tcx.hir.node_to_hir_id(stmt.node.id());
let opt_dxn_ext = cx.region_scope_tree.opt_destruction_scope(hir_id.local_id);
let stmt_span = StatementSpan(cx.tcx.hir.span(stmt.node.id()));
match stmt.node {
hir::StmtKind::Expr(ref expr, _) |
hir::StmtKind::Semi(ref expr, _) => {
Expand All @@ -69,6 +70,7 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
expr: expr.to_ref(),
},
opt_destruction_scope: opt_dxn_ext,
span: stmt_span,
})))
}
hir::StmtKind::Decl(ref decl, _) => {
Expand Down Expand Up @@ -111,6 +113,7 @@ fn mirror_stmts<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
lint_level: cx.lint_level_of(local.id),
},
opt_destruction_scope: opt_dxn_ext,
span: stmt_span,
})));
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/librustc_mir/hair/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,14 @@ pub enum StmtRef<'tcx> {
Mirror(Box<Stmt<'tcx>>),
}

#[derive(Clone, Debug)]
pub struct StatementSpan(pub Span);

#[derive(Clone, Debug)]
pub struct Stmt<'tcx> {
pub kind: StmtKind<'tcx>,
pub opt_destruction_scope: Option<region::Scope>,
pub span: StatementSpan,
}

#[derive(Clone, Debug)]
Expand Down
20 changes: 20 additions & 0 deletions src/test/ui/nll/issue-54382-use-span-of-tail-of-block.nll.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
error[E0597]: `_thing1` does not live long enough
--> $DIR/issue-54382-use-span-of-tail-of-block.rs:7:29
|
LL | D("other").next(&_thing1)
| ----------------^^^^^^^^-
| | |
| | borrowed value does not live long enough
| a temporary with access to the borrow is created here ...
LL | }
LL | }
| - `_thing1` dropped here while still borrowed
LL |
LL | ;
| - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
|
= note: The temporary is part of an expression at the end of a block. Consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped.

error: aborting due to previous error

For more information about this error, try `rustc --explain E0597`.
28 changes: 28 additions & 0 deletions src/test/ui/nll/issue-54382-use-span-of-tail-of-block.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
fn main() {
{
let mut _thing1 = D(Box::new("thing1"));
{
let _thing2 = D("thing2");
side_effects();
D("other").next(&_thing1)
}
}

;
}

#[derive(Debug)]
struct D<T: std::fmt::Debug>(T);

impl<T: std::fmt::Debug> Drop for D<T> {
fn drop(&mut self) {
println!("dropping {:?})", self);
}
}

impl<T: std::fmt::Debug> D<T> {
fn next<U: std::fmt::Debug>(&self, _other: U) -> D<U> { D(_other) }
fn end(&self) { }
}

fn side_effects() { }
15 changes: 15 additions & 0 deletions src/test/ui/nll/issue-54382-use-span-of-tail-of-block.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error[E0597]: `_thing1` does not live long enough
--> $DIR/issue-54382-use-span-of-tail-of-block.rs:7:30
|
LL | D("other").next(&_thing1)
| ^^^^^^^ borrowed value does not live long enough
LL | }
LL | }
| - `_thing1` dropped here while still borrowed
LL |
LL | ;
| - borrowed value needs to live until here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0597`.