Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Name the captured upvars for closures/generators in debuginfo #85020

Merged
merged 4 commits into from
Aug 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1280,6 +1280,31 @@ fn prepare_struct_metadata(
// Tuples
//=-----------------------------------------------------------------------------

/// Returns names of captured upvars for closures and generators.
///
/// Here are some examples:
/// - `name__field1__field2` when the upvar is captured by value.
/// - `_ref__name__field` when the upvar is captured by reference.
fn closure_saved_names_of_captured_variables(tcx: TyCtxt<'tcx>, def_id: DefId) -> Vec<String> {
let body = tcx.optimized_mir(def_id);

body.var_debug_info
.iter()
.filter_map(|var| {
let is_ref = match var.value {
mir::VarDebugInfoContents::Place(place) if place.local == mir::Local::new(1) => {
// The projection is either `[.., Field, Deref]` or `[.., Field]`. It
// implies whether the variable is captured by value or by reference.
matches!(place.projection.last().unwrap(), mir::ProjectionElem::Deref)
}
_ => return None,
};
let prefix = if is_ref { "_ref__" } else { "" };
Some(prefix.to_owned() + &var.name.as_str())
tmandry marked this conversation as resolved.
Show resolved Hide resolved
})
.collect::<Vec<_>>()
}

/// Creates `MemberDescription`s for the fields of a tuple.
struct TupleMemberDescriptionFactory<'tcx> {
ty: Ty<'tcx>,
Expand All @@ -1289,14 +1314,25 @@ struct TupleMemberDescriptionFactory<'tcx> {

impl<'tcx> TupleMemberDescriptionFactory<'tcx> {
fn create_member_descriptions(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec<MemberDescription<'ll>> {
let mut capture_names = match *self.ty.kind() {
ty::Generator(def_id, ..) | ty::Closure(def_id, ..) => {
Some(closure_saved_names_of_captured_variables(cx.tcx, def_id).into_iter())
}
_ => 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 = if let Some(names) = capture_names.as_mut() {
names.next().unwrap()
} else {
format!("__{}", i)
};
MemberDescription {
name: format!("__{}", i),
name,
type_metadata: type_metadata(cx, component_type, self.span),
offset: layout.fields.offset(i),
size,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/dep_graph/dep_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ pub type DepNode = rustc_query_system::dep_graph::DepNode<DepKind>;
// required that their size stay the same, but we don't want to change
// it inadvertently. This assert just ensures we're aware of any change.
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
static_assert_size!(DepNode, 17);
static_assert_size!(DepNode, 18);

#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
static_assert_size!(DepNode, 24);
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,16 @@ rustc_queries! {
}
}

query symbols_for_closure_captures(
key: (LocalDefId, DefId)
) -> Vec<rustc_span::Symbol> {
desc {
|tcx| "symbols for captures of closure `{}` in `{}`",
tcx.def_path_str(key.1),
tcx.def_path_str(key.0.to_def_id())
}
}

/// MIR after our optimization passes have run. This is MIR that is ready
/// for codegen. This is also the only query that can fetch non-local MIR, at present.
query optimized_mir(key: DefId) -> &'tcx mir::Body<'tcx> {
Expand Down
54 changes: 53 additions & 1 deletion compiler/rustc_middle/src/ty/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ use crate::hir::place::{
};
use crate::{mir, ty};

use std::fmt::Write;

use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_span::Span;
use rustc_span::{Span, Symbol};

use super::{Ty, TyCtxt};

Expand Down Expand Up @@ -159,6 +161,43 @@ impl CapturedPlace<'tcx> {
place_to_string_for_capture(tcx, &self.place)
}

/// Returns a symbol of the captured upvar, which looks like `name__field1__field2`.
fn to_symbol(&self, tcx: TyCtxt<'tcx>) -> Symbol {
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 mut symbol = tcx.hir().name(hir_id).as_str().to_string();

let mut ty = self.place.base_ty;
for proj in self.place.projections.iter() {
lrh2000 marked this conversation as resolved.
Show resolved Hide resolved
match proj.kind {
HirProjectionKind::Field(idx, variant) => match ty.kind() {
ty::Tuple(_) => write!(&mut symbol, "__{}", idx).unwrap(),
ty::Adt(def, ..) => {
write!(
&mut symbol,
"__{}",
def.variants[variant].fields[idx as usize].ident.name.as_str(),
)
.unwrap();
}
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;
}

Symbol::intern(&symbol)
}

/// 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 {
Expand Down Expand Up @@ -209,6 +248,15 @@ impl CapturedPlace<'tcx> {
}
}

fn symbols_for_closure_captures<'tcx>(
tcx: TyCtxt<'tcx>,
def_id: (LocalDefId, DefId),
) -> Vec<Symbol> {
let typeck_results = tcx.typeck(def_id.0);
let captures = typeck_results.closure_min_captures_flattened(def_id.1);
captures.into_iter().map(|captured_place| captured_place.to_symbol(tcx)).collect()
}

/// Return true if the `proj_possible_ancestor` represents an ancestor path
/// to `proj_capture` or `proj_possible_ancestor` is same as `proj_capture`,
/// assuming they both start off of the same root variable.
Expand Down Expand Up @@ -393,3 +441,7 @@ impl BorrowKind {
}
}
}

pub fn provide(providers: &mut ty::query::Providers) {
*providers = ty::query::Providers { symbols_for_closure_captures, ..*providers }
}
8 changes: 7 additions & 1 deletion compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ pub use self::IntVarValue::*;
pub use self::Variance::*;
pub use adt::*;
pub use assoc::*;
pub use closure::*;
pub use generics::*;
pub use vtable::*;

Expand Down Expand Up @@ -55,6 +54,12 @@ pub use rustc_type_ir::*;

pub use self::binding::BindingMode;
pub use self::binding::BindingMode::*;
pub use self::closure::{
is_ancestor_or_same_capture, place_to_string_for_capture, BorrowKind, CaptureInfo,
CapturedPlace, ClosureKind, MinCaptureInformationMap, MinCaptureList,
RootVariableMinCaptureList, UpvarBorrow, UpvarCapture, UpvarCaptureMap, UpvarId, UpvarListMap,
UpvarPath, CAPTURE_STRUCT_LOCAL,
};
pub use self::consts::{Const, ConstInt, ConstKind, InferConst, ScalarInt, Unevaluated, ValTree};
pub use self::context::{
tls, CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations,
Expand Down Expand Up @@ -1979,6 +1984,7 @@ pub fn ast_uint_ty(uty: UintTy) -> ast::UintTy {
}

pub fn provide(providers: &mut ty::query::Providers) {
closure::provide(providers);
context::provide(providers);
erase_regions::provide(providers);
layout::provide(providers);
Expand Down
21 changes: 8 additions & 13 deletions compiler/rustc_mir_build/src/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use rustc_middle::mir::*;
use rustc_middle::thir::{BindingMode, Expr, ExprId, LintLevel, PatKind, Thir};
use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeckResults};
use rustc_span::symbol::{kw, sym};
use rustc_span::symbol::sym;
use rustc_span::Span;
use rustc_target::spec::abi::Abi;
use rustc_target::spec::PanicStrategy;
Expand Down Expand Up @@ -959,13 +959,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ty::Generator(_, substs, _) => ty::UpvarSubsts::Generator(substs),
_ => span_bug!(self.fn_span, "upvars with non-closure env ty {:?}", closure_ty),
};
let def_id = self.def_id.as_local().unwrap();
let capture_syms = tcx.symbols_for_closure_captures((def_id, fn_def_id));
let capture_tys = upvar_substs.upvar_tys();
let captures_with_tys =
hir_typeck_results.closure_min_captures_flattened(fn_def_id).zip(capture_tys);
let captures_with_tys = hir_typeck_results
.closure_min_captures_flattened(fn_def_id)
.zip(capture_tys.zip(capture_syms));

self.upvar_mutbls = captures_with_tys
.enumerate()
.map(|(i, (captured_place, ty))| {
.map(|(i, (captured_place, (ty, sym)))| {
let capture = captured_place.info.capture_kind;
let var_id = match captured_place.place.base {
HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
Expand All @@ -974,14 +977,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {

let mutability = captured_place.mutability;

// FIXME(project-rfc-2229#8): Store more precise information
let mut name = kw::Empty;
if let Some(Node::Binding(pat)) = tcx_hir.find(var_id) {
if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {
name = ident.name;
}
}

let mut projs = closure_env_projs.clone();
projs.push(ProjectionElem::Field(Field::new(i), ty));
match capture {
Expand All @@ -992,7 +987,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
};

self.var_debug_info.push(VarDebugInfo {
name,
name: sym,
source_info: SourceInfo::outermost(tcx_hir.span(var_id)),
value: VarDebugInfoContents::Place(Place {
local: ty::CAPTURE_STRUCT_LOCAL,
Expand Down
99 changes: 99 additions & 0 deletions src/test/debuginfo/captured-fields-1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// compile-flags:-g

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

// gdb-command:run
// gdb-command:print test
// gdbr-check:$1 = captured_fields_1::main::{closure#0} {_ref__my_ref__my_field1: 0x[...]}
// gdb-command:continue
// gdb-command:print test
// gdbr-check:$2 = captured_fields_1::main::{closure#1} {_ref__my_ref__my_field2: 0x[...]}
// gdb-command:continue
// gdb-command:print test
// gdbr-check:$3 = captured_fields_1::main::{closure#2} {_ref__my_ref: 0x[...]}
// gdb-command:continue
// gdb-command:print test
// gdbr-check:$4 = captured_fields_1::main::{closure#3} {my_ref: 0x[...]}
// gdb-command:continue
// gdb-command:print test
// gdbr-check:$5 = captured_fields_1::main::{closure#4} {my_var__my_field2: 22}
// gdb-command:continue
// gdb-command:print test
// gdbr-check:$6 = captured_fields_1::main::{closure#5} {my_var: captured_fields_1::MyStruct {my_field1: 11, my_field2: 22}}
// gdb-command:continue

// === LLDB TESTS ==================================================================================

// lldb-command:run
// lldb-command:print test
// lldbg-check:(captured_fields_1::main::{closure#0}) $0 = { _ref__my_ref__my_field1 = 0x[...] }
// lldb-command:continue
// lldb-command:print test
// lldbg-check:(captured_fields_1::main::{closure#1}) $1 = { _ref__my_ref__my_field2 = 0x[...] }
// lldb-command:continue
// lldb-command:print test
// lldbg-check:(captured_fields_1::main::{closure#2}) $2 = { _ref__my_ref = 0x[...] }
// lldb-command:continue
// lldb-command:print test
// lldbg-check:(captured_fields_1::main::{closure#3}) $3 = { my_ref = 0x[...] }
// lldb-command:continue
// lldb-command:print test
// lldbg-check:(captured_fields_1::main::{closure#4}) $4 = { my_var__my_field2 = 22 }
// lldb-command:continue
// lldb-command:print test
// lldbg-check:(captured_fields_1::main::{closure#5}) $5 = { 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 = move || {
let a = my_var.my_field2;
};

_zzz(); // #break

let test = || {
let a = my_var;
};

_zzz(); // #break
}

fn _zzz() {}
Loading