diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 7e136c1b24cb..60c6bd61cd73 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -1289,14 +1289,36 @@ struct TupleMemberDescriptionFactory<'tcx> { impl<'tcx> TupleMemberDescriptionFactory<'tcx> { fn create_member_descriptions(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec> { + // For closures and generators, name the captured upvars + // with the help of `CapturedPlace::to_mangled_name`. + let closure_def_id = match *self.ty.kind() { + ty::Generator(def_id, ..) => def_id.as_local(), + ty::Closure(def_id, ..) => def_id.as_local(), + _ => None, + }; + let captures = match closure_def_id { + Some(local_def_id) => { + let typeck_results = cx.tcx.typeck(local_def_id); + let captures = typeck_results + .closure_min_captures_flattened(local_def_id.to_def_id()) + .collect::>(); + Some(captures) + } + _ => None, + }; + let layout = cx.layout_of(self.ty); self.component_types .iter() .enumerate() .map(|(i, &component_type)| { let (size, align) = cx.size_and_align_of(component_type); + let name = captures + .as_ref() + .map(|c| c[i].to_mangled_name(cx.tcx)) + .unwrap_or_else(|| format!("__{}", i)); MemberDescription { - name: format!("__{}", i), + name, type_metadata: type_metadata(cx, component_type, self.span), offset: layout.fields.offset(i), size, diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index 0706a057dd0c..0d4c63505072 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -159,6 +159,54 @@ impl CapturedPlace<'tcx> { place_to_string_for_capture(tcx, &self.place) } + /// Returns mangled names of captured upvars. Here are some examples: + /// - `_captured_val__name__field` + /// - `_captured_ref__name__field` + /// + /// The purpose is to use those names in debuginfo. They should be human-understandable. + /// Without the names, the end users may get confused when the debuggers just print some + /// pointers in closures or generators. + pub fn to_mangled_name(&self, tcx: TyCtxt<'tcx>) -> String { + let prefix = match self.info.capture_kind { + ty::UpvarCapture::ByValue(_) => "_captured_val__", + ty::UpvarCapture::ByRef(_) => "_captured_ref__", + }; + + let hir_id = match self.place.base { + HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, + base => bug!("Expected an upvar, found {:?}", base), + }; + let name = tcx.hir().name(hir_id); + + let mut ty = self.place.base_ty; + let mut fields = String::new(); + for proj in self.place.projections.iter() { + match proj.kind { + HirProjectionKind::Field(idx, variant) => match ty.kind() { + ty::Tuple(_) => fields = format!("{}__{}", fields, idx), + ty::Adt(def, ..) => { + fields = format!( + "{}__{}", + fields, + def.variants[variant].fields[idx as usize].ident.name.as_str(), + ); + } + ty => { + bug!("Unexpected type {:?} for `Field` projection", ty) + } + }, + + // Ignore derefs for now, as they are likely caused by + // autoderefs that don't appear in the original code. + HirProjectionKind::Deref => {} + proj => bug!("Unexpected projection {:?} in captured place", proj), + } + ty = proj.ty; + } + + prefix.to_owned() + &name.to_string() + &fields + } + /// Returns the hir-id of the root variable for the captured place. /// e.g., if `a.b.c` was captured, would return the hir-id for `a`. pub fn get_root_variable(&self) -> hir::HirId { diff --git a/src/test/debuginfo/captured-fields.rs b/src/test/debuginfo/captured-fields.rs new file mode 100644 index 000000000000..5489fa147206 --- /dev/null +++ b/src/test/debuginfo/captured-fields.rs @@ -0,0 +1,87 @@ +// compile-flags:-g + +// === GDB TESTS =================================================================================== + +// gdb-command:run +// gdb-command:print test +// gdbr-check:$1 = captured_fields::main::{closure#0} {_captured_ref__my_ref__my_field1: 0x[...]} +// gdb-command:continue +// gdb-command:print test +// gdbr-check:$2 = captured_fields::main::{closure#1} {_captured_ref__my_ref__my_field2: 0x[...]} +// gdb-command:continue +// gdb-command:print test +// gdbr-check:$3 = captured_fields::main::{closure#2} {_captured_ref__my_ref: 0x[...]} +// gdb-command:continue +// gdb-command:print test +// gdbr-check:$4 = captured_fields::main::{closure#3} {_captured_val__my_ref: 0x[...]} +// gdb-command:continue +// gdb-command:print test +// gdbr-check:$5 = captured_fields::main::{closure#4} {_captured_val__my_var: captured_fields::MyStruct {my_field1: 11, my_field2: 22}} +// gdb-command:continue + +// === LLDB TESTS ================================================================================== + +// lldb-command:run +// lldb-command:print test +// lldbg-check:(captured_fields::main::{closure#0}) $0 = { _captured_ref__my_ref__my_field1 = 0x[...] } +// lldb-command:continue +// lldb-command:print test +// lldbg-check:(captured_fields::main::{closure#1}) $1 = { _captured_ref__my_ref__my_field2 = 0x[...] } +// lldb-command:continue +// lldb-command:print test +// lldbg-check:(captured_fields::main::{closure#2}) $2 = { _captured_ref__my_ref = 0x[...] } +// lldb-command:continue +// lldb-command:print test +// lldbg-check:(captured_fields::main::{closure#3}) $3 = { _captured_val__my_ref = 0x[...] } +// lldb-command:continue +// lldb-command:print test +// lldbg-check:(captured_fields::main::{closure#4}) $4 = { _captured_val__my_var = { my_field1 = 11 my_field2 = 22 } } +// lldb-command:continue + +#![feature(capture_disjoint_fields)] +#![allow(unused)] + +struct MyStruct { + my_field1: u32, + my_field2: u32, +} + +fn main() { + let mut my_var = MyStruct { + my_field1: 11, + my_field2: 22, + }; + let my_ref = &mut my_var; + + let test = || { + let a = &mut my_ref.my_field1; + }; + + _zzz(); // #break + + let test = || { + let a = &my_ref.my_field2; + }; + + _zzz(); // #break + + let test = || { + let a = &my_ref; + }; + + _zzz(); // #break + + let test = || { + let a = my_ref; + }; + + _zzz(); // #break + + let test = || { + let a = my_var; + }; + + _zzz(); // #break +} + +fn _zzz() {} diff --git a/src/test/debuginfo/generator-objects.rs b/src/test/debuginfo/generator-objects.rs index 7ac3304aa963..46a3d7924a1d 100644 --- a/src/test/debuginfo/generator-objects.rs +++ b/src/test/debuginfo/generator-objects.rs @@ -11,16 +11,16 @@ // gdb-command:run // gdb-command:print b -// gdb-check:$1 = generator_objects::main::{generator#0}::Unresumed(0x[...]) +// gdb-check:$1 = generator_objects::main::{generator#0}::Unresumed{_captured_ref__a: 0x[...]} // gdb-command:continue // gdb-command:print b -// gdb-check:$2 = generator_objects::main::{generator#0}::Suspend0{c: 6, d: 7, __0: 0x[...]} +// gdb-check:$2 = generator_objects::main::{generator#0}::Suspend0{c: 6, d: 7, _captured_ref__a: 0x[...]} // gdb-command:continue // gdb-command:print b -// gdb-check:$3 = generator_objects::main::{generator#0}::Suspend1{c: 7, d: 8, __0: 0x[...]} +// gdb-check:$3 = generator_objects::main::{generator#0}::Suspend1{c: 7, d: 8, _captured_ref__a: 0x[...]} // gdb-command:continue // gdb-command:print b -// gdb-check:$4 = generator_objects::main::{generator#0}::Returned(0x[...]) +// gdb-check:$4 = generator_objects::main::{generator#0}::Returned{_captured_ref__a: 0x[...]} // === LLDB TESTS ================================================================================== diff --git a/src/test/debuginfo/issue-57822.rs b/src/test/debuginfo/issue-57822.rs index f6d2146fe11f..22d55ae989fb 100644 --- a/src/test/debuginfo/issue-57822.rs +++ b/src/test/debuginfo/issue-57822.rs @@ -11,17 +11,17 @@ // gdb-command:run // gdb-command:print g -// gdb-check:$1 = issue_57822::main::{closure#1} (issue_57822::main::{closure#0} (1)) +// gdb-check:$1 = issue_57822::main::{closure#1} {_captured_val__f: issue_57822::main::{closure#0} {_captured_val__x: 1}} // gdb-command:print b -// gdb-check:$2 = issue_57822::main::{generator#3}::Unresumed(issue_57822::main::{generator#2}::Unresumed(2)) +// gdb-check:$2 = issue_57822::main::{generator#3}::Unresumed{_captured_val__a: issue_57822::main::{generator#2}::Unresumed{_captured_val__y: 2}} // === LLDB TESTS ================================================================================== // lldb-command:run // lldb-command:print g -// lldbg-check:(issue_57822::main::{closure#1}) $0 = { 0 = { 0 = 1 } } +// lldbg-check:(issue_57822::main::{closure#1}) $0 = { _captured_val__f = { _captured_val__x = 1 } } // lldb-command:print b // lldbg-check:(issue_57822::main::{generator#3}) $1 =