From 33e7d95e9c4ff02b4fb949ea51a26a8bf5e9ae5c Mon Sep 17 00:00:00 2001 From: Michael Woerister <michaelwoerister@gmail> Date: Mon, 5 Aug 2013 11:12:40 +0200 Subject: [PATCH] debuginfo: Implemented proper handling of lexical scopes and variable shadowing. --- src/librustc/middle/trans/_match.rs | 21 +- src/librustc/middle/trans/base.rs | 6 +- src/librustc/middle/trans/common.rs | 8 +- src/librustc/middle/trans/controlflow.rs | 3 - src/librustc/middle/trans/debuginfo.rs | 691 ++++++++++++++---- src/librustc/middle/trans/expr.rs | 4 +- .../debug-info/lexical-scope-in-for-loop.rs | 77 ++ src/test/debug-info/lexical-scope-in-if.rs | 128 ++++ src/test/debug-info/lexical-scope-in-match.rs | 148 ++++ .../lexical-scope-in-unconditional-loop.rs | 127 ++++ src/test/debug-info/lexical-scope-in-while.rs | 123 ++++ .../debug-info/lexical-scope-with-macro.rs | 129 ++++ .../lexical-scopes-in-block-expression.rs | 344 +++++++++ .../name-shadowing-and-scope-nesting.rs | 95 +++ ...variable-scope.rs => shadowed-variable.rs} | 29 +- src/test/debug-info/simple-lexical-scope.rs | 87 +++ 16 files changed, 1874 insertions(+), 146 deletions(-) create mode 100644 src/test/debug-info/lexical-scope-in-for-loop.rs create mode 100644 src/test/debug-info/lexical-scope-in-if.rs create mode 100644 src/test/debug-info/lexical-scope-in-match.rs create mode 100644 src/test/debug-info/lexical-scope-in-unconditional-loop.rs create mode 100644 src/test/debug-info/lexical-scope-in-while.rs create mode 100644 src/test/debug-info/lexical-scope-with-macro.rs create mode 100644 src/test/debug-info/lexical-scopes-in-block-expression.rs create mode 100644 src/test/debug-info/name-shadowing-and-scope-nesting.rs rename src/test/debug-info/{variable-scope.rs => shadowed-variable.rs} (74%) create mode 100644 src/test/debug-info/simple-lexical-scope.rs diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index c98d859337c..bb1e3fa1718 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -214,6 +214,7 @@ use middle::trans::expr; use middle::trans::glue; use middle::trans::tvec; use middle::trans::type_of; +use middle::trans::debuginfo; use middle::ty; use util::common::indenter; use util::ppaux::{Repr, vec_map_to_str}; @@ -385,6 +386,7 @@ struct BindingInfo { llmatch: ValueRef, trmode: TransBindingMode, id: ast::NodeId, + span: span, ty: ty::t, } @@ -1305,7 +1307,7 @@ fn insert_lllocals(bcx: @mut Block, BindArgument => bcx.fcx.llargs }; - for (_, &binding_info) in bindings_map.iter() { + for (&ident, &binding_info) in bindings_map.iter() { let llval = match binding_info.trmode { // By value bindings: use the stack slot that we // copied/moved the value into @@ -1325,6 +1327,14 @@ fn insert_lllocals(bcx: @mut Block, debug!("binding %? to %s", binding_info.id, bcx.val_to_str(llval)); llmap.insert(binding_info.id, llval); + + if bcx.sess().opts.extra_debuginfo { + debuginfo::create_match_binding_metadata(bcx, + ident, + binding_info.id, + binding_info.ty, + binding_info.span); + } } return bcx; } @@ -1771,7 +1781,7 @@ fn create_bindings_map(bcx: @mut Block, pat: @ast::pat) -> BindingsMap { let ccx = bcx.ccx(); let tcx = bcx.tcx(); let mut bindings_map = HashMap::new(); - do pat_bindings(tcx.def_map, pat) |bm, p_id, _s, path| { + do pat_bindings(tcx.def_map, pat) |bm, p_id, span, path| { let ident = path_to_ident(path); let variable_ty = node_id_type(bcx, p_id); let llvariable_ty = type_of::type_of(ccx, variable_ty); @@ -1793,8 +1803,11 @@ fn create_bindings_map(bcx: @mut Block, pat: @ast::pat) -> BindingsMap { } }; bindings_map.insert(ident, BindingInfo { - llmatch: llmatch, trmode: trmode, - id: p_id, ty: variable_ty + llmatch: llmatch, + trmode: trmode, + id: p_id, + span: span, + ty: variable_ty }); } return bindings_map; diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 1291a586682..db439eceab8 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -1103,7 +1103,6 @@ pub fn trans_stmt(cx: @mut Block, s: &ast::stmt) -> @mut Block { } let mut bcx = cx; - debuginfo::update_source_pos(cx, s.span); match s.node { ast::stmt_expr(e, _) | ast::stmt_semi(e, _) => { @@ -1634,7 +1633,8 @@ pub fn new_fn_ctxt_w_id(ccx: @mut CrateContext, param_substs: param_substs, span: sp, path: path, - ccx: ccx + ccx: ccx, + debug_context: None, }; fcx.llenv = unsafe { llvm::LLVMGetParam(llfndecl, fcx.env_arg_pos() as c_uint) @@ -1933,7 +1933,7 @@ pub fn trans_fn(ccx: @mut CrateContext, attrs, output_type, |fcx| { - if ccx.sess.opts.extra_debuginfo + if ccx.sess.opts.debuginfo && fcx_has_nonzero_span(fcx) { debuginfo::create_function_metadata(fcx); } diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index 3253e24ae88..10185a9a795 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -23,6 +23,7 @@ use middle::trans::build; use middle::trans::datum; use middle::trans::glue; use middle::trans::write_guard; +use middle::trans::debuginfo; use middle::ty::substs; use middle::ty; use middle::typeck; @@ -226,7 +227,12 @@ pub struct FunctionContext { path: path, // This function's enclosing crate context. - ccx: @mut CrateContext + ccx: @mut CrateContext, + + // Used and maintained by the debuginfo module. + // @jdm: Not sure if the Option-wrapper is a good idea. It allows to save some space in + // non-debug builds, but generates quite a bit of noise at usage sites. What's your opinion? + debug_context: Option<~debuginfo::FunctionDebugContext> } impl FunctionContext { diff --git a/src/librustc/middle/trans/controlflow.rs b/src/librustc/middle/trans/controlflow.rs index 46eb3928d59..2a3003812fe 100644 --- a/src/librustc/middle/trans/controlflow.rs +++ b/src/librustc/middle/trans/controlflow.rs @@ -19,7 +19,6 @@ use middle::trans::base::*; use middle::trans::build::*; use middle::trans::callee; use middle::trans::common::*; -use middle::trans::debuginfo; use middle::trans::expr; use middle::trans::type_of::*; use middle::ty; @@ -38,12 +37,10 @@ pub fn trans_block(bcx: @mut Block, b: &ast::Block, dest: expr::Dest) -> @mut Bl let _icx = push_ctxt("trans_block"); let mut bcx = bcx; for s in b.stmts.iter() { - debuginfo::update_source_pos(bcx, b.span); bcx = trans_stmt(bcx, *s); } match b.expr { Some(e) => { - debuginfo::update_source_pos(bcx, e.span); bcx = expr::trans_into(bcx, e, dest); } None => { diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index 1fb64d9c671..c315421a779 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -99,9 +99,7 @@ pub struct DebugContext { priv created_files: HashMap<~str, DIFile>, priv created_functions: HashMap<ast::NodeId, DISubprogram>, priv created_blocks: HashMap<ast::NodeId, DILexicalBlock>, - priv created_types: HashMap<uint, DIType>, - priv last_function_context_id: ast::NodeId, - priv argument_counter: uint, + priv created_types: HashMap<uint, DIType> } impl DebugContext { @@ -118,8 +116,21 @@ impl DebugContext { created_files: HashMap::new(), created_functions: HashMap::new(), created_blocks: HashMap::new(), - created_types: HashMap::new(), - last_function_context_id: -1, // magic value :( + created_types: HashMap::new() + }; + } +} + + +pub struct FunctionDebugContext { + priv scope_map: HashMap<ast::NodeId, DIScope>, + priv argument_counter: uint, +} + +impl FunctionDebugContext { + priv fn new() -> FunctionDebugContext { + return FunctionDebugContext { + scope_map: HashMap::new(), argument_counter: 1, }; } @@ -141,61 +152,24 @@ pub fn finalize(cx: @mut CrateContext) { pub fn create_local_var_metadata(bcx: @mut Block, local: &ast::Local) { let cx = bcx.ccx(); let def_map = cx.tcx.def_map; - let pattern = local.pat; - let scope = match bcx.parent { - None => create_function_metadata(bcx.fcx), - Some(_) => lexical_block_metadata(bcx) - }; + do pat_util::pat_bindings(def_map, local.pat) |_, node_id, span, path_ref| { - let filename = span_start(cx, local.span).file.name; - let file_metadata = file_metadata(cx, filename); + let var_ident = ast_util::path_to_ident(path_ref); + let var_type = node_id_type(bcx, node_id); - do pat_util::pat_bindings(def_map, pattern) |_, node_id, span, path_ref| { - - let ident = ast_util::path_to_ident(path_ref); - let name: &str = cx.sess.str_of(ident); - debug!("create_local_var_metadata: %s", name); - let loc = span_start(cx, span); - let ty = node_id_type(bcx, node_id); - let type_metadata = type_metadata(cx, ty, span); - - let var_metadata = do name.to_c_str().with_ref |name| { - unsafe { - llvm::LLVMDIBuilderCreateLocalVariable( - DIB(cx), - DW_TAG_auto_variable, - scope, - name, - file_metadata, - loc.line as c_uint, - type_metadata, - false, - 0, - 0) - } - }; - - let llptr = match bcx.fcx.lllocals.find_copy(&node_id) { - Some(v) => v, - None => { - bcx.tcx().sess.span_bug(span, fmt!("No entry in lllocals table for %?", node_id)); - } - }; - - set_debug_location(cx, lexical_block_metadata(bcx), loc.line, loc.col.to_uint()); - unsafe { - let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd( - DIB(cx), - llptr, - var_metadata, - bcx.llbb); - - llvm::LLVMSetInstDebugLocation(trans::build::B(bcx).llbuilder, instr); - } + declare_local(bcx, var_ident, node_id, var_type, span); } } +pub fn create_match_binding_metadata(bcx: @mut Block, + variable_ident: ast::ident, + node_id: ast::NodeId, + variable_type: ty::t, + span: span) { + declare_local(bcx, variable_ident, node_id, variable_type, span); +} + /// Creates debug information for the given function argument. /// /// Adds the created metadata nodes directly to the crate's IR. @@ -213,20 +187,6 @@ pub fn create_argument_metadata(bcx: @mut Block, return; } - // Limited the scope within which `debug_context` is live, - // otherwise => borrowing errors - { - let debug_context = dbg_cx(cx); - - // If this is a new function, reset the counter. llvm::DIBuilder - // wants arguments to be indexed starting from 1. - if fcx.id != debug_context.last_function_context_id { - debug_context.argument_counter = 1; - } - // Keep track of the function we are in - debug_context.last_function_context_id = fcx.id; - } - let def_map = cx.tcx.def_map; let file_metadata = file_metadata(cx, filename); let scope = create_function_metadata(fcx); @@ -241,9 +201,9 @@ pub fn create_argument_metadata(bcx: @mut Block, debug!("create_argument_metadata: %s", name); let argument_index = { - let debug_context = dbg_cx(cx); - let argument_index = debug_context.argument_counter; - debug_context.argument_counter += 1; + let counter = &mut fcx.debug_context.get_mut_ref().argument_counter; + let argument_index = *counter; + *counter += 1; argument_index as c_uint }; @@ -270,7 +230,7 @@ pub fn create_argument_metadata(bcx: @mut Block, } }; - set_debug_location(cx, lexical_block_metadata(bcx), loc.line, loc.col.to_uint()); + set_debug_location(cx, scope, loc.line, loc.col.to_uint()); unsafe { let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd( DIB(cx), @@ -286,20 +246,28 @@ pub fn create_argument_metadata(bcx: @mut Block, /// Sets the current debug location at the beginning of the span /// /// Maps to a call to llvm::LLVMSetCurrentDebugLocation(...) -pub fn update_source_pos(bcx: @mut Block, span: span) { - if !bcx.sess().opts.debuginfo || (*span.lo == 0 && *span.hi == 0) { +pub fn update_source_pos(fcx: &FunctionContext, + node_id: ast::NodeId, + span: span) { + let cx: &mut CrateContext = fcx.ccx; + + if !cx.sess.opts.debuginfo || (*span.lo == 0 && *span.hi == 0) { return; } - debug!("update_source_pos: %s", bcx.sess().codemap.span_to_str(span)); - let loc = span_start(bcx.ccx(), span); - set_debug_location(bcx.ccx(), lexical_block_metadata(bcx), loc.line, loc.col.to_uint()) + + debug!("update_source_pos: %s", cx.sess.codemap.span_to_str(span)); + + let loc = span_start(cx, span); + let scope = scope_metadata(fcx, node_id, span); + + set_debug_location(cx, scope, loc.line, loc.col.to_uint()); } /// Creates debug information for the given function. /// /// Adds the created metadata nodes directly to the crate's IR. /// The return value should be ignored if called from outside of the debuginfo module. -pub fn create_function_metadata(fcx: &FunctionContext) -> DISubprogram { +pub fn create_function_metadata(fcx: &mut FunctionContext) -> DISubprogram { let cx = fcx.ccx; let fnitem = cx.tcx.items.get_copy(&fcx.id); @@ -349,8 +317,8 @@ pub fn create_function_metadata(fcx: &FunctionContext) -> DISubprogram { _ => fcx.ccx.sess.bug(fmt!("create_function_metadata: unexpected sort of node: %?", fnitem)) }; - match dbg_cx(cx).created_functions.find(&id) { - Some(fn_metadata) => return *fn_metadata, + match dbg_cx(cx).created_functions.find_copy(&id) { + Some(fn_metadata) => return fn_metadata, None => () } @@ -405,6 +373,25 @@ pub fn create_function_metadata(fcx: &FunctionContext) -> DISubprogram { } }}; + assert!(fcx.debug_context.is_none()); + + let mut fn_debug_context = ~FunctionDebugContext::new(); + let entry_block_id = fcx.entry_bcx.get_ref().node_info.get_ref().id; + let entry_block = cx.tcx.items.get(&entry_block_id); + + match *entry_block { + ast_map::node_block(ref block) => { + let scope_map = &mut fn_debug_context.scope_map; + populate_scope_map(cx, block, fn_metadata, scope_map); + } + _ => cx.sess.span_bug(span, + fmt!("debuginfo::create_function_metadata() - \ + FunctionContext::entry_bcx::node_info points to wrong type of ast_map entry. \ + Expected: ast_map::node_block, actual: %?", *entry_block)) + } + + fcx.debug_context = Some(fn_debug_context); + dbg_cx(cx).created_functions.insert(id, fn_metadata); return fn_metadata; } @@ -445,6 +432,56 @@ fn compile_unit_metadata(cx: @mut CrateContext) { }}}}}; } +fn declare_local(bcx: @mut Block, + variable_ident: ast::ident, + node_id: ast::NodeId, + variable_type: ty::t, + span: span) { + let cx: &mut CrateContext = bcx.ccx(); + + let filename = span_start(cx, span).file.name; + let file_metadata = file_metadata(cx, filename); + + let name: &str = cx.sess.str_of(variable_ident); + let loc = span_start(cx, span); + let type_metadata = type_metadata(cx, variable_type, span); + let scope = scope_metadata(bcx.fcx, node_id, span); + + let var_metadata = do name.as_c_str |name| { + unsafe { + llvm::LLVMDIBuilderCreateLocalVariable( + DIB(cx), + DW_TAG_auto_variable, + scope, + name, + file_metadata, + loc.line as c_uint, + type_metadata, + false, + 0, + 0) + } + }; + + let llptr = match bcx.fcx.lllocals.find_copy(&node_id) { + Some(v) => v, + None => { + bcx.tcx().sess.span_bug(span, fmt!("No entry in lllocals table for %?", node_id)); + } + }; + + set_debug_location(cx, scope, loc.line, loc.col.to_uint()); + unsafe { + let instr = llvm::LLVMDIBuilderInsertDeclareAtEnd( + DIB(cx), + llptr, + var_metadata, + bcx.llbb); + + llvm::LLVMSetInstDebugLocation(trans::build::B(bcx).llbuilder, instr); + } +} + fn file_metadata(cx: &mut CrateContext, full_path: &str) -> DIFile { match dbg_cx(cx).created_files.find_equiv(&full_path) { Some(file_metadata) => return *file_metadata, @@ -473,50 +510,26 @@ fn file_metadata(cx: &mut CrateContext, full_path: &str) -> DIFile { return file_metadata; } -/// Get or create the lexical block metadata node for the given LLVM basic block. -fn lexical_block_metadata(bcx: @mut Block) -> DILexicalBlock { - let cx = bcx.ccx(); - let mut bcx = bcx; +/// Finds the scope metadata node for the given AST node. +fn scope_metadata(fcx: &FunctionContext, + node_id: ast::NodeId, + span: span) -> DIScope { + if fcx.debug_context.is_none() { + fcx.ccx.sess.span_bug(span, "debuginfo: FunctionDebugContext should be initialized \ + but is not!"); + } - // Search up the tree of basic blocks until we find one that knows the containing lexical block. - while bcx.node_info.is_none() { - match bcx.parent { - Some(b) => bcx = b, - None => cx.sess.bug("debuginfo: Could not find lexical block for LLVM basic block.") + let scope_map = &fcx.debug_context.get_ref().scope_map; + + match scope_map.find_copy(&node_id) { + Some(scope_metadata) => scope_metadata, + None => { + let node = fcx.ccx.tcx.items.get_copy(&node_id); + + fcx.ccx.sess.span_bug(span, + fmt!("debuginfo: Could not find scope info for node %?", node)); } } - - let span = bcx.node_info.unwrap().span; - let id = bcx.node_info.unwrap().id; - - // Check whether we already have a cache entry for this node id - match dbg_cx(cx).created_blocks.find(&id) { - Some(block) => return *block, - None => () - } - - debug!("lexical_block_metadata: %s", bcx.sess().codemap.span_to_str(span)); - - let parent = match bcx.parent { - None => create_function_metadata(bcx.fcx), - Some(b) => lexical_block_metadata(b) - }; - - let loc = span_start(cx, span); - let file_metadata = file_metadata(cx, loc.file.name); - - let lexical_block_metadata = unsafe { - llvm::LLVMDIBuilderCreateLexicalBlock( - DIB(cx), - parent, - file_metadata, - loc.line as c_uint, - loc.col.to_uint() as c_uint) - }; - - dbg_cx(cx).created_blocks.insert(id, lexical_block_metadata); - - return lexical_block_metadata; } fn basic_type_metadata(cx: &mut CrateContext, t: ty::t) -> DIType { @@ -1206,7 +1219,7 @@ fn type_metadata(cx: &mut CrateContext, return type_metadata; } -fn set_debug_location(cx: @mut CrateContext, scope: DIScope, line: uint, col: uint) { +fn set_debug_location(cx: &mut CrateContext, scope: DIScope, line: uint, col: uint) { if dbg_cx(cx).curr_loc == (line, col) { return; } @@ -1224,7 +1237,6 @@ fn set_debug_location(cx: @mut CrateContext, scope: DIScope, line: uint, col: ui } } - //=------------------------------------------------------------------------------------------------- // Utility Functions //=------------------------------------------------------------------------------------------------- @@ -1256,3 +1268,436 @@ fn dbg_cx<'a>(cx: &'a mut CrateContext) -> &'a mut DebugContext { fn DIB(cx: &CrateContext) -> DIBuilderRef { cx.dbg_cx.get_ref().builder } + + +// This procedure builds the *scope map* for a given function, which maps any given ast::NodeId in +// the function's AST to the correct DIScope metadata instance. +// +// This builder procedure walks the AST in execution order and keeps track of what belongs to which +// scope, creating DIScope DIEs along the way, and introducing *artificial* lexical scope +// descriptors where necessary. These artificial scopes allow GDB to correctly handle name +// shadowing. +fn populate_scope_map(cx: &mut CrateContext, + fn_entry_block: &ast::Block, + fn_metadata: DISubprogram, + scope_map: &mut HashMap<ast::NodeId, DIScope>) { + + struct ScopeStackEntry { + scope_metadata: DIScope, + ident: Option<ast::ident> + } + + let mut scope_stack = ~[ScopeStackEntry { scope_metadata: fn_metadata, ident: None }]; + + walk_block(cx, fn_entry_block, &mut scope_stack, scope_map); + + // local helper functions for walking the AST. + + fn with_new_scope(cx: &mut CrateContext, + scope_span: span, + scope_stack: &mut ~[ScopeStackEntry], + scope_map: &mut HashMap<ast::NodeId, DIScope>, + inner_walk: &fn(&mut CrateContext, + &mut ~[ScopeStackEntry], + &mut HashMap<ast::NodeId, DIScope>)) { + + // Create a new lexical scope and push it onto the stack + let loc = cx.sess.codemap.lookup_char_pos(scope_span.lo); + let file_metadata = file_metadata(cx, loc.file.name); + let parent_scope = scope_stack.last().scope_metadata; + + let scope_metadata = unsafe { + llvm::LLVMDIBuilderCreateLexicalBlock( + DIB(cx), + parent_scope, + file_metadata, + loc.line as c_uint, + loc.col.to_uint() as c_uint) + }; + + scope_stack.push(ScopeStackEntry { scope_metadata: scope_metadata, ident: None }); + + inner_walk(cx, scope_stack, scope_map); + + // pop artificial scopes + while scope_stack.last().ident.is_some() { + scope_stack.pop(); + } + + if scope_stack.last().scope_metadata != scope_metadata { + cx.sess.span_bug(scope_span, "debuginfo: Inconsistency in scope management."); + } + + scope_stack.pop(); + } + + fn walk_block(cx: &mut CrateContext, + block: &ast::Block, + scope_stack: &mut ~[ScopeStackEntry], + scope_map: &mut HashMap<ast::NodeId, DIScope>) { + + scope_map.insert(block.id, scope_stack.last().scope_metadata); + + // The interesting things here are statements and the concluding expression. + for &@ ref statement in block.stmts.iter() { + scope_map.insert(ast_util::stmt_id(statement), scope_stack.last().scope_metadata); + + match statement.node { + ast::stmt_decl(@ref decl, _) => walk_decl(cx, decl, scope_stack, scope_map), + ast::stmt_expr(@ref exp, _) | + ast::stmt_semi(@ref exp, _) => walk_expr(cx, exp, scope_stack, scope_map), + ast::stmt_mac(*) => () // ignore macros (which should be expanded anyway) + } + } + + for &@ref exp in block.expr.iter() { + walk_expr(cx, exp, scope_stack, scope_map); + } + } + + fn walk_decl(cx: &mut CrateContext, + decl: &ast::decl, + scope_stack: &mut ~[ScopeStackEntry], + scope_map: &mut HashMap<ast::NodeId, DIScope>) { + match *decl { + codemap::spanned { node: ast::decl_local(@ref local), _ } => { + + scope_map.insert(local.id, scope_stack.last().scope_metadata); + + walk_pattern(cx, local.pat, scope_stack, scope_map); + + for &@ref exp in local.init.iter() { + walk_expr(cx, exp, scope_stack, scope_map); + } + } + _ => () + } + } + + fn walk_pattern(cx: &mut CrateContext, + pat: @ast::pat, + scope_stack: &mut ~[ScopeStackEntry], + scope_map: &mut HashMap<ast::NodeId, DIScope>) { + + let def_map = cx.tcx.def_map; + + // Unfortunately, we cannot just use pat_util::pat_bindings() or ast_util::walk_pat() here + // because we have to visit *all* nodes in order to put them into the scope map. The above + // function don't do that. + match pat.node { + ast::pat_ident(_, ref path_ref, ref sub_pat_opt) => { + + // Check if this is a binding. If so we need to put it on the scope stack and maybe + // introduce an articial scope + if pat_util::pat_is_binding(def_map, pat) { + + let ident = ast_util::path_to_ident(path_ref); + + // LLVM does not properly generate 'DW_AT_start_scope' fields for variable DIEs. + // For this reason we have to introduce an artificial scope at bindings whenever + // a variable with the same name is declared in *any* parent scope. + // + // Otherwise the following error occurs: + // + // let x = 10; + // + // do_something(); // 'gdb print x' correctly prints 10 + // + // { + // do_something(); // 'gdb print x' prints 0, because it already reads the + // // uninitialized 'x' from the next line... + // let x = 100; + // do_something(); // 'gdb print x' correctly prints 100 + // } + + // Is there already a binding with that name? + let need_new_scope = scope_stack + .rev_iter() + .find_(|entry| entry.ident.iter().any(|i| *i == ident)) + .is_some(); + + if need_new_scope { + // Create a new lexical scope and push it onto the stack + let loc = cx.sess.codemap.lookup_char_pos(pat.span.lo); + let file_metadata = file_metadata(cx, loc.file.name); + let parent_scope = scope_stack.last().scope_metadata; + + let scope_metadata = unsafe { + llvm::LLVMDIBuilderCreateLexicalBlock( + DIB(cx), + parent_scope, + file_metadata, + loc.line as c_uint, + loc.col.to_uint() as c_uint) + }; + + scope_stack.push(ScopeStackEntry { + scope_metadata: scope_metadata, + ident: Some(ident) + }); + + } else { + // Push a new entry anyway so the name can be found + let prev_metadata = scope_stack.last().scope_metadata; + scope_stack.push(ScopeStackEntry { + scope_metadata: prev_metadata, + ident: Some(ident) + }); + } + } + + scope_map.insert(pat.id, scope_stack.last().scope_metadata); + + for &sub_pat in sub_pat_opt.iter() { + walk_pattern(cx, sub_pat, scope_stack, scope_map); + } + } + + ast::pat_wild => { + scope_map.insert(pat.id, scope_stack.last().scope_metadata); + } + + ast::pat_enum(_, ref sub_pats_opt) => { + scope_map.insert(pat.id, scope_stack.last().scope_metadata); + + for ref sub_pats in sub_pats_opt.iter() { + for &p in sub_pats.iter() { + walk_pattern(cx, p, scope_stack, scope_map); + } + } + } + + ast::pat_struct(_, ref field_pats, _) => { + scope_map.insert(pat.id, scope_stack.last().scope_metadata); + + for &ast::field_pat { pat: sub_pat, _ } in field_pats.iter() { + walk_pattern(cx, sub_pat, scope_stack, scope_map); + } + } + + ast::pat_tup(ref sub_pats) => { + scope_map.insert(pat.id, scope_stack.last().scope_metadata); + + for &sub_pat in sub_pats.iter() { + walk_pattern(cx, sub_pat, scope_stack, scope_map); + } + } + + ast::pat_box(sub_pat) | + ast::pat_uniq(sub_pat) | + ast::pat_region(sub_pat) => { + scope_map.insert(pat.id, scope_stack.last().scope_metadata); + walk_pattern(cx, sub_pat, scope_stack, scope_map); + } + + ast::pat_lit(@ref exp) => { + scope_map.insert(pat.id, scope_stack.last().scope_metadata); + walk_expr(cx, exp, scope_stack, scope_map); + } + + ast::pat_range(@ref exp1, @ref exp2) => { + scope_map.insert(pat.id, scope_stack.last().scope_metadata); + walk_expr(cx, exp1, scope_stack, scope_map); + walk_expr(cx, exp2, scope_stack, scope_map); + } + + ast::pat_vec(ref front_sub_pats, ref middle_sub_pats, ref back_sub_pats) => { + scope_map.insert(pat.id, scope_stack.last().scope_metadata); + + for &sub_pat in front_sub_pats.iter() { + walk_pattern(cx, sub_pat, scope_stack, scope_map); + } + + for &sub_pat in middle_sub_pats.iter() { + walk_pattern(cx, sub_pat, scope_stack, scope_map); + } + + for &sub_pat in back_sub_pats.iter() { + walk_pattern(cx, sub_pat, scope_stack, scope_map); + } + } + } + } + + fn walk_expr(cx: &mut CrateContext, + exp: &ast::expr, + scope_stack: &mut ~[ScopeStackEntry], + scope_map: &mut HashMap<ast::NodeId, DIScope>) { + + scope_map.insert(exp.id, scope_stack.last().scope_metadata); + + match exp.node { + ast::expr_self | + ast::expr_lit(_) | + ast::expr_break(_) | + ast::expr_again(_) | + ast::expr_path(_) => (), + + ast::expr_vstore(@ref sub_exp, _) | + ast::expr_cast(@ref sub_exp, _) | + ast::expr_addr_of(_, @ref sub_exp) | + ast::expr_field(@ref sub_exp, _, _) | + ast::expr_paren(@ref sub_exp) => walk_expr(cx, sub_exp, scope_stack, scope_map), + + ast::expr_ret(exp_opt) => match exp_opt { + Some(@ref sub_exp) => walk_expr(cx, sub_exp, scope_stack, scope_map), + None => () + }, + + ast::expr_unary(node_id, _, @ref sub_exp) => { + scope_map.insert(node_id, scope_stack.last().scope_metadata); + walk_expr(cx, sub_exp, scope_stack, scope_map); + } + + ast::expr_assign_op(node_id, _, @ref lhs, @ref rhs) | + ast::expr_index(node_id, @ref lhs, @ref rhs) | + ast::expr_binary(node_id, _, @ref lhs, @ref rhs) => { + scope_map.insert(node_id, scope_stack.last().scope_metadata); + walk_expr(cx, lhs, scope_stack, scope_map); + walk_expr(cx, rhs, scope_stack, scope_map); + } + + ast::expr_vec(ref init_expressions, _) | + ast::expr_tup(ref init_expressions) => { + for &@ref ie in init_expressions.iter() { + walk_expr(cx, ie, scope_stack, scope_map); + } + } + + ast::expr_assign(@ref sub_exp1, @ref sub_exp2) | + ast::expr_log(@ref sub_exp1, @ref sub_exp2) | + ast::expr_repeat(@ref sub_exp1, @ref sub_exp2, _) => { + walk_expr(cx, sub_exp1, scope_stack, scope_map); + walk_expr(cx, sub_exp2, scope_stack, scope_map); + } + + ast::expr_if(@ref cond_exp, ref then_block, ref opt_else_exp) => { + walk_expr(cx, cond_exp, scope_stack, scope_map); + + do with_new_scope(cx, then_block.span, scope_stack, scope_map) |c, s, m| { + walk_block(c, then_block, s, m); + } + + match *opt_else_exp { + Some(@ref else_exp) => walk_expr(cx, else_exp, scope_stack, scope_map), + _ => () + } + } + + ast::expr_while(@ref cond_exp, ref loop_body) => { + walk_expr(cx, cond_exp, scope_stack, scope_map); + + do with_new_scope(cx, loop_body.span, scope_stack, scope_map) |c, s, m| { + walk_block(c, loop_body, s, m); + } + } + + ast::expr_for_loop(_, _, _) => { + cx.sess.span_bug(exp.span, "debuginfo::populate_scope_map() - \ + Found unexpanded for-loop."); + } + + ast::expr_mac(_) => { + cx.sess.span_bug(exp.span, "debuginfo::populate_scope_map() - \ + Found unexpanded macro."); + } + + ast::expr_loop(ref block, _) | + ast::expr_block(ref block) => { + do with_new_scope(cx, block.span, scope_stack, scope_map) |c, s, m| { + walk_block(c, block, s, m); + } + } + + ast::expr_fn_block(ast::fn_decl { inputs: ref inputs, _ }, ref block) => { + + do with_new_scope(cx, block.span, scope_stack, scope_map) |c, s, m| { + + for &ast::arg { pat: pattern, _ } in inputs.iter() { + walk_pattern(c, pattern, s, m); + } + + walk_block(c, block, s, m); + } + } + + // ast::expr_loop_body(@ref inner_exp) | + ast::expr_do_body(@ref inner_exp) => { + let inner_expr_is_expr_fn_block = match *inner_exp { + ast::expr { node: ast::expr_fn_block(*), _ } => true, + _ => false + }; + + if !inner_expr_is_expr_fn_block { + cx.sess.span_bug(inner_exp.span, "debuginfo: Inner expression was expected \ + to be an ast::expr_fn_block."); + } + + walk_expr(cx, inner_exp, scope_stack, scope_map); + } + + ast::expr_call(@ref fn_exp, ref args, _) => { + walk_expr(cx, fn_exp, scope_stack, scope_map); + + for &@ref arg_exp in args.iter() { + walk_expr(cx, arg_exp, scope_stack, scope_map); + } + } + + ast::expr_method_call(node_id, @ref receiver_exp, _, _, ref args, _) => { + scope_map.insert(node_id, scope_stack.last().scope_metadata); + walk_expr(cx, receiver_exp, scope_stack, scope_map); + + for &@ref arg_exp in args.iter() { + walk_expr(cx, arg_exp, scope_stack, scope_map); + } + } + + ast::expr_match(@ref discriminant_exp, ref arms) => { + walk_expr(cx, discriminant_exp, scope_stack, scope_map); + + // for each arm we have to first walk the pattern as these might introduce new + // artificial scopes. It should be sufficient to walk only one pattern per arm, as + // they all must contain the same binding names + + for arm_ref in arms.iter() { + let arm_span = arm_ref.pats[0].span; + + do with_new_scope(cx, arm_span, scope_stack, scope_map) |c, s, m| { + walk_pattern(c, arm_ref.pats[0], s, m); + + for &@ref guard_exp in arm_ref.guard.iter() { + walk_expr(c, guard_exp, s, m) + } + + walk_block(c, &arm_ref.body, s, m); + } + } + } + + ast::expr_struct(_, ref fields, ref base_exp) => { + for &ast::Field { expr: @ref exp, _ } in fields.iter() { + walk_expr(cx, exp, scope_stack, scope_map); + } + + match *base_exp { + Some(@ref exp) => walk_expr(cx, exp, scope_stack, scope_map), + None => () + } + } + + ast::expr_inline_asm(ast::inline_asm { inputs: ref inputs, + outputs: ref outputs, + _ }) => { + // inputs, outputs: ~[(@str, @expr)] + for &(_, @ref exp) in inputs.iter() { + walk_expr(cx, exp, scope_stack, scope_map); + } + + for &(_, @ref exp) in outputs.iter() { + walk_expr(cx, exp, scope_stack, scope_map); + } + } + } + } +} diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index cb4a7f364da..d56fcb1081f 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -413,7 +413,7 @@ pub fn trans_into(bcx: @mut Block, expr: @ast::expr, dest: Dest) -> @mut Block { dest.to_str(bcx.ccx())); let _indenter = indenter(); - debuginfo::update_source_pos(bcx, expr.span); + debuginfo::update_source_pos(bcx.fcx, expr.id, expr.span); let dest = { if ty::type_is_nil(ty) || ty::type_is_bot(ty) { @@ -485,7 +485,7 @@ fn trans_to_datum_unadjusted(bcx: @mut Block, expr: @ast::expr) -> DatumBlock { debug!("trans_to_datum_unadjusted(expr=%s)", bcx.expr_to_str(expr)); let _indenter = indenter(); - debuginfo::update_source_pos(bcx, expr.span); + debuginfo::update_source_pos(bcx.fcx, expr.id, expr.span); match ty::expr_kind(bcx.tcx(), bcx.ccx().maps.method_map, expr) { ty::LvalueExpr => { diff --git a/src/test/debug-info/lexical-scope-in-for-loop.rs b/src/test/debug-info/lexical-scope-in-for-loop.rs new file mode 100644 index 00000000000..9aae26b50be --- /dev/null +++ b/src/test/debug-info/lexical-scope-in-for-loop.rs @@ -0,0 +1,77 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// xfail-win32 Broken because of LLVM bug: http://llvm.org/bugs/show_bug.cgi?id=16249 + +// compile-flags:-Z extra-debug-info +// debugger:break zzz +// debugger:run + +// FIRST ITERATION +// debugger:finish +// debugger:print x +// check:$1 = 1 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$2 = -1 +// debugger:continue + +// SECOND ITERATION +// debugger:finish +// debugger:print x +// check:$3 = 2 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$4 = -2 +// debugger:continue + +// THIRD ITERATION +// debugger:finish +// debugger:print x +// check:$5 = 3 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$6 = -3 +// debugger:continue + +// AFTER LOOP +// debugger:finish +// debugger:print x +// check:$7 = 1000000 +// debugger:continue + +fn main() { + + let range = [1, 2, 3]; + + let x = 1000000; // wan meeeljen doollaars! + + for &x in range.iter() { + zzz(); + sentinel(); + + let x = -1 * x; + + zzz(); + sentinel(); + } + + zzz(); + sentinel(); +} + +fn zzz() {()} +fn sentinel() {()} diff --git a/src/test/debug-info/lexical-scope-in-if.rs b/src/test/debug-info/lexical-scope-in-if.rs new file mode 100644 index 00000000000..296ad7ca861 --- /dev/null +++ b/src/test/debug-info/lexical-scope-in-if.rs @@ -0,0 +1,128 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// xfail-win32 Broken because of LLVM bug: http://llvm.org/bugs/show_bug.cgi?id=16249 + +// compile-flags:-Z extra-debug-info +// debugger:break zzz +// debugger:run + +// BEFORE if +// debugger:finish +// debugger:print x +// check:$1 = 999 +// debugger:print y +// check:$2 = -1 +// debugger:continue + +// AT BEGINNING of 'then' block +// debugger:finish +// debugger:print x +// check:$3 = 999 +// debugger:print y +// check:$4 = -1 +// debugger:continue + +// AFTER 1st redeclaration of 'x' +// debugger:finish +// debugger:print x +// check:$5 = 1001 +// debugger:print y +// check:$6 = -1 +// debugger:continue + +// AFTER 2st redeclaration of 'x' +// debugger:finish +// debugger:print x +// check:$7 = 1002 +// debugger:print y +// check:$8 = 1003 +// debugger:continue + +// AFTER 1st if expression +// debugger:finish +// debugger:print x +// check:$9 = 999 +// debugger:print y +// check:$10 = -1 +// debugger:continue + +// BEGINNING of else branch +// debugger:finish +// debugger:print x +// check:$11 = 999 +// debugger:print y +// check:$12 = -1 +// debugger:continue + +// BEGINNING of else branch +// debugger:finish +// debugger:print x +// check:$13 = 1004 +// debugger:print y +// check:$14 = 1005 +// debugger:continue + +// BEGINNING of else branch +// debugger:finish +// debugger:print x +// check:$15 = 999 +// debugger:print y +// check:$16 = -1 +// debugger:continue + +use std::util; + +fn main() { + + let x = 999; + let y = -1; + + zzz(); + sentinel(); + + if x < 1000 { + zzz(); + sentinel(); + + let x = 1001; + + zzz(); + sentinel(); + + let x = 1002; + let y = 1003; + zzz(); + sentinel(); + } else { + util::unreachable(); + } + + zzz(); + sentinel(); + + if x > 1000 { + util::unreachable(); + } else { + zzz(); + sentinel(); + + let x = 1004; + let y = 1005; + zzz(); + sentinel(); + } + + zzz(); + sentinel(); +} + +fn zzz() {()} +fn sentinel() {()} diff --git a/src/test/debug-info/lexical-scope-in-match.rs b/src/test/debug-info/lexical-scope-in-match.rs new file mode 100644 index 00000000000..ccdb20d8202 --- /dev/null +++ b/src/test/debug-info/lexical-scope-in-match.rs @@ -0,0 +1,148 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// xfail-win32 Broken because of LLVM bug: http://llvm.org/bugs/show_bug.cgi?id=16249 + +// compile-flags:-Z extra-debug-info +// debugger:break zzz +// debugger:run + +// debugger:finish +// debugger:print shadowed +// check:$1 = 231 +// debugger:print not_shadowed +// check:$2 = 232 +// debugger:continue + +// debugger:finish +// debugger:print shadowed +// check:$3 = 233 +// debugger:print not_shadowed +// check:$4 = 232 +// debugger:print local_to_arm +// check:$5 = 234 +// debugger:continue + +// debugger:finish +// debugger:print shadowed +// check:$6 = 236 +// debugger:print not_shadowed +// check:$7 = 232 +// debugger:continue + +// debugger:finish +// debugger:print shadowed +// check:$8 = 237 +// debugger:print not_shadowed +// check:$9 = 232 +// debugger:print local_to_arm +// check:$10 = 238 +// debugger:continue + +// debugger:finish +// debugger:print shadowed +// check:$11 = 239 +// debugger:print not_shadowed +// check:$12 = 232 +// debugger:continue + +// debugger:finish +// debugger:print shadowed +// check:$13 = 241 +// debugger:print not_shadowed +// check:$14 = 232 +// debugger:continue + +// debugger:finish +// debugger:print shadowed +// check:$15 = 243 +// debugger:print *local_to_arm +// check:$16 = 244 +// debugger:continue + +// debugger:finish +// debugger:print shadowed +// check:$17 = 231 +// debugger:print not_shadowed +// check:$18 = 232 +// debugger:continue + +struct Struct { + x: int, + y: int +} + +fn main() { + + let shadowed = 231; + let not_shadowed = 232; + + zzz(); + sentinel(); + + match (233, 234) { + (shadowed, local_to_arm) => { + + zzz(); + sentinel(); + } + } + + match (235, 236) { + // with literal + (235, shadowed) => { + + zzz(); + sentinel(); + } + _ => {} + } + + match Struct { x: 237, y: 238 } { + Struct { x: shadowed, y: local_to_arm } => { + + zzz(); + sentinel(); + } + } + + match Struct { x: 239, y: 240 } { + // ignored field + Struct { x: shadowed, _ } => { + + zzz(); + sentinel(); + } + } + + match Struct { x: 241, y: 242 } { + // with literal + Struct { x: shadowed, y: 242 } => { + + zzz(); + sentinel(); + } + _ => {} + } + + match (243, 244) { + (shadowed, ref local_to_arm) => { + + zzz(); + sentinel(); + } + } + + zzz(); + sentinel(); +} + +fn zzz() {()} +fn sentinel() {()} diff --git a/src/test/debug-info/lexical-scope-in-unconditional-loop.rs b/src/test/debug-info/lexical-scope-in-unconditional-loop.rs new file mode 100644 index 00000000000..21c4e746685 --- /dev/null +++ b/src/test/debug-info/lexical-scope-in-unconditional-loop.rs @@ -0,0 +1,127 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// xfail-win32 Broken because of LLVM bug: http://llvm.org/bugs/show_bug.cgi?id=16249 + +// compile-flags:-Z extra-debug-info +// debugger:break zzz +// debugger:run + +// FIRST ITERATION +// debugger:finish +// debugger:print x +// check:$1 = 0 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$2 = 1 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$3 = 101 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$4 = 101 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$5 = -987 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$6 = 101 +// debugger:continue + + +// SECOND ITERATION +// debugger:finish +// debugger:print x +// check:$7 = 1 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$8 = 2 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$9 = 102 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$10 = 102 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$11 = -987 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$12 = 102 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$13 = 2 +// debugger:continue + +fn main() { + + let mut x = 0; + + loop { + if x >= 2 { + break; + } + + zzz(); + sentinel(); + + x += 1; + zzz(); + sentinel(); + + // Shadow x + let x = x + 100; + zzz(); + sentinel(); + + // open scope within loop's top level scope + { + zzz(); + sentinel(); + + let x = -987; + + zzz(); + sentinel(); + } + + // Check that we get the x before the inner scope again + zzz(); + sentinel(); + } + + zzz(); + sentinel(); +} + +fn zzz() {()} +fn sentinel() {()} diff --git a/src/test/debug-info/lexical-scope-in-while.rs b/src/test/debug-info/lexical-scope-in-while.rs new file mode 100644 index 00000000000..b350130df8f --- /dev/null +++ b/src/test/debug-info/lexical-scope-in-while.rs @@ -0,0 +1,123 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// xfail-win32 Broken because of LLVM bug: http://llvm.org/bugs/show_bug.cgi?id=16249 + +// compile-flags:-Z extra-debug-info +// debugger:break zzz +// debugger:run + +// FIRST ITERATION +// debugger:finish +// debugger:print x +// check:$1 = 0 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$2 = 1 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$3 = 101 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$4 = 101 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$5 = -987 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$6 = 101 +// debugger:continue + + +// SECOND ITERATION +// debugger:finish +// debugger:print x +// check:$7 = 1 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$8 = 2 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$9 = 102 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$10 = 102 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$11 = -987 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$12 = 102 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$13 = 2 +// debugger:continue + +fn main() { + + let mut x = 0; + + while x < 2 { + zzz(); + sentinel(); + + x += 1; + zzz(); + sentinel(); + + // Shadow x + let x = x + 100; + zzz(); + sentinel(); + + // open scope within loop's top level scope + { + zzz(); + sentinel(); + + let x = -987; + + zzz(); + sentinel(); + } + + // Check that we get the x before the inner scope again + zzz(); + sentinel(); + } + + zzz(); + sentinel(); +} + +fn zzz() {()} +fn sentinel() {()} diff --git a/src/test/debug-info/lexical-scope-with-macro.rs b/src/test/debug-info/lexical-scope-with-macro.rs new file mode 100644 index 00000000000..409ecd5a0e2 --- /dev/null +++ b/src/test/debug-info/lexical-scope-with-macro.rs @@ -0,0 +1,129 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// xfail-win32 Broken because of LLVM bug: http://llvm.org/bugs/show_bug.cgi?id=16249 + +// compile-flags:-Z extra-debug-info +// debugger:break zzz +// debugger:run + +// debugger:finish +// debugger:print a +// check:$1 = 10 +// debugger:print b +// check:$2 = 34 +// debugger:continue + +// debugger:finish +// debugger:print a +// check:$3 = 890242 +// debugger:print b +// check:$4 = 34 +// debugger:continue + +// debugger:finish +// debugger:print a +// check:$5 = 10 +// debugger:print b +// check:$6 = 34 +// debugger:continue + +// debugger:finish +// debugger:print a +// check:$7 = 102 +// debugger:print b +// check:$8 = 34 +// debugger:continue + +// debugger:finish +// debugger:print a +// check:$9 = 110 +// debugger:print b +// check:$10 = 34 +// debugger:continue + +// debugger:finish +// debugger:print a +// check:$11 = 10 +// debugger:print b +// check:$12 = 34 +// debugger:continue + +// debugger:finish +// debugger:print a +// check:$13 = 10 +// debugger:print b +// check:$14 = 34 +// debugger:print c +// check:$15 = 400 +// debugger:continue + +macro_rules! trivial( + ($e1:expr) => ($e1) +) + +macro_rules! no_new_scope( + ($e1:expr) => (($e1 + 2) - 1) +) + +macro_rules! new_scope( + () => ({ + let a = 890242; + zzz(); + sentinel(); + }) +) + +macro_rules! shadow_within_macro( + ($e1:expr) => ({ + let a = $e1 + 2; + + zzz(); + sentinel(); + + let a = $e1 + 10; + + zzz(); + sentinel(); + }) +) + + +macro_rules! dup_expr( + ($e1:expr) => (($e1) + ($e1)) +) + + +fn main() { + + let a = trivial!(10); + let b = no_new_scope!(33); + + zzz(); + sentinel(); + + new_scope!(); + + zzz(); + sentinel(); + + shadow_within_macro!(100); + + zzz(); + sentinel(); + + let c = dup_expr!(10 * 20); + + zzz(); + sentinel(); +} + +fn zzz() {()} +fn sentinel() {()} diff --git a/src/test/debug-info/lexical-scopes-in-block-expression.rs b/src/test/debug-info/lexical-scopes-in-block-expression.rs new file mode 100644 index 00000000000..03de640053b --- /dev/null +++ b/src/test/debug-info/lexical-scopes-in-block-expression.rs @@ -0,0 +1,344 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// xfail-win32 Broken because of LLVM bug: http://llvm.org/bugs/show_bug.cgi?id=16249 + +// compile-flags:-Z extra-debug-info +// debugger:break zzz +// debugger:run + +// STRUCT EXPRESSION +// debugger:finish +// debugger:print val +// check:$1 = -1 +// debugger:print ten +// check:$2 = 10 +// debugger:continue + +// debugger:finish +// debugger:print val +// check:$3 = 11 +// debugger:print ten +// check:$4 = 10 +// debugger:continue + +// debugger:finish +// debugger:print val +// check:$5 = -1 +// debugger:print ten +// check:$6 = 10 +// debugger:continue + +// FUNCTION CALL +// debugger:finish +// debugger:print val +// check:$7 = -1 +// debugger:print ten +// check:$8 = 10 +// debugger:continue + +// debugger:finish +// debugger:print val +// check:$9 = 12 +// debugger:print ten +// check:$10 = 10 +// debugger:continue + +// debugger:finish +// debugger:print val +// check:$11 = -1 +// debugger:print ten +// check:$12 = 10 +// debugger:continue + +// TUPLE EXPRESSION +// debugger:finish +// debugger:print val +// check:$13 = -1 +// debugger:print ten +// check:$14 = 10 +// debugger:continue + +// debugger:finish +// debugger:print val +// check:$15 = 13 +// debugger:print ten +// check:$16 = 10 +// debugger:continue + +// debugger:finish +// debugger:print val +// check:$17 = -1 +// debugger:print ten +// check:$18 = 10 +// debugger:continue + +// VEC EXPRESSION +// debugger:finish +// debugger:print val +// check:$19 = -1 +// debugger:print ten +// check:$20 = 10 +// debugger:continue + +// debugger:finish +// debugger:print val +// check:$21 = 14 +// debugger:print ten +// check:$22 = 10 +// debugger:continue + +// debugger:finish +// debugger:print val +// check:$23 = -1 +// debugger:print ten +// check:$24 = 10 +// debugger:continue + +// REPEAT VEC EXPRESSION +// debugger:finish +// debugger:print val +// check:$25 = -1 +// debugger:print ten +// check:$26 = 10 +// debugger:continue + +// debugger:finish +// debugger:print val +// check:$27 = 15 +// debugger:print ten +// check:$28 = 10 +// debugger:continue + +// debugger:finish +// debugger:print val +// check:$29 = -1 +// debugger:print ten +// check:$30 = 10 +// debugger:continue + +// ASSIGNMENT EXPRESSION +// debugger:finish +// debugger:print val +// check:$31 = -1 +// debugger:print ten +// check:$32 = 10 +// debugger:continue + +// debugger:finish +// debugger:print val +// check:$33 = 16 +// debugger:print ten +// check:$34 = 10 +// debugger:continue + +// debugger:finish +// debugger:print val +// check:$35 = -1 +// debugger:print ten +// check:$36 = 10 +// debugger:continue + + +// ARITHMETIC EXPRESSION +// debugger:finish +// debugger:print val +// check:$37 = -1 +// debugger:print ten +// check:$38 = 10 +// debugger:continue + +// debugger:finish +// debugger:print val +// check:$39 = 17 +// debugger:print ten +// check:$40 = 10 +// debugger:continue + +// debugger:finish +// debugger:print val +// check:$41 = -1 +// debugger:print ten +// check:$42 = 10 +// debugger:continue + +// INDEX EXPRESSION +// debugger:finish +// debugger:print val +// check:$43 = -1 +// debugger:print ten +// check:$44 = 10 +// debugger:continue + +// debugger:finish +// debugger:print val +// check:$45 = 18 +// debugger:print ten +// check:$46 = 10 +// debugger:continue + +// debugger:finish +// debugger:print val +// check:$47 = -1 +// debugger:print ten +// check:$48 = 10 +// debugger:continue + +struct Point { + x: int, + y: int +} + +fn a_function(x: int) -> int { + x + 1 +} + +fn main() { + + let val = -1; + let ten = 10; + + // surrounded by struct expression + let point = Point { + x: { + zzz(); + sentinel(); + + let val = ten + 1; + + zzz(); + sentinel(); + + val + }, + y: 10 + }; + + zzz(); + sentinel(); + + // surrounded by function call + let _ = a_function({ + zzz(); + sentinel(); + + let val = ten + 2; + + zzz(); + sentinel(); + + val + }); + + zzz(); + sentinel(); + + + // surrounded by tup + let _ = ({ + zzz(); + sentinel(); + + let val = ten + 3; + + zzz(); + sentinel(); + + val + }, 0); + + zzz(); + sentinel(); + + // surrounded by vec + let _ = [{ + zzz(); + sentinel(); + + let val = ten + 4; + + zzz(); + sentinel(); + + val + }, 0, 0]; + + zzz(); + sentinel(); + + // surrounded by repeat vec + let _ = [{ + zzz(); + sentinel(); + + let val = ten + 5; + + zzz(); + sentinel(); + + val + }, ..10]; + + zzz(); + sentinel(); + + // assignment expression + let mut var = 0; + var = { + zzz(); + sentinel(); + + let val = ten + 6; + + zzz(); + sentinel(); + + val + }; + + zzz(); + sentinel(); + + // arithmetic expression + var = 10 + -{ + zzz(); + sentinel(); + + let val = ten + 7; + + zzz(); + sentinel(); + + val + } * 5; + + zzz(); + sentinel(); + + // index expression + let a_vector = [10, ..20]; + let _ = a_vector[{ + zzz(); + sentinel(); + + let val = ten + 8; + + zzz(); + sentinel(); + + val + }]; + + zzz(); + sentinel(); +} + +fn zzz() {()} +fn sentinel() {()} diff --git a/src/test/debug-info/name-shadowing-and-scope-nesting.rs b/src/test/debug-info/name-shadowing-and-scope-nesting.rs new file mode 100644 index 00000000000..ba8c08e46fd --- /dev/null +++ b/src/test/debug-info/name-shadowing-and-scope-nesting.rs @@ -0,0 +1,95 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// xfail-win32 Broken because of LLVM bug: http://llvm.org/bugs/show_bug.cgi?id=16249 + +// compile-flags:-Z extra-debug-info +// debugger:break zzz +// debugger:run + +// debugger:finish +// debugger:print x +// check:$1 = false +// debugger:print y +// check:$2 = true +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$3 = 10 +// debugger:print y +// check:$4 = true +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$5 = 10.5 +// debugger:print y +// check:$6 = 20 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$7 = true +// debugger:print y +// check:$8 = 2220 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$9 = 203203.5 +// debugger:print y +// check:$10 = 2220 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$11 = 10.5 +// debugger:print y +// check:$12 = 20 +// debugger:continue + +fn main() { + let x = false; + let y = true; + + zzz(); + sentinel(); + + let x = 10; + + zzz(); + sentinel(); + + let x = 10.5; + let y = 20; + + zzz(); + sentinel(); + + { + let x = true; + let y = 2220; + + zzz(); + sentinel(); + + let x = 203203.5; + + zzz(); + sentinel(); + } + + zzz(); + sentinel(); +} + +fn zzz() {()} +fn sentinel() {()} diff --git a/src/test/debug-info/variable-scope.rs b/src/test/debug-info/shadowed-variable.rs similarity index 74% rename from src/test/debug-info/variable-scope.rs rename to src/test/debug-info/shadowed-variable.rs index dd3a1671b78..a532f7fbc2e 100644 --- a/src/test/debug-info/variable-scope.rs +++ b/src/test/debug-info/shadowed-variable.rs @@ -8,42 +8,51 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// xfail-test +// xfail-win32 Broken because of LLVM bug: http://llvm.org/bugs/show_bug.cgi?id=16249 // compile-flags:-Z extra-debug-info // debugger:break zzz // debugger:run + // debugger:finish // debugger:print x // check:$1 = false // debugger:print y // check:$2 = true - // debugger:continue + // debugger:finish // debugger:print x // check:$3 = 10 - +// debugger:print y +// check:$4 = true // debugger:continue + // debugger:finish // debugger:print x -// check:$4 = false +// check:$5 = 10.5 // debugger:print y -// check:$5 = 11 +// check:$6 = 20 +// debugger:continue fn main() { let x = false; let y = true; zzz(); + sentinel(); - { - let x = 10; - zzz(); - } + let x = 10; - let y = 11; zzz(); + sentinel(); + + let x = 10.5; + let y = 20; + + zzz(); + sentinel(); } fn zzz() {()} +fn sentinel() {()} diff --git a/src/test/debug-info/simple-lexical-scope.rs b/src/test/debug-info/simple-lexical-scope.rs new file mode 100644 index 00000000000..96a394b5ed7 --- /dev/null +++ b/src/test/debug-info/simple-lexical-scope.rs @@ -0,0 +1,87 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// xfail-win32 Broken because of LLVM bug: http://llvm.org/bugs/show_bug.cgi?id=16249 + +// compile-flags:-Z extra-debug-info +// debugger:break zzz +// debugger:run + +// debugger:finish +// debugger:print x +// check:$1 = false +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$2 = false +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$3 = 10 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$4 = 10 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$5 = 10.5 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$6 = 10 +// debugger:continue + +// debugger:finish +// debugger:print x +// check:$7 = false +// debugger:continue + + +fn main() { + let x = false; + + zzz(); + sentinel(); + + { + zzz(); + sentinel(); + + let x = 10; + + zzz(); + sentinel(); + + { + zzz(); + sentinel(); + + let x = 10.5; + + zzz(); + sentinel(); + } + + zzz(); + sentinel(); + } + + zzz(); + sentinel(); +} + +fn zzz() {()} +fn sentinel() {()}