debuginfo: Make sure that all calls to drop glue are associated with debug locations.

This commit makes rustc emit debug locations for all call
and invoke statements in LLVM IR, if they are contained
within a function that debuginfo is enabled for. This is
important because LLVM does not handle the case where a
function body containing debuginfo is inlined into another
function with debuginfo, but the inlined call statement
does not have a debug location. In this case, LLVM will
not know where (in terms of source code coordinates) the
function was inlined to and we end up with some statements
still linked to the source locations in there original,
non-inlined function without any indication that they are
indeed an inline-copy. Later, when generating DWARF from
the IR, LLVM will interpret this as corrupt IR and abort.

Unfortunately, the undesirable case described above can
still occur when using LTO. If there is a crate compiled
without debuginfo calling into a crate compiled with
debuginfo, we again end up with the conditions triggering
the error. This is why some LTO tests still fail with the
dreaded assertion, if the standard library was built with
debuginfo enabled.
That is, `RUSTFLAGS_STAGE2=-g make rustc-stage2` will
succeed but `RUSTFLAGS_STAGE2=-g make check` will still
fail after this commit has been merged. This is a problem
that has to be dealt with separately.

Fixes #17201
Fixes #15816
Fixes #15156
This commit is contained in:
Michael Woerister 2014-09-24 08:49:38 +02:00
parent d299bafb31
commit 302486e49b
10 changed files with 285 additions and 74 deletions

View file

