Skip to content

Commit 2022bac

Browse files
debuginfo: Make sure that type names for closure and generator environments are unique in debuginfo.
Before this change, closure/generator environments coming from different instantiations of the same generic function were all assigned the same name even though they were distinct types with potentially different data layout. Now we append the generic arguments of the originating function to the type name. This commit also emits '{closure_env#0}' as the name of these types in order to disambiguate them from the accompanying closure function '{closure#0}'. Previously both were assigned the same name.
1 parent 0bcacb3 commit 2022bac

13 files changed

+286
-120
lines changed

compiler/rustc_codegen_llvm/src/debuginfo/mod.rs

+20-17
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use rustc_data_structures::sync::Lrc;
2525
use rustc_hir::def_id::{DefId, DefIdMap};
2626
use rustc_index::vec::IndexVec;
2727
use rustc_middle::mir;
28-
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
28+
use rustc_middle::ty::layout::LayoutOf;
2929
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
3030
use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TypeFoldable};
3131
use rustc_session::config::{self, DebugInfo};
@@ -307,9 +307,11 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
307307
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
308308
maybe_definition_llfn: Option<&'ll Value>,
309309
) -> &'ll DIScope {
310+
let tcx = self.tcx;
311+
310312
let def_id = instance.def_id();
311313
let containing_scope = get_containing_scope(self, instance);
312-
let span = self.tcx.def_span(def_id);
314+
let span = tcx.def_span(def_id);
313315
let loc = self.lookup_debug_loc(span.lo());
314316
let file_metadata = file_metadata(self, &loc.file);
315317

@@ -319,16 +321,24 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
319321
};
320322

321323
let mut name = String::new();
322-
type_names::push_item_name(self.tcx(), def_id, false, &mut name);
324+
type_names::push_item_name(tcx, def_id, false, &mut name);
323325

324326
// Find the enclosing function, in case this is a closure.
325-
let enclosing_fn_def_id = self.tcx().typeck_root_def_id(def_id);
327+
let enclosing_fn_def_id = tcx.typeck_root_def_id(def_id);
328+
329+
// We look up the generics of the enclosing function and truncate the substs
330+
// to their length in order to cut off extra stuff that might be in there for
331+
// closures or generators.
332+
let generics = tcx.generics_of(enclosing_fn_def_id);
333+
let substs = instance.substs.truncate_to(tcx, generics);
334+
335+
type_names::push_generic_params(
336+
tcx,
337+
tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substs),
338+
&mut name,
339+
);
326340

327-
// Get_template_parameters() will append a `<...>` clause to the function
328-
// name if necessary.
329-
let generics = self.tcx().generics_of(enclosing_fn_def_id);
330-
let substs = instance.substs.truncate_to(self.tcx(), generics);
331-
let template_parameters = get_template_parameters(self, generics, substs, &mut name);
341+
let template_parameters = get_template_parameters(self, generics, substs);
332342

333343
let linkage_name = &mangled_name_of_instance(self, instance).name;
334344
// Omit the linkage_name if it is the same as subprogram name.
@@ -350,7 +360,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
350360
if self.sess().opts.optimize != config::OptLevel::No {
351361
spflags |= DISPFlags::SPFlagOptimized;
352362
}
353-
if let Some((id, _)) = self.tcx.entry_fn(()) {
363+
if let Some((id, _)) = tcx.entry_fn(()) {
354364
if id == def_id {
355365
spflags |= DISPFlags::SPFlagMainSubprogram;
356366
}
@@ -429,14 +439,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
429439
cx: &CodegenCx<'ll, 'tcx>,
430440
generics: &ty::Generics,
431441
substs: SubstsRef<'tcx>,
432-
name_to_append_suffix_to: &mut String,
433442
) -> &'ll DIArray {
434-
type_names::push_generic_params(
435-
cx.tcx,
436-
cx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substs),
437-
name_to_append_suffix_to,
438-
);
439-
440443
if substs.types().next().is_none() {
441444
return create_DIArray(DIB(cx), &[]);
442445
}

compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs

+74-36
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313

1414
use rustc_data_structures::fx::FxHashSet;
1515
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
16-
use rustc_hir as hir;
1716
use rustc_hir::def_id::DefId;
1817
use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathData};
18+
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Mutability};
1919
use rustc_middle::ty::layout::IntegerExt;
2020
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
2121
use rustc_middle::ty::{self, AdtDef, ExistentialProjection, Ty, TyCtxt};
@@ -102,14 +102,14 @@ fn push_debuginfo_type_name<'tcx>(
102102
ty::RawPtr(ty::TypeAndMut { ty: inner_type, mutbl }) => {
103103
if cpp_like_debuginfo {
104104
match mutbl {
105-
hir::Mutability::Not => output.push_str("ptr_const$<"),
106-
hir::Mutability::Mut => output.push_str("ptr_mut$<"),
105+
Mutability::Not => output.push_str("ptr_const$<"),
106+
Mutability::Mut => output.push_str("ptr_mut$<"),
107107
}
108108
} else {
109109
output.push('*');
110110
match mutbl {
111-
hir::Mutability::Not => output.push_str("const "),
112-
hir::Mutability::Mut => output.push_str("mut "),
111+
Mutability::Not => output.push_str("const "),
112+
Mutability::Mut => output.push_str("mut "),
113113
}
114114
}
115115

@@ -131,8 +131,8 @@ fn push_debuginfo_type_name<'tcx>(
131131
output.push_str(mutbl.prefix_str());
132132
} else if !is_slice_or_str {
133133
match mutbl {
134-
hir::Mutability::Not => output.push_str("ref$<"),
135-
hir::Mutability::Mut => output.push_str("ref_mut$<"),
134+
Mutability::Not => output.push_str("ref$<"),
135+
Mutability::Mut => output.push_str("ref_mut$<"),
136136
}
137137
}
138138

