Skip to content

Commit

Permalink
Improved support of collapse_debuginfo attribute for macroses. Enable…
Browse files Browse the repository at this point in the history
…d by default for builtin and core/std macroses.
  • Loading branch information
azhogin committed Dec 13, 2023
1 parent 77d1699 commit 374c882
Show file tree
Hide file tree
Showing 13 changed files with 396 additions and 43 deletions.
10 changes: 1 addition & 9 deletions compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,7 @@ impl DebugContext {
// In order to have a good line stepping behavior in debugger, we overwrite debug
// locations of macro expansions with that of the outermost expansion site (when the macro is
// annotated with `#[collapse_debuginfo]` or when `-Zdebug-macros` is provided).
let span = if tcx.should_collapse_debuginfo(span) {
span
} else {
// Walk up the macro expansion chain until we reach a non-expanded span.
// We also stop at the function body level because no line stepping can occur
// at the level above that.
rustc_span::hygiene::walk_chain(span, function_span.ctxt())
};

let span = tcx.collapsed_debuginfo(span, function_span.ctxt());
match tcx.sess.source_map().lookup_line(span.lo()) {
Ok(SourceFileAndLine { sf: file, line }) => {
let line_pos = file.lines()[line];
Expand Down
17 changes: 6 additions & 11 deletions compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,21 +228,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
/// In order to have a good line stepping behavior in debugger, we overwrite debug
/// locations of macro expansions with that of the outermost expansion site (when the macro is
/// annotated with `#[collapse_debuginfo]` or when `-Zdebug-macros` is provided).
fn adjust_span_for_debugging(&self, mut span: Span) -> Span {
fn adjust_span_for_debugging(&self, span: Span) -> Span {
// Bail out if debug info emission is not enabled.
if self.debug_context.is_none() {
return span;
}

if self.cx.tcx().should_collapse_debuginfo(span) {
// Walk up the macro expansion chain until we reach a non-expanded span.
// We also stop at the function body level because no line stepping can occur
// at the level above that.
// Use span of the outermost expansion site, while keeping the original lexical scope.
span = rustc_span::hygiene::walk_chain(span, self.mir.span.ctxt());
}

span
// Walk up the macro expansion chain until we reach a non-expanded span.
// We also stop at the function body level because no line stepping can occur
// at the level above that.
// Use span of the outermost expansion site, while keeping the original lexical scope.
self.cx.tcx().collapsed_debuginfo(span, self.mir.span.ctxt())
}

fn spill_operand_to_stack(
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_expand/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,8 @@ impl SyntaxExtension {
let local_inner_macros = attr::find_by_name(attrs, sym::macro_export)
.and_then(|macro_export| macro_export.meta_item_list())
.is_some_and(|l| attr::list_contains_name(&l, sym::local_inner_macros));
let collapse_debuginfo = attr::contains_name(attrs, sym::collapse_debuginfo);
let collapse_debuginfo = attr::contains_name(attrs, sym::collapse_debuginfo)
|| attr::contains_name(attrs, sym::rustc_builtin_macro);
tracing::debug!(?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe);

let (builtin_name, helper_attrs) = attr::find_by_name(attrs, sym::rustc_builtin_macro)
Expand Down
36 changes: 32 additions & 4 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ use rustc_query_system::ich::StableHashingContext;
use rustc_serialize::{Decodable, Encodable};
use rustc_session::lint::LintBuffer;
pub use rustc_session::lint::RegisteredTools;
use rustc_span::hygiene::MacroKind;
use rustc_span::hygiene::{HygieneData, MacroKind};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{ExpnId, ExpnKind, Span};
use rustc_span::{ExpnId, ExpnKind, Span, SyntaxContext};
use rustc_target::abi::{Align, FieldIdx, Integer, IntegerType, VariantIdx};
pub use rustc_target::abi::{ReprFlags, ReprOptions};
pub use rustc_type_ir::{DebugWithInfcx, InferCtxtLike, WithInfcx};
Expand Down Expand Up @@ -2515,6 +2515,28 @@ impl<'tcx> TyCtxt<'tcx> {
(ident, scope)
}

/// Returns `true` if `span` originates in a macro's expansion where debuginfo should be
/// collapsed.
pub fn in_macro_expansion_with_collapse_debuginfo(
self,
span: Span,
hdata: &HygieneData,
) -> bool {
if span.from_expansion() {
let outer = hdata.expn_data(hdata.outer_expn(span.ctxt())).clone();
let in_std_macro = match outer.macro_def_id {
Some(macro_def_id) => {
let crate_name = self.crate_name(macro_def_id.krate);
crate_name == sym::std || crate_name == sym::core
}
None => false,
};
!matches!(outer.kind, ExpnKind::Macro(..)) || outer.collapse_debuginfo || in_std_macro
} else {
false
}
}

/// Returns `true` if the debuginfo for `span` should be collapsed to the outermost expansion
/// site. Only applies when `Span` is the result of macro expansion.
///
Expand All @@ -2523,15 +2545,21 @@ impl<'tcx> TyCtxt<'tcx> {
/// - If `collapse_debuginfo` is not enabled, then debuginfo is collapsed by default.
///
/// When `-Zdebug-macros` is provided then debuginfo will never be collapsed.
pub fn should_collapse_debuginfo(self, span: Span) -> bool {
pub fn should_collapse_debuginfo(self, hdata: &HygieneData, span: Span) -> bool {
!self.sess.opts.unstable_opts.debug_macros
&& if self.features().collapse_debuginfo {
span.in_macro_expansion_with_collapse_debuginfo()
self.in_macro_expansion_with_collapse_debuginfo(span, hdata)
} else {
span.from_expansion()
}
}

pub fn collapsed_debuginfo(self, span: Span, upto: SyntaxContext) -> Span {
rustc_span::hygiene::walk_chain_collapsed(span, upto, |hdata, span| {
self.should_collapse_debuginfo(hdata, span)
})
}

#[inline]
pub fn is_const_fn_raw(self, def_id: DefId) -> bool {
matches!(
Expand Down
58 changes: 47 additions & 11 deletions compiler/rustc_span/src/hygiene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ impl ExpnId {
}

#[derive(Debug)]
pub(crate) struct HygieneData {
pub struct HygieneData {
/// Each expansion should have an associated expansion data, but sometimes there's a delay
/// between creation of an expansion ID and obtaining its data (e.g. macros are collected
/// first and then resolved later), so we use an `Option` here.
Expand Down Expand Up @@ -377,7 +377,7 @@ impl HygieneData {
self.local_expn_data[expn_id].as_ref().expect("no expansion data for an expansion ID")
}

fn expn_data(&self, expn_id: ExpnId) -> &ExpnData {
pub fn expn_data(&self, expn_id: ExpnId) -> &ExpnData {
if let Some(expn_id) = expn_id.as_local() {
self.local_expn_data[expn_id].as_ref().expect("no expansion data for an expansion ID")
} else {
Expand Down Expand Up @@ -412,7 +412,7 @@ impl HygieneData {
self.syntax_context_data[ctxt.0 as usize].opaque_and_semitransparent
}

fn outer_expn(&self, ctxt: SyntaxContext) -> ExpnId {
pub fn outer_expn(&self, ctxt: SyntaxContext) -> ExpnId {
self.syntax_context_data[ctxt.0 as usize].outer_expn
}

Expand Down Expand Up @@ -443,18 +443,38 @@ impl HygieneData {
}

fn walk_chain(&self, mut span: Span, to: SyntaxContext) -> Span {
debug!("walk_chain({:?}, {:?})", span, to);
debug!("walk_chain: span ctxt = {:?}", span.ctxt());
while span.from_expansion() && span.ctxt() != to {
trace!("walk_chain({:?}, {:?})", span, to);
trace!("walk_chain: span ctxt = {:?}", span.ctxt());
while span.ctxt() != to && span.from_expansion() {
let outer_expn = self.outer_expn(span.ctxt());
debug!("walk_chain({:?}): outer_expn={:?}", span, outer_expn);
trace!("walk_chain({:?}): outer_expn={:?}", span, outer_expn);
let expn_data = self.expn_data(outer_expn);
debug!("walk_chain({:?}): expn_data={:?}", span, expn_data);
trace!("walk_chain({:?}): expn_data={:?}", span, expn_data);
span = expn_data.call_site;
}
span
}

fn walk_chain_collapsed<F: Fn(&HygieneData, Span) -> bool>(
&self,
mut span: Span,
to: SyntaxContext,
should_collapse: F,
) -> Span {
let orig_span = span;
trace!("walk_chain_collapsed({:?}, {:?})", span, to);
trace!("walk_chain_collapsed: span ctxt = {:?}", span.ctxt());
while span.ctxt() != to && should_collapse(&self, span) {
let outer_expn = self.outer_expn(span.ctxt());
trace!("walk_chain_collapsed({:?}): outer_expn={:?}", span, outer_expn);
let expn_data = self.expn_data(outer_expn);
trace!("walk_chain_collapsed({:?}): expn_data={:?}", span, expn_data);
span = expn_data.call_site;
}
trace!("walk_chain_collapsed: for span {:?} >>> return span = {:?}", orig_span, span);
span
}

fn adjust(&self, ctxt: &mut SyntaxContext, expn_id: ExpnId) -> Option<ExpnId> {
let mut scope = None;
while !self.is_descendant_of(expn_id, self.outer_expn(*ctxt)) {
Expand Down Expand Up @@ -568,9 +588,25 @@ impl HygieneData {
}

pub fn walk_chain(span: Span, to: SyntaxContext) -> Span {
debug!("walk_chain call({:?}, {:?})", span, to);
HygieneData::with(|data| data.walk_chain(span, to))
}

pub fn walk_chain_collapsed<F: Fn(&HygieneData, Span) -> bool>(
span: Span,
to: SyntaxContext,
should_collapse: F,
) -> Span {
debug!("walk_chain_collapsed call({:?}, {:?})", span, to);
HygieneData::with(|hdata| {
if should_collapse(hdata, span) {
hdata.walk_chain_collapsed(span, to, should_collapse)
} else {
span
}
})
}

pub fn update_dollar_crate_names(mut get_name: impl FnMut(SyntaxContext) -> Symbol) {
// The new contexts that need updating are at the end of the list and have `$crate` as a name.
let (len, to_update) = HygieneData::with(|data| {
Expand Down Expand Up @@ -938,7 +974,7 @@ pub struct ExpnData {
pub local_inner_macros: bool,
/// Should debuginfo for the macro be collapsed to the outermost expansion site (in other
/// words, was the macro definition annotated with `#[collapse_debuginfo]`)?
pub(crate) collapse_debuginfo: bool,
pub collapse_debuginfo: bool,
}

impl !PartialEq for ExpnData {}
Expand Down Expand Up @@ -1279,7 +1315,7 @@ pub fn decode_expn_id(
decode_data: impl FnOnce(ExpnId) -> (ExpnData, ExpnHash),
) -> ExpnId {
if index == 0 {
trace!("decode_expn_id: deserialized root");
//trace!("decode_expn_id: deserialized root");
return ExpnId::root();
}

Expand Down Expand Up @@ -1312,7 +1348,7 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext
) -> SyntaxContext {
let raw_id: u32 = Decodable::decode(d);
if raw_id == 0 {
trace!("decode_syntax_context: deserialized root");
//trace!("decode_syntax_context: deserialized root");
// The root is special
return SyntaxContext::root();
}
Expand Down
7 changes: 0 additions & 7 deletions compiler/rustc_span/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -568,13 +568,6 @@ impl Span {
self.ctxt() != SyntaxContext::root()
}

/// Returns `true` if `span` originates in a macro's expansion where debuginfo should be
/// collapsed.
pub fn in_macro_expansion_with_collapse_debuginfo(self) -> bool {
let outer_expn = self.ctxt().outer_expn_data();
matches!(outer_expn.kind, ExpnKind::Macro(..)) && outer_expn.collapse_debuginfo
}

/// Returns `true` if `span` originates in a derive-macro's expansion.
pub fn in_derive_expansion(self) -> bool {
matches!(self.ctxt().outer_expn_data().kind, ExpnKind::Macro(MacroKind::Derive, _))
Expand Down
1 change: 1 addition & 0 deletions library/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@
#![feature(c_unwind)]
#![feature(cfg_target_thread_local)]
#![feature(cfi_encoding)]
#![feature(collapse_debuginfo)]
#![feature(concat_idents)]
#![feature(const_mut_refs)]
#![feature(const_trait_impl)]
Expand Down
59 changes: 59 additions & 0 deletions tests/debuginfo/collapse-debuginfo-in-non-collapse-macro.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// ignore-lldb
#![feature(collapse_debuginfo)]

// Test that statement, skipped by macros, is correctly processed in debuginfo

// compile-flags:-g

// === GDB TESTS ===================================================================================

// gdb-command:run
// gdb-command:next
// gdb-check:[...]#loc2[...]
// gdb-command:step
// gdb-command:frame
// gdb-check:[...]#loc_call1_pre[...]
// gdb-command:next
// gdb-command:frame
// gdb-check:[...]#loc_in_proxy[...]
// gdb-command:finish
// gdb-check:[...]#loc4[...]
// gdb-command:next
// gdb-command:frame
// gdb-check:[...]#loc5[...]
// gdb-command:continue


macro_rules! proxy_println {
($($arg:tt)*) => {{
println!($($arg)*); // #loc_in_proxy
}};
}

// Macro accepts 3 statements and removes the 2nd statement
macro_rules! remove_second_statement {
($s1:stmt; $s2:stmt; $s3:stmt;) => { $s1 $s3 }
}

fn call1() {
let rv = 0; // #loc_call1_pre
proxy_println!("one"); // #loc_call1
}

fn call2() {
proxy_println!("two"); // #loc_call2
}

fn call3() {
proxy_println!("three"); // #loc_call3
}

fn main() {
let ret = 0; // #break, step should go to call1
remove_second_statement! { // #loc1
call1(); // #loc2, breakpoint should set to call1, step should go call3
call2(); // #loc3, breakpoint should set to call3
call3(); // #loc4, breakpoint should set to call3, step should go closing brace
}
std::process::exit(ret); // #loc5
}
54 changes: 54 additions & 0 deletions tests/debuginfo/collapse-debuginfo-through-desugar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// ignore-lldb
#![feature(collapse_debuginfo)]

// Test that statement, skipped by macros, is correctly processed in debuginfo

// compile-flags:-g

// === GDB TESTS ===================================================================================

// gdb-command:run
// gdb-command:next
// gdb-command:frame
// gdb-check:[...]#loc2[...]
// gdb-command:step
// gdb-command:frame
// gdb-command:step
// gdb-command:frame
// gdb-check:[...]#loc_call1_println[...]
// gdb-command:finish
// gdb-command:finish
// gdb-check:[...]#loc4[...]
// gdb-command:next
// gdb-command:frame
// gdb-check:[...]#loc5[...]
// gdb-command:continue

// Macro accepts 3 statements and removes the 2nd statement
macro_rules! remove_second_statement {
($s1:stmt; $s2:stmt; $s3:stmt;) => { $s1 $s3 }
}

fn call1() {
(||{
println!("one") // #loc_call1_println
})(); // #loc_call1
}

fn call2() {
println!("two"); // #loc_call2
}

fn call3() {
println!("three"); // #loc_call3
}

fn main() {
let ret = 0; // #break, step should go to call1
remove_second_statement! { // #loc1
call1(); // #loc2, breakpoint should set to call1, step should go call3
call2(); // #loc3, breakpoint should set to call3
call3(); // #loc4, breakpoint should set to call3, step should go closing brace
}
std::process::exit(ret); // #loc5
}

0 comments on commit 374c882

Please sign in to comment.