@ -1791,7 +1791,7 @@ pub fn trans_closure(ccx: &CrateContext,
body: &ast::Block,
llfndecl: ValueRef,
param_substs: &param_substs,
id: ast::NodeId,
fn_ast_id: ast::NodeId,
_attributes: &[ast::Attribute],
arg_types: Vec<ty::t>,
output_type: ty::t,
@ -1811,7 +1811,7 @@ pub fn trans_closure(ccx: &CrateContext,
let arena = TypedArena::new();
let fcx = new_fn_ctxt(ccx,
llfndecl,
id,
fn_ast_id,
has_env,
output_type,
param_substs,
@ -1820,7 +1820,9 @@ pub fn trans_closure(ccx: &CrateContext,
let mut bcx = init_function(&fcx, false, output_type);
// cleanup scope for the incoming arguments
let arg_scope = fcx.push_custom_cleanup_scope();
let fn_cleanup_debug_loc =
debuginfo::get_cleanup_debug_loc_for_ast_node(fn_ast_id, body.span, true);
let arg_scope = fcx.push_custom_cleanup_scope_with_debug_loc(fn_cleanup_debug_loc);
let block_ty = node_id_type(bcx, body.id);
@ -1969,7 +1971,9 @@ pub fn trans_named_tuple_constructor<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
ctor_ty: ty::t,
disr: ty::Disr,
args: callee::CallArgs,
dest: expr::Dest) -> Result<'blk, 'tcx> {
dest: expr::Dest,
call_info: Option<NodeInfo>)
-> Result<'blk, 'tcx> {
let ccx = bcx.fcx.ccx;
let tcx = ccx.tcx();
@ -1999,8 +2003,13 @@ pub fn trans_named_tuple_constructor<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
match args {
callee::ArgExprs(exprs) => {
let fields = exprs.iter().map(|x| &**x).enumerate().collect::<Vec<_>>();
bcx = expr::trans_adt(bcx, result_ty, disr, fields.as_slice(),
None, expr::SaveIn(llresult));
bcx = expr::trans_adt(bcx,
result_ty,
disr,
fields.as_slice(),
None,
expr::SaveIn(llresult),
call_info);
}
_ => ccx.sess().bug("expected expr as arguments for variant/struct tuple constructor")
}
@ -2010,7 +2019,9 @@ pub fn trans_named_tuple_constructor<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
// drop the temporary we made
let bcx = match dest {
expr::SaveIn(_) => bcx,
expr::Ignore => glue::drop_ty(bcx, llresult, result_ty)
expr::Ignore => {
glue::drop_ty(bcx, llresult, result_ty, call_info)
}
};
Result::new(bcx, llresult)

View file

@ -714,8 +714,12 @@ pub fn trans_call_inner<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
fcx.pop_custom_cleanup_scope(arg_cleanup_scope);
let ctor_ty = callee_ty.subst(bcx.tcx(), &substs);
return base::trans_named_tuple_constructor(bcx, ctor_ty, disr,
args, dest.unwrap());
return base::trans_named_tuple_constructor(bcx,
ctor_ty,
disr,
args,
dest.unwrap(),
call_info);
}
};
@ -835,7 +839,7 @@ pub fn trans_call_inner<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
match (dest, opt_llretslot) {
(Some(expr::Ignore), Some(llretslot)) => {
// drop the value if it is not being saved.
bcx = glue::drop_ty(bcx, llretslot, ret_ty);
bcx = glue::drop_ty(bcx, llretslot, ret_ty, call_info);
call_lifetime_end(bcx, llretslot);
}
_ => {}

View file

@ -18,7 +18,8 @@ use middle::trans::base;
use middle::trans::build;
use middle::trans::callee;
use middle::trans::common;
use middle::trans::common::{Block, FunctionContext, ExprId};
use middle::trans::common::{Block, FunctionContext, ExprId, NodeInfo};
use middle::trans::debuginfo;
use middle::trans::glue;
use middle::trans::type_::Type;
use middle::ty;
@ -36,6 +37,10 @@ pub struct CleanupScope<'blk, 'tcx: 'blk> {
// Cleanups to run upon scope exit.
cleanups: Vec<CleanupObj>,
// The debug location any drop calls generated for this scope will be
// associated with.
debug_loc: Option<NodeInfo>,
cached_early_exits: Vec<CachedEarlyExit>,
cached_landing_pad: Option<BasicBlockRef>,
}
@ -69,7 +74,10 @@ pub struct CachedEarlyExit {
pub trait Cleanup {
fn must_unwind(&self) -> bool;
fn clean_on_unwind(&self) -> bool;
fn trans<'blk, 'tcx>(&self, bcx: Block<'blk, 'tcx>) -> Block<'blk, 'tcx>;
fn trans<'blk, 'tcx>(&self,
bcx: Block<'blk, 'tcx>,
debug_loc: Option<NodeInfo>)
-> Block<'blk, 'tcx>;
}
pub type CleanupObj = Box<Cleanup+'static>;
@ -80,14 +88,14 @@ pub enum ScopeId {
}
impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
fn push_ast_cleanup_scope(&self, id: ast::NodeId) {
fn push_ast_cleanup_scope(&self, debug_loc: NodeInfo) {
/*!
* Invoked when we start to trans the code contained
* within a new cleanup scope.
*/
debug!("push_ast_cleanup_scope({})",
self.ccx.tcx().map.node_to_string(id));
self.ccx.tcx().map.node_to_string(debug_loc.id));
// FIXME(#2202) -- currently closure bodies have a parent
// region, which messes up the assertion below, since there
@ -101,10 +109,15 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
// this new AST scope had better be its immediate child.
let top_scope = self.top_ast_scope();
if top_scope.is_some() {
assert_eq!(self.ccx.tcx().region_maps.opt_encl_scope(id), top_scope);
assert_eq!(self.ccx
.tcx()
.region_maps
.opt_encl_scope(debug_loc.id),
top_scope);
}
self.push_scope(CleanupScope::new(AstScopeKind(id)));
self.push_scope(CleanupScope::new(AstScopeKind(debug_loc.id),
Some(debug_loc)));
}
fn push_loop_cleanup_scope(&self,
@ -114,13 +127,38 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
self.ccx.tcx().map.node_to_string(id));
assert_eq!(Some(id), self.top_ast_scope());
self.push_scope(CleanupScope::new(LoopScopeKind(id, exits)));
// Just copy the debuginfo source location from the enclosing scope
let debug_loc = self.scopes
.borrow()
.last()
.unwrap()
.debug_loc;
self.push_scope(CleanupScope::new(LoopScopeKind(id, exits), debug_loc));
}
fn push_custom_cleanup_scope(&self) -> CustomScopeIndex {
let index = self.scopes_len();
debug!("push_custom_cleanup_scope(): {}", index);
self.push_scope(CleanupScope::new(CustomScopeKind));
// Just copy the debuginfo source location from the enclosing scope
let debug_loc = self.scopes
.borrow()
.last()
.map(|opt_scope| opt_scope.debug_loc)
.unwrap_or(None);
self.push_scope(CleanupScope::new(CustomScopeKind, debug_loc));
CustomScopeIndex { index: index }
}
fn push_custom_cleanup_scope_with_debug_loc(&self,
debug_loc: NodeInfo)
-> CustomScopeIndex {
let index = self.scopes_len();
debug!("push_custom_cleanup_scope(): {}", index);
self.push_scope(CleanupScope::new(CustomScopeKind, Some(debug_loc)));
CustomScopeIndex { index: index }
}
@ -141,7 +179,6 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
let scope = self.pop_scope();
self.trans_scope_cleanups(bcx, &scope)
}
fn pop_loop_cleanup_scope(&self,
@ -175,9 +212,9 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> {
}
fn pop_and_trans_custom_cleanup_scope(&self,
bcx: Block<'blk, 'tcx>,
custom_scope: CustomScopeIndex)
-> Block<'blk, 'tcx> {
bcx: Block<'blk, 'tcx>,
custom_scope: CustomScopeIndex)
-> Block<'blk, 'tcx> {
/*!
* Removes the top cleanup scope from the stack, which must be
* a temporary scope, and generates the code to do its
@ -503,7 +540,7 @@ impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx
let mut bcx = bcx;
if !bcx.unreachable.get() {
for cleanup in scope.cleanups.iter().rev() {
bcx = cleanup.trans(bcx);
bcx = cleanup.trans(bcx, scope.debug_loc);
}
}
bcx
@ -671,7 +708,8 @@ impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx
let mut bcx_out = bcx_in;
for cleanup in scope.cleanups.iter().rev() {
if cleanup_is_suitable_for(&**cleanup, label) {
bcx_out = cleanup.trans(bcx_out);
bcx_out = cleanup.trans(bcx_out,
scope.debug_loc);
}
}
build::Br(bcx_out, prev_llbb);
@ -785,9 +823,12 @@ impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx
}
impl<'blk, 'tcx> CleanupScope<'blk, 'tcx> {
fn new(kind: CleanupScopeKind<'blk, 'tcx>) -> CleanupScope<'blk, 'tcx> {
fn new(kind: CleanupScopeKind<'blk, 'tcx>,
debug_loc: Option<NodeInfo>)
-> CleanupScope<'blk, 'tcx> {
CleanupScope {
kind: kind,
debug_loc: debug_loc,
cleanups: vec!(),
cached_early_exits: vec!(),
cached_landing_pad: None,
@ -902,11 +943,14 @@ impl Cleanup for DropValue {
self.must_unwind
}
fn trans<'blk, 'tcx>(&self, bcx: Block<'blk, 'tcx>) -> Block<'blk, 'tcx> {
fn trans<'blk, 'tcx>(&self,
bcx: Block<'blk, 'tcx>,
debug_loc: Option<NodeInfo>)
-> Block<'blk, 'tcx> {
let bcx = if self.is_immediate {
glue::drop_ty_immediate(bcx, self.val, self.ty)
glue::drop_ty_immediate(bcx, self.val, self.ty, debug_loc)
} else {
glue::drop_ty(bcx, self.val, self.ty)
glue::drop_ty(bcx, self.val, self.ty, debug_loc)
};
if self.zero {
base::zero_mem(bcx, self.val, self.ty);
@ -935,7 +979,12 @@ impl Cleanup for FreeValue {
true
}
fn trans<'blk, 'tcx>(&self, bcx: Block<'blk, 'tcx>) -> Block<'blk, 'tcx> {
fn trans<'blk, 'tcx>(&self,
bcx: Block<'blk, 'tcx>,
debug_loc: Option<NodeInfo>)
-> Block<'blk, 'tcx> {
apply_debug_loc(bcx.fcx, debug_loc);
match self.heap {
HeapManaged => {
glue::trans_free(bcx, self.ptr)
@ -963,7 +1012,12 @@ impl Cleanup for FreeSlice {
true
}
fn trans<'blk, 'tcx>(&self, bcx: Block<'blk, 'tcx>) -> Block<'blk, 'tcx> {
fn trans<'blk, 'tcx>(&self,
bcx: Block<'blk, 'tcx>,
debug_loc: Option<NodeInfo>)
-> Block<'blk, 'tcx> {
apply_debug_loc(bcx.fcx, debug_loc);
match self.heap {
HeapManaged => {
glue::trans_free(bcx, self.ptr)
@ -988,7 +1042,11 @@ impl Cleanup for LifetimeEnd {
true
}
fn trans<'blk, 'tcx>(&self, bcx: Block<'blk, 'tcx>) -> Block<'blk, 'tcx> {
fn trans<'blk, 'tcx>(&self,
bcx: Block<'blk, 'tcx>,
debug_loc: Option<NodeInfo>)
-> Block<'blk, 'tcx> {
apply_debug_loc(bcx.fcx, debug_loc);
base::call_lifetime_end(bcx, self.ptr);
bcx
}
@ -1023,15 +1081,29 @@ fn cleanup_is_suitable_for(c: &Cleanup,
!label.is_unwind() || c.clean_on_unwind()
}
fn apply_debug_loc(fcx: &FunctionContext, debug_loc: Option<NodeInfo>) {
match debug_loc {
Some(ref src_loc) => {
debuginfo::set_source_location(fcx, src_loc.id, src_loc.span);
}
None => {
debuginfo::clear_source_location(fcx);
}
}
}
///////////////////////////////////////////////////////////////////////////
// These traits just exist to put the methods into this file.
pub trait CleanupMethods<'blk, 'tcx> {
fn push_ast_cleanup_scope(&self, id: ast::NodeId);
fn push_ast_cleanup_scope(&self, id: NodeInfo);
fn push_loop_cleanup_scope(&self,
id: ast::NodeId,
exits: [Block<'blk, 'tcx>, ..EXIT_MAX]);
id: ast::NodeId,
exits: [Block<'blk, 'tcx>, ..EXIT_MAX]);
fn push_custom_cleanup_scope(&self) -> CustomScopeIndex;
fn push_custom_cleanup_scope_with_debug_loc(&self,
debug_loc: NodeInfo)
-> CustomScopeIndex;
fn pop_and_trans_ast_cleanup_scope(&self,
bcx: Block<'blk, 'tcx>,
cleanup_scope: ast::NodeId)

View file

@ -22,6 +22,7 @@ use middle::trans::cleanup;
use middle::trans::common::*;
use middle::trans::consts;
use middle::trans::datum;
use middle::trans::debuginfo;
use middle::trans::expr;
use middle::trans::meth;
use middle::trans::type_::Type;
@ -53,7 +54,9 @@ pub fn trans_stmt<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
let mut bcx = cx;
let id = ast_util::stmt_id(s);
fcx.push_ast_cleanup_scope(id);
let cleanup_debug_loc =
debuginfo::get_cleanup_debug_loc_for_ast_node(id, s.span, false);
fcx.push_ast_cleanup_scope(cleanup_debug_loc);
match s.node {
ast::StmtExpr(ref e, _) | ast::StmtSemi(ref e, _) => {
@ -75,8 +78,7 @@ pub fn trans_stmt<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
ast::StmtMac(..) => cx.tcx().sess.bug("unexpanded macro")
}
bcx = fcx.pop_and_trans_ast_cleanup_scope(
bcx, ast_util::stmt_id(s));
bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, ast_util::stmt_id(s));
return bcx;
}
@ -100,7 +102,9 @@ pub fn trans_block<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
let fcx = bcx.fcx;
let mut bcx = bcx;
fcx.push_ast_cleanup_scope(b.id);
let cleanup_debug_loc =
debuginfo::get_cleanup_debug_loc_for_ast_node(b.id, b.span, true);
fcx.push_ast_cleanup_scope(cleanup_debug_loc);
for s in b.stmts.iter() {
bcx = trans_stmt(bcx, &**s);

View file

@ -1030,6 +1030,55 @@ pub fn create_argument_metadata(bcx: Block, arg: &ast::Arg) {
})
}
pub fn get_cleanup_debug_loc_for_ast_node(node_id: ast::NodeId,
node_span: Span,
is_block: bool)
-> NodeInfo {
// A debug location needs two things:
// (1) A span (of which only the beginning will actually be used)
// (2) An AST node-id which will be used to look up the lexical scope
// for the location in the functions scope-map
//
// This function will calculate the debug location for compiler-generated
// cleanup calls that are executed when control-flow leaves the
// scope identified by `node_id`.
//
// For everything but block-like things we can simply take id and span of
// the given expression, meaning that from a debugger's view cleanup code is
// executed at the same source location as the statement/expr itself.
//
// Blocks are a special case. Here we want the cleanup to be linked to the
// closing curly brace of the block. The *scope* the cleanup is executed in
// is up to debate: It could either still be *within* the block being
// cleaned up, meaning that locals from the block are still visible in the
// debugger.
// Or it could be in the scope that the block is contained in, so any locals
// from within the block are already considered out-of-scope and thus not
// accessible in the debugger anymore.
//
// The current implementation opts for the second option: cleanup of a block
// already happens in the parent scope of the block. The main reason for
// this decision is that scoping becomes controlflow dependent when variable
// shadowing is involved and it's impossible to decide statically which
// scope is actually left when the cleanup code is executed.
// In practice it shouldn't make much of a difference.
let cleanup_span = if is_block {
Span {
lo: node_span.hi - codemap::BytePos(1), // closing brace should always be 1 byte...
hi: node_span.hi,
expn_id: node_span.expn_id
}
} else {
node_span
};
NodeInfo {
id: node_id,
span: cleanup_span
}
}
/// Sets the current debug location at the beginning of the span.
///
/// Maps to a call to llvm::LLVMSetCurrentDebugLocation(...). The node_id
@ -1107,7 +1156,9 @@ pub fn create_function_debug_context(cx: &CrateContext,
// Do this here already, in case we do an early exit from this function.
set_debug_location(cx, UnknownLocation);
if fn_ast_id == -1 {
if fn_ast_id == ast::DUMMY_NODE_ID {
// This is a function not linked to any source location, so don't
// generate debuginfo for it.
return FunctionDebugContext { repr: FunctionWithoutDebugInfo };
}
@ -1289,6 +1340,7 @@ pub fn create_function_debug_context(cx: &CrateContext,
fn_decl.inputs.as_slice(),
&*top_level_block,
fn_metadata,
fn_ast_id,
&mut *fn_debug_context.scope_map.borrow_mut());
return FunctionDebugContext { repr: DebugInfo(fn_debug_context) };
@ -1297,7 +1349,7 @@ pub fn create_function_debug_context(cx: &CrateContext,
fn_ast_id: ast::NodeId,
fn_decl: &ast::FnDecl,
param_substs: &param_substs,
error_span: Span) -> DIArray {
error_reporting_span: Span) -> DIArray {
if cx.sess().opts.debuginfo == LimitedDebugInfo {
return create_DIArray(DIB(cx), []);
}
@ -1310,7 +1362,7 @@ pub fn create_function_debug_context(cx: &CrateContext,
signature.push(ptr::null_mut());
}
_ => {
assert_type_for_node_id(cx, fn_ast_id, error_span);
assert_type_for_node_id(cx, fn_ast_id, error_reporting_span);
let return_type = ty::node_id_to_type(cx.tcx(), fn_ast_id);
let return_type = return_type.substp(cx.tcx(), param_substs);
@ -1634,15 +1686,17 @@ fn file_metadata(cx: &CrateContext, full_path: &str) -> DIFile {
/// Finds the scope metadata node for the given AST node.
fn scope_metadata(fcx: &FunctionContext,
node_id: ast::NodeId,
span: Span)
error_reporting_span: Span)
-> DIScope {
let scope_map = &fcx.debug_context.get_ref(fcx.ccx, span).scope_map;
let scope_map = &fcx.debug_context
.get_ref(fcx.ccx, error_reporting_span)
.scope_map;
match scope_map.borrow().find_copy(&node_id) {
Some(scope_metadata) => scope_metadata,
None => {
let node = fcx.ccx.tcx().map.get(node_id);
fcx.ccx.sess().span_bug(span,
fcx.ccx.sess().span_bug(error_reporting_span,
format!("debuginfo: Could not find scope info for node {:?}",
node).as_slice());
}
@ -3139,9 +3193,12 @@ fn fn_should_be_ignored(fcx: &FunctionContext) -> bool {
}
}
fn assert_type_for_node_id(cx: &CrateContext, node_id: ast::NodeId, error_span: Span) {
fn assert_type_for_node_id(cx: &CrateContext,
node_id: ast::NodeId,
error_reporting_span: Span) {
if !cx.tcx().node_types.borrow().contains_key(&(node_id as uint)) {
cx.sess().span_bug(error_span, "debuginfo: Could not find type for node id!");
cx.sess().span_bug(error_reporting_span,
"debuginfo: Could not find type for node id!");
}
}
@ -3169,6 +3226,7 @@ fn populate_scope_map(cx: &CrateContext,
args: &[ast::Arg],
fn_entry_block: &ast::Block,
fn_metadata: DISubprogram,
fn_ast_id: ast::NodeId,
scope_map: &mut HashMap<ast::NodeId, DIScope>) {
let def_map = &cx.tcx().def_map;
@ -3179,13 +3237,15 @@ fn populate_scope_map(cx: &CrateContext,
let mut scope_stack = vec!(ScopeStackEntry { scope_metadata: fn_metadata,
ident: None });
scope_map.insert(fn_ast_id, fn_metadata);
// Push argument identifiers onto the stack so arguments integrate nicely
// with variable shadowing.
for arg in args.iter() {
pat_util::pat_bindings(def_map, &*arg.pat, |_, _, _, path1| {
pat_util::pat_bindings(def_map, &*arg.pat, |_, node_id, _, path1| {
scope_stack.push(ScopeStackEntry { scope_metadata: fn_metadata,
ident: Some(path1.node) });
scope_map.insert(node_id, fn_metadata);
})
}

View file

@ -119,10 +119,13 @@ pub fn trans_into<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
}
debug!("trans_into() expr={}", expr.repr(bcx.tcx()));
let cleanup_debug_loc = debuginfo::get_cleanup_debug_loc_for_ast_node(expr.id,
expr.span,
false);
bcx.fcx.push_ast_cleanup_scope(cleanup_debug_loc);
debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);
bcx.fcx.push_ast_cleanup_scope(expr.id);
let kind = ty::expr_kind(bcx.tcx(), expr);
bcx = match kind {
ty::LvalueExpr | ty::RvalueDatumExpr => {
@ -154,7 +157,10 @@ pub fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
let mut bcx = bcx;
let fcx = bcx.fcx;
fcx.push_ast_cleanup_scope(expr.id);
let cleanup_debug_loc = debuginfo::get_cleanup_debug_loc_for_ast_node(expr.id,
expr.span,
false);
fcx.push_ast_cleanup_scope(cleanup_debug_loc);
let datum = unpack_datum!(bcx, trans_unadjusted(bcx, expr));
let datum = unpack_datum!(bcx, apply_adjustments(bcx, expr, datum));
bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, expr.id);
@ -644,7 +650,9 @@ fn trans_datum_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
match x.node {
ast::ExprRepeat(..) | ast::ExprVec(..) => {
// Special case for slices.
fcx.push_ast_cleanup_scope(x.id);
let cleanup_debug_loc =
debuginfo::get_cleanup_debug_loc_for_ast_node(x.id, x.span, false);
fcx.push_ast_cleanup_scope(cleanup_debug_loc);
let datum = unpack_datum!(
bcx, tvec::trans_slice_vec(bcx, expr, &**x));
bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, x.id);
@ -908,6 +916,8 @@ fn trans_rvalue_stmt_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
return bcx;
}
debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);
match expr.node {
ast::ExprParen(ref e) => {
trans_into(bcx, &**e, Ignore)
@ -954,10 +964,14 @@ fn trans_rvalue_stmt_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
//
// We could avoid this intermediary with some analysis
// to determine whether `dst` may possibly own `src`.
debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);
let src_datum = unpack_datum!(bcx, trans(bcx, &**src));
let src_datum = unpack_datum!(
bcx, src_datum.to_rvalue_datum(bcx, "ExprAssign"));
bcx = glue::drop_ty(bcx, dst_datum.val, dst_datum.ty);
bcx = glue::drop_ty(bcx,
dst_datum.val,
dst_datum.ty,
Some(NodeInfo { id: expr.id, span: expr.span }));
src_datum.store_to(bcx, dst_datum.val)
} else {
trans_into(bcx, &**src, SaveIn(dst_datum.to_llref()))
@ -987,6 +1001,8 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
let mut bcx = bcx;
let tcx = bcx.tcx();
debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);
match expr.node {
ast::ExprParen(ref e) => {
trans_into(bcx, &**e, dest)
@ -1014,7 +1030,13 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
ast::ExprTup(ref args) => {
let numbered_fields: Vec<(uint, &ast::Expr)> =
args.iter().enumerate().map(|(i, arg)| (i, &**arg)).collect();
trans_adt(bcx, expr_ty(bcx, expr), 0, numbered_fields.as_slice(), None, dest)
trans_adt(bcx,
expr_ty(bcx, expr),
0,
numbered_fields.as_slice(),
None,
dest,
Some(NodeInfo { id: expr.id, span: expr.span }))
}
ast::ExprLit(ref lit) => {
match lit.node {
@ -1297,15 +1319,15 @@ pub fn with_field_tys<R>(tcx: &ty::ctxt,
fn trans_struct<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
fields: &[ast::Field],
base: Option<&ast::Expr>,
base: Option<&ast::Expr>,
expr_span: codemap::Span,
id: ast::NodeId,
expr_id: ast::NodeId,
dest: Dest) -> Block<'blk, 'tcx> {
let _icx = push_ctxt("trans_rec");
let ty = node_id_type(bcx, id);
let ty = node_id_type(bcx, expr_id);
let tcx = bcx.tcx();
with_field_tys(tcx, ty, Some(id), |discr, field_tys| {
with_field_tys(tcx, ty, Some(expr_id), |discr, field_tys| {
let mut need_base = Vec::from_elem(field_tys.len(), true);
let numbered_fields = fields.iter().map(|field| {
@ -1342,7 +1364,13 @@ fn trans_struct<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
}
};
trans_adt(bcx, ty, discr, numbered_fields.as_slice(), optbase, dest)
trans_adt(bcx,
ty,
discr,
numbered_fields.as_slice(),
optbase,
dest,
Some(NodeInfo { id: expr_id, span: expr_span }))
})
}
@ -1376,11 +1404,20 @@ pub fn trans_adt<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
discr: ty::Disr,
fields: &[(uint, &ast::Expr)],
optbase: Option<StructBaseInfo>,
dest: Dest) -> Block<'blk, 'tcx> {
dest: Dest,
source_location: Option<NodeInfo>)
-> Block<'blk, 'tcx> {
let _icx = push_ctxt("trans_adt");
let fcx = bcx.fcx;
let repr = adt::represent_type(bcx.ccx(), ty);
match source_location {
Some(src_loc) => debuginfo::set_source_location(bcx.fcx,
src_loc.id,
src_loc.span),
None => {}
};
// If we don't care about the result, just make a
// temporary stack slot
let addr = match dest {
@ -1414,6 +1451,13 @@ pub fn trans_adt<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
}
}
match source_location {
Some(src_loc) => debuginfo::set_source_location(bcx.fcx,
src_loc.id,
src_loc.span),
None => {}
};
// Now, we just overwrite the fields we've explicitly specified
for &(i, ref e) in fields.iter() {
let dest = adt::trans_field_ptr(bcx, &*repr, addr, discr, i);
@ -1432,7 +1476,7 @@ pub fn trans_adt<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
match dest {
SaveIn(_) => bcx,
Ignore => {
bcx = glue::drop_ty(bcx, addr, ty);
bcx = glue::drop_ty(bcx, addr, ty, source_location);
base::call_lifetime_end(bcx, addr);
bcx
}

View file

@ -28,6 +28,7 @@ use middle::trans::cleanup;
use middle::trans::cleanup::CleanupMethods;
use middle::trans::common::*;
use middle::trans::datum;
use middle::trans::debuginfo;
use middle::trans::expr;
use middle::trans::machine::*;
use middle::trans::reflect;
@ -125,7 +126,10 @@ pub fn get_drop_glue_type(ccx: &CrateContext, t: ty::t) -> ty::t {
}
}
pub fn drop_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v: ValueRef, t: ty::t)
pub fn drop_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
v: ValueRef,
t: ty::t,
source_location: Option<NodeInfo>)
-> Block<'blk, 'tcx> {
// NB: v is an *alias* of type t here, not a direct value.
debug!("drop_ty(t={})", t.repr(bcx.tcx()));
@ -139,17 +143,26 @@ pub fn drop_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v: ValueRef, t: ty::t)
} else {
v
};
match source_location {
Some(sl) => debuginfo::set_source_location(bcx.fcx, sl.id, sl.span),
None => debuginfo::clear_source_location(bcx.fcx)
};
Call(bcx, glue, [ptr], None);
}
bcx
}
pub fn drop_ty_immediate<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v: ValueRef, t: ty::t)
pub fn drop_ty_immediate<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
v: ValueRef,
t: ty::t,
source_location: Option<NodeInfo>)
-> Block<'blk, 'tcx> {
let _icx = push_ctxt("drop_ty_immediate");
let vp = alloca(bcx, type_of(bcx.ccx(), t), "");
Store(bcx, v, vp);
drop_ty(bcx, vp, t)
drop_ty(bcx, vp, t, source_location)
}
pub fn get_drop_glue(ccx: &CrateContext, t: ty::t) -> ValueRef {
@ -464,7 +477,7 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, t: ty::t)
let llbox = Load(bcx, llval);
let not_null = IsNotNull(bcx, llbox);
with_cond(bcx, not_null, |bcx| {
let bcx = drop_ty(bcx, v0, content_ty);
let bcx = drop_ty(bcx, v0, content_ty, None);
let info = GEPi(bcx, v0, [0, abi::slice_elt_len]);
let info = Load(bcx, info);
let (llsize, llalign) = size_and_align_of_dst(bcx, content_ty, info);
@ -477,7 +490,7 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, t: ty::t)
let llbox = Load(bcx, llval);
let not_null = IsNotNull(bcx, llbox);
with_cond(bcx, not_null, |bcx| {
let bcx = drop_ty(bcx, llbox, content_ty);
let bcx = drop_ty(bcx, llbox, content_ty, None);
trans_exchange_free_ty(bcx, llbox, content_ty)
})
}
@ -508,11 +521,14 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, t: ty::t)
}
ty::NoDtor => {
// No dtor? Just the default case
iter_structural_ty(bcx, v0, t, drop_ty)
iter_structural_ty(bcx, v0, t, |bb, vv, tt| drop_ty(bb, vv, tt, None))
}
}
}
ty::ty_unboxed_closure(..) => iter_structural_ty(bcx, v0, t, drop_ty),
ty::ty_unboxed_closure(..) => iter_structural_ty(bcx,
v0,
t,
|bb, vv, tt| drop_ty(bb, vv, tt, None)),
ty::ty_closure(ref f) if f.store == ty::UniqTraitStore => {
let box_cell_v = GEPi(bcx, v0, [0u, abi::fn_field_box]);
let env = Load(bcx, box_cell_v);
@ -544,7 +560,7 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, t: ty::t)
assert!(ty::type_is_sized(bcx.tcx(), t));
if ty::type_needs_drop(bcx.tcx(), t) &&
ty::type_is_structural(t) {
iter_structural_ty(bcx, v0, t, drop_ty)
iter_structural_ty(bcx, v0, t, |bb, vv, tt| drop_ty(bb, vv, tt, None))
} else {
bcx
}
@ -574,7 +590,7 @@ fn decr_refcnt_maybe_free<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
let v = Load(free_bcx, box_ptr_ptr);
let body = GEPi(free_bcx, v, [0u, abi::box_field_body]);
let free_bcx = drop_ty(free_bcx, body, t);
let free_bcx = drop_ty(free_bcx, body, t, None);
let free_bcx = trans_free(free_bcx, v);
Br(free_bcx, next_bcx.llbb);

View file

@ -541,7 +541,7 @@ pub fn trans_intrinsic_call<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, node: ast::N
// If we made a temporary stack slot, let's clean it up
match dest {
expr::Ignore => {
bcx = glue::drop_ty(bcx, llresult, ret_ty);
bcx = glue::drop_ty(bcx, llresult, ret_ty, Some(call_info));
}
expr::SaveIn(_) => {}
}

View file

@ -64,7 +64,7 @@ pub fn make_drop_glue_unboxed<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
let dataptr = get_dataptr(bcx, vptr);
let bcx = if ty::type_needs_drop(tcx, unit_ty) {
let len = get_len(bcx, vptr);
iter_vec_raw(bcx, dataptr, unit_ty, len, glue::drop_ty)
iter_vec_raw(bcx, dataptr, unit_ty, len, |bb, vv, tt| glue::drop_ty(bb, vv, tt, None))
} else {
bcx
};

View file

@ -130,8 +130,8 @@ fn main() {
let i32_val: i32 = -32;
let i32_ref: &i32 = &i32_val;
let uint_val: i64 = -64;
let i64_ref: &i64 = &uint_val;
let i64_val: i64 = -64;
let i64_ref: &i64 = &i64_val;
let uint_val: uint = 1;
let uint_ref: &uint = &uint_val;