@@ -345,14 +345,39 @@ fn push_debuginfo_type_name<'tcx>(
345345
// processing
346346
visited.remove(t);
347347
}
348-
ty::Closure(def_id, ..) | ty::Generator(def_id, ..) => {
349-
let key = tcx.def_key(def_id);
348+
ty::Closure(def_id, substs) | ty::Generator(def_id, substs, ..) => {
349+
// Name will be "{closure_env#0}<T1, T2, ...>", "{generator_env#0}<T1, T2, ...>", or
350+
// "{async_fn_env#0}<T1, T2, ...>", etc.
351+
let def_key = tcx.def_key(def_id);
352+
350353
if qualified {
351-
let parent_def_id = DefId { index: key.parent.unwrap(), ..def_id };
354+
let parent_def_id = DefId { index: def_key.parent.unwrap(), ..def_id };
352355
push_item_name(tcx, parent_def_id, true, output);
353356
output.push_str("::");
354357
}
355-
push_unqualified_item_name(tcx, def_id, key.disambiguated_data, output);
358+
359+
let mut label = String::with_capacity(20);
360+
write!(&mut label, "{}_env", generator_kind_label(tcx.generator_kind(def_id))).unwrap();
361+
362+
push_disambiguated_special_name(
363+
&label,
364+
def_key.disambiguated_data.disambiguator,
365+
cpp_like_debuginfo,
366+
output,
367+
);
368+
369+
// We also need to add the generic arguments of the async fn/generator or
370+
// the enclosing function (for closures or async blocks), so that we end
371+
// up with a unique name for every instantiation.
372+
373+
// Find the generics of the enclosing function, as defined in the source code.
374+
let enclosing_fn_def_id = tcx.typeck_root_def_id(def_id);
375+
let generics = tcx.generics_of(enclosing_fn_def_id);
376+
377+
// Truncate the substs to the length of the above generics. This will cut off
378+
// anything closure- or generator-specific.
379+
let substs = substs.truncate_to(tcx, generics);
380+
push_generic_params_internal(tcx, substs, output, visited);
356381
}
357382
// Type parameters from polymorphized functions.
358383
ty::Param(_) => {
@@ -509,6 +534,29 @@ pub fn push_item_name(tcx: TyCtxt<'_>, def_id: DefId, qualified: bool, output: &
509534
push_unqualified_item_name(tcx, def_id, def_key.disambiguated_data, output);
510535
}
511536

537+
fn generator_kind_label(generator_kind: Option<GeneratorKind>) -> &'static str {
538+
match generator_kind {
539+
Some(GeneratorKind::Async(AsyncGeneratorKind::Block)) => "async_block",
540+
Some(GeneratorKind::Async(AsyncGeneratorKind::Closure)) => "async_closure",
541+
Some(GeneratorKind::Async(AsyncGeneratorKind::Fn)) => "async_fn",
542+
Some(GeneratorKind::Gen) => "generator",
543+
None => "closure",
544+
}
545+
}
546+
547+
fn push_disambiguated_special_name(
548+
label: &str,
549+
disambiguator: u32,
550+
cpp_like_debuginfo: bool,
551+
output: &mut String,
552+
) {
553+
if cpp_like_debuginfo {
554+
write!(output, "{}${}", label, disambiguator).unwrap();
555+
} else {
556+
write!(output, "{{{}#{}}}", label, disambiguator).unwrap();
557+
}
558+
}
559+
512560
fn push_unqualified_item_name(
513561
tcx: TyCtxt<'_>,
514562
def_id: DefId,
@@ -519,42 +567,32 @@ fn push_unqualified_item_name(
519567
DefPathData::CrateRoot => {
520568
output.push_str(tcx.crate_name(def_id.krate).as_str());
521569
}
522-
DefPathData::ClosureExpr if tcx.generator_kind(def_id).is_some() => {
523-
let key = match tcx.generator_kind(def_id).unwrap() {
524-
hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) => "async_block",
525-
hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure) => "async_closure",
526-
hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn) => "async_fn",
527-
hir::GeneratorKind::Gen => "generator",
528-
};
529-
// Generators look like closures, but we want to treat them differently
530-
// in the debug info.
531-
if cpp_like_debuginfo(tcx) {
532-
write!(output, "{}${}", key, disambiguated_data.disambiguator).unwrap();
533-
} else {
534-
write!(output, "{{{}#{}}}", key, disambiguated_data.disambiguator).unwrap();
535-
}
570+
DefPathData::ClosureExpr => {
571+
let label = generator_kind_label(tcx.generator_kind(def_id));
572+
573+
push_disambiguated_special_name(
574+
label,
575+
disambiguated_data.disambiguator,
576+
cpp_like_debuginfo(tcx),
577+
output,
578+
);
536579
}
537580
_ => match disambiguated_data.data.name() {
538581
DefPathDataName::Named(name) => {
539582
output.push_str(name.as_str());
540583
}
541584
DefPathDataName::Anon { namespace } => {
542-
if cpp_like_debuginfo(tcx) {
543-
write!(output, "{}${}", namespace, disambiguated_data.disambiguator).unwrap();
544-
} else {
545-
write!(output, "{{{}#{}}}", namespace, disambiguated_data.disambiguator)
546-
.unwrap();
547-
}
585+
push_disambiguated_special_name(
586+
namespace.as_str(),
587+
disambiguated_data.disambiguator,
588+
cpp_like_debuginfo(tcx),
589+
output,
590+
);
548591
}
549592
},
550593
};
551594
}
552595

553-
// Pushes the generic parameters in the given `InternalSubsts` to the output string.
554-
// This ignores region parameters, since they can't reliably be
555-
// reconstructed for items from non-local crates. For local crates, this
556-
// would be possible but with inlining and LTO we have to use the least
557-
// common denominator - otherwise we would run into conflicts.
558596
fn push_generic_params_internal<'tcx>(
559597
tcx: TyCtxt<'tcx>,
560598
substs: SubstsRef<'tcx>,

src/test/codegen/async-fn-debug-msvc.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ async fn async_fn_test() {
1717
// FIXME: No way to reliably check the filename.
1818

1919
// CHECK-DAG: [[ASYNC_FN:!.*]] = !DINamespace(name: "async_fn_test"
20-
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "async_fn$0"
20+
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "async_fn_env$0"
2121
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]],
2222
// For brevity, we only check the struct name and members of the last variant.
2323
// CHECK-SAME: file: [[FILE:![0-9]*]], line: 11,

src/test/codegen/async-fn-debug.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ async fn async_fn_test() {
1717
// FIXME: No way to reliably check the filename.
1818

1919
// CHECK-DAG: [[ASYNC_FN:!.*]] = !DINamespace(name: "async_fn_test"
20-
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn#0}", scope: [[ASYNC_FN]]
20+
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: [[ASYNC_FN]]
2121
// CHECK: [[VARIANT:!.*]] = !DICompositeType(tag: DW_TAG_variant_part, scope: [[ASYNC_FN]],
2222
// CHECK-NOT: flags: DIFlagArtificial
2323
// CHECK-SAME: discriminator: [[DISC:![0-9]*]]

src/test/codegen/debug-vtable.rs

+39-14
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,36 @@
1-
// compile-flags: -Cdebuginfo=2 -Copt-level=0 -Ccodegen-units=1
2-
// ignore-tidy-linelength
3-
41
// This test checks the debuginfo for the expected 3 vtables is generated for correct names and number
52
// of entries.
63

7-
// NONMSVC-LABEL: !DIGlobalVariable(name: "<debug_vtable::Foo as debug_vtable::SomeTrait>::{vtable}"
8-
// MSVC-LABEL: !DIGlobalVariable(name: "impl$<debug_vtable::Foo, debug_vtable::SomeTrait>::vtable$"
4+
// Use the v0 symbol mangling scheme to codegen order independent of rustc version.
5+
// Unnamed items like shims are generated in lexicographical order of their symbol name and in the
6+
// legacy mangling scheme rustc version and generic parameters are both hashed into a single part
7+
// of the name, thus randomizing item order with respect to rustc version.
8+
9+
// compile-flags: -Cdebuginfo=2 -Copt-level=0 -Csymbol-mangling-version=v0
10+
// ignore-tidy-linelength
11+
12+
// NONMSVC: !DIGlobalVariable(name: "<debug_vtable::Foo as debug_vtable::SomeTrait>::{vtable}"
13+
// MSVC: !DIGlobalVariable(name: "impl$<debug_vtable::Foo, debug_vtable::SomeTrait>::vtable$"
914
// NONMSVC: !DIDerivedType(tag: DW_TAG_pointer_type, name: "*const ()",
1015
// MSVC: !DIDerivedType(tag: DW_TAG_pointer_type, name: "ptr_const$<tuple$<> >",
1116
// CHECK: !DISubrange(count: 5
1217

13-
// NONMSVC-LABEL: !DIGlobalVariable(name: "<debug_vtable::Foo as debug_vtable::SomeTraitWithGenerics<u64, i8>>::{vtable}"
14-
// MSVC-LABEL: !DIGlobalVariable(name: "impl$<debug_vtable::Foo, debug_vtable::SomeTraitWithGenerics<u64,i8> >::vtable$"
18+
// NONMSVC: !DIGlobalVariable(name: "<debug_vtable::Foo as debug_vtable::SomeTraitWithGenerics<u64, i8>>::{vtable}"
19+
// MSVC: !DIGlobalVariable(name: "impl$<debug_vtable::Foo, debug_vtable::SomeTraitWithGenerics<u64,i8> >::vtable$"
1520
// CHECK: !DISubrange(count: 4
1621

17-
// NONMSVC-LABEL: !DIGlobalVariable(name: "<debug_vtable::Foo as _>::{vtable}"
18-
// MSVC-LABEL: !DIGlobalVariable(name: "impl$<debug_vtable::Foo, _>::vtable$"
22+
// NONMSVC: !DIGlobalVariable(name: "<debug_vtable::Foo as _>::{vtable}"
23+
// MSVC: !DIGlobalVariable(name: "impl$<debug_vtable::Foo, _>::vtable$"
1924
// CHECK: !DISubrange(count: 3
2025

21-
// NONMSVC-LABEL: !DIGlobalVariable(name: "<debug_vtable::bar::{closure#0} as core::ops::function::FnOnce<(core::option::Option<&dyn core::ops::function::Fn<(), Output=()>>)>>::{vtable}"
22-
// MSVC-LABEL: !DIGlobalVariable(name: "impl$<debug_vtable::bar::closure$0, core::ops::function::FnOnce<tuple$<enum$<core::option::Option<ref$<dyn$<core::ops::function::Fn<tuple$<>,assoc$<Output,tuple$<> > > > > >, {{.*}}, {{.*}}, Some> > > >::vtable$"
26+
// NONMSVC: !DIGlobalVariable(name: "<debug_vtable::bar::{closure_env#0} as core::ops::function::FnOnce<(core::option::Option<&dyn core::ops::function::Fn<(), Output=()>>)>>::{vtable}"
27+
// MSVC: !DIGlobalVariable(name: "impl$<debug_vtable::bar::closure_env$0, core::ops::function::FnOnce<tuple$<enum$<core::option::Option<ref$<dyn$<core::ops::function::Fn<tuple$<>,assoc$<Output,tuple$<> > > > > >, {{.*}}, {{.*}}, Some> > > >::vtable$"
28+
29+
// NONMSVC: !DIGlobalVariable(name: "<debug_vtable::generic_closure::{closure_env#0}<bool> as core::ops::function::FnOnce<()>>::{vtable}"
30+
// MSVC: !DIGlobalVariable(name: "impl$<debug_vtable::generic_closure::closure_env$0<bool>, core::ops::function::FnOnce<tuple$<> > >::vtable$
31+
32+
// NONMSVC: !DIGlobalVariable(name: "<debug_vtable::generic_closure::{closure_env#0}<u32> as core::ops::function::FnOnce<()>>::{vtable}"
33+
// MSVC: !DIGlobalVariable(name: "impl$<debug_vtable::generic_closure::closure_env$0<u32>, core::ops::function::FnOnce<tuple$<> > >::vtable$
2334

2435
#![crate_type = "lib"]
2536

@@ -31,16 +42,22 @@ pub trait SomeTrait {
3142
}
3243

3344
impl SomeTrait for Foo {
34-
fn method1(&self) -> u32 { 1 }
35-
fn method2(&self) -> u32 { 2 }
45+
fn method1(&self) -> u32 {
46+
1
47+
}
48+
fn method2(&self) -> u32 {
49+
2
50+
}
3651
}
3752

3853
pub trait SomeTraitWithGenerics<T, U> {
3954
fn method1(&self) -> (T, U);
4055
}
4156

4257
impl SomeTraitWithGenerics<u64, i8> for Foo {
43-
fn method1(&self) -> (u64, i8) { (1, 2) }
58+
fn method1(&self) -> (u64, i8) {
59+
(1, 2)
60+
}
4461
}
4562

4663
pub fn foo(x: &Foo) -> (u32, (u64, i8), &dyn Send) {
@@ -55,3 +72,11 @@ pub fn foo(x: &Foo) -> (u32, (u64, i8), &dyn Send) {
5572
pub fn bar() -> Box<dyn FnOnce(Option<&dyn Fn()>)> {
5673
Box::new(|_x: Option<&dyn Fn()>| {})
5774
}
75+
76+
fn generic_closure<T: 'static>(x: T) -> Box<dyn FnOnce() -> T> {
77+
Box::new(move || x)
78+
}
79+
80+
pub fn instantiate_generic_closures() -> (Box<dyn FnOnce() -> u32>, Box<dyn FnOnce() -> bool>) {
81+
(generic_closure(1u32), generic_closure(false))
82+
}

0 commit comments

Comments
 (0)