Skip to content

Commit 014026d

Browse files
committed
Auto merge of rust-lang#87153 - michaelwoerister:debuginfo-names-dyn-trait-projection-bounds, r=wesleywiser
[debuginfo] Emit associated type bindings in trait object type names. This PR updates debuginfo type name generation for trait objects to include associated type bindings and auto trait bounds -- so that, for example, the debuginfo type name of `&dyn Iterator<Item=Foo>` and `&dyn Iterator<Item=Bar>` don't both map to just `&dyn Iterator` anymore. The following table shows examples of debuginfo type names before and after the PR: | type | before | after | |------|---------|-------| | `&dyn Iterator<Item=u32>>` | `&dyn Iterator` | `&dyn Iterator<Item=u32>` | | `&(dyn Iterator<Item=u32>> + Sync)` | `&dyn Iterator` | `&(dyn Iterator<Item=u32> + Sync)` | | `&(dyn SomeTrait<bool, i8, Bar=u32>> + Send)` | `&dyn SomeTrait<bool, i8>` | `&(dyn SomeTrait<bool, i8, Bar=u32>> + Send)` | For targets that need C++-like type names, we use `assoc$<Item,u32>` instead of `Item=u32`: | type | before | after | |------|---------|-------| | `&dyn Iterator<Item=u32>>` | `ref$<dyn$<Iterator> >` | `ref$<dyn$<Iterator<assoc$<Item,u32> > > >` | | `&(dyn Iterator<Item=u32>> + Sync)` | `ref$<dyn$<Iterator> >` | `ref$<dyn$<Iterator<assoc$<Item,u32> >,Sync> >` | | `&(dyn SomeTrait<bool, i8, Bar=u32>> + Send)` | `ref$<dyn$<SomeTrait<bool, i8> > >` | `ref$<dyn$<SomeTrait<bool,i8,assoc$<Bar,u32> > >,Send> >` | The PR also adds self-profiling measurements for debuginfo type name generation (re. rust-lang#86431). It looks like the compiler spends up to 0.5% of its time in that task, so the potential for optimizing it via caching seems limited. However, the perf run also shows [the biggest regression](https://perf.rust-lang.org/detailed-query.html?commit=585e91c718b0b2c5319e1fffd0ff1e62aaf7ccc2&base_commit=b9197978a90be6f7570741eabe2da175fec75375&benchmark=tokio-webpush-simple-debug&run_name=incr-unchanged) in a test case that does not even invoke the code in question. This suggests that the length of the names we generate here can affect performance by influencing how much data the linker has to copy around. Fixes rust-lang#86134.
2 parents d5af634 + 5b1bfae commit 014026d

10 files changed

+182
-108
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -3747,6 +3747,7 @@ dependencies = [
37473747
"rustc_span",
37483748
"rustc_symbol_mangling",
37493749
"rustc_target",
3750+
"smallvec",
37503751
"tempfile",
37513752
"tracing",
37523753
]

compiler/rustc_codegen_ssa/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ libc = "0.2.50"
1616
jobserver = "0.1.22"
1717
tempfile = "3.2"
1818
pathdiff = "0.2.0"
19+
smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
1920

2021
rustc_serialize = { path = "../rustc_serialize" }
2122
rustc_ast = { path = "../rustc_ast" }

compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs

+139-71
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathD
1919
use rustc_middle::ich::NodeIdHashingMode;
2020
use rustc_middle::ty::layout::IntegerExt;
2121
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
22-
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
22+
use rustc_middle::ty::{self, AdtDef, ExistentialProjection, Ty, TyCtxt};
2323
use rustc_target::abi::{Integer, TagEncoding, Variants};
24+
use smallvec::SmallVec;
2425

2526
use std::fmt::Write;
2627

@@ -33,6 +34,8 @@ pub fn compute_debuginfo_type_name<'tcx>(
3334
t: Ty<'tcx>,
3435
qualified: bool,
3536
) -> String {
37+
let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name");
38+
3639
let mut result = String::with_capacity(64);
3740
let mut visited = FxHashSet::default();
3841
push_debuginfo_type_name(tcx, t, qualified, &mut result, &mut visited);
@@ -41,7 +44,7 @@ pub fn compute_debuginfo_type_name<'tcx>(
4144

4245
// Pushes the name of the type as it should be stored in debuginfo on the
4346
// `output` String. See also compute_debuginfo_type_name().
44-
pub fn push_debuginfo_type_name<'tcx>(
47+
fn push_debuginfo_type_name<'tcx>(
4548
tcx: TyCtxt<'tcx>,
4649
t: Ty<'tcx>,
4750
qualified: bool,
@@ -84,25 +87,14 @@ pub fn push_debuginfo_type_name<'tcx>(
8487

8588
for component_type in component_types {
8689
push_debuginfo_type_name(tcx, component_type.expect_ty(), true, output, visited);
87-
output.push(',');
88-
89-
// Natvis does not always like having spaces between parts of the type name
90-
// and this causes issues when we need to write a typename in natvis, for example
91-
// as part of a cast like the `HashMap` visualizer does.
92-
if !cpp_like_names {
93-
output.push(' ');
94-
}
90+
push_arg_separator(cpp_like_names, output);
9591
}
9692
if !component_types.is_empty() {
97-
output.pop();
98-
99-
if !cpp_like_names {
100-
output.pop();
101-
}
93+
pop_arg_separator(output);
10294
}
10395

10496
if cpp_like_names {
105-
push_close_angle_bracket(tcx, output);
97+
push_close_angle_bracket(cpp_like_names, output);
10698
} else {
10799
output.push(')');
108100
}
@@ -124,7 +116,7 @@ pub fn push_debuginfo_type_name<'tcx>(
124116
push_debuginfo_type_name(tcx, inner_type, qualified, output, visited);
125117

126118
if cpp_like_names {
127-
push_close_angle_bracket(tcx, output);
119+
push_close_angle_bracket(cpp_like_names, output);
128120
}
129121
}
130122
ty::Ref(_, inner_type, mutbl) => {
@@ -150,7 +142,7 @@ pub fn push_debuginfo_type_name<'tcx>(
150142
push_debuginfo_type_name(tcx, inner_type, qualified, output, visited);
151143

152144
if cpp_like_names && !is_slice_or_str {
153-
push_close_angle_bracket(tcx, output);
145+
push_close_angle_bracket(cpp_like_names, output);
154146
}
155147
}
156148
ty::Array(inner_type, len) => {
@@ -182,69 +174,97 @@ pub fn push_debuginfo_type_name<'tcx>(
182174
push_debuginfo_type_name(tcx, inner_type, true, output, visited);
183175

184176
if cpp_like_names {
185-
push_close_angle_bracket(tcx, output);
177+
push_close_angle_bracket(cpp_like_names, output);
186178
} else {
187179
output.push(']');
188180
}
189181
}
190182
ty::Dynamic(ref trait_data, ..) => {
191-
if cpp_like_names {
183+
let auto_traits: SmallVec<[DefId; 4]> = trait_data.auto_traits().collect();
184+
185+
let has_enclosing_parens = if cpp_like_names {
192186
output.push_str("dyn$<");
187+
false
193188
} else {
194-
output.push_str("dyn ");
195-
}
189+
if trait_data.len() > 1 && auto_traits.len() != 0 {
190+
// We need enclosing parens because there is more than one trait
191+
output.push_str("(dyn ");
192+
true
193+
} else {
194+
output.push_str("dyn ");
195+
false
196+
}
197+
};
196198

197199
if let Some(principal) = trait_data.principal() {
198200
let principal =
199201
tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), principal);
200202
push_item_name(tcx, principal.def_id, qualified, output);
201-
push_generic_params_internal(tcx, principal.substs, output, visited);
202-
} else {
203-
// The auto traits come ordered by `DefPathHash`, which guarantees stability if the
204-
// environment is stable (e.g., incremental builds) but not otherwise (e.g.,
205-
// updated compiler version, different target).
206-
//
207-
// To avoid that causing instabilities in test output, sort the auto-traits
208-
// alphabetically.
209-
let mut auto_traits: Vec<_> = trait_data
210-
.iter()
211-
.filter_map(|predicate| {
212-
match tcx.normalize_erasing_late_bound_regions(
213-
ty::ParamEnv::reveal_all(),
214-
predicate,
215-
) {
216-
ty::ExistentialPredicate::AutoTrait(def_id) => {
217-
let mut name = String::new();
218-
push_item_name(tcx, def_id, true, &mut name);
219-
Some(name)
220-
}
221-
_ => None,
222-
}
203+
let principal_has_generic_params =
204+
push_generic_params_internal(tcx, principal.substs, output, visited);
205+
206+
let projection_bounds: SmallVec<[_; 4]> = trait_data
207+
.projection_bounds()
208+
.map(|bound| {
209+
let ExistentialProjection { item_def_id, ty, .. } = bound.skip_binder();
210+
(item_def_id, ty)
223211
})
224212
.collect();
225-
auto_traits.sort();
226213

227-
for name in auto_traits {
228-
output.push_str(&name);
214+
if projection_bounds.len() != 0 {
215+
if principal_has_generic_params {
216+
// push_generic_params_internal() above added a `>` but we actually
217+
// want to add more items to that list, so remove that again.
218+
pop_close_angle_bracket(output);
219+
}
229220

230-
if cpp_like_names {
231-
output.push_str(", ");
232-
} else {
233-
output.push_str(" + ");
221+
for (item_def_id, ty) in projection_bounds {
222+
push_arg_separator(cpp_like_names, output);
223+
224+
if cpp_like_names {
225+
output.push_str("assoc$<");
226+
push_item_name(tcx, item_def_id, false, output);
227+
push_arg_separator(cpp_like_names, output);
228+
push_debuginfo_type_name(tcx, ty, true, output, visited);
229+
push_close_angle_bracket(cpp_like_names, output);
230+
} else {
231+
push_item_name(tcx, item_def_id, false, output);
232+
output.push('=');
233+
push_debuginfo_type_name(tcx, ty, true, output, visited);
234+
}
234235
}
236+
237+
push_close_angle_bracket(cpp_like_names, output);
235238
}
236239

237-
// Remove the trailing joining characters. For cpp_like_names
238-
// this is `, ` otherwise ` + `.
239-
output.pop();
240-
output.pop();
241-
if !cpp_like_names {
242-
output.pop();
240+
if auto_traits.len() != 0 {
241+
push_auto_trait_separator(cpp_like_names, output);
243242
}
244243
}
245244

245+
if auto_traits.len() != 0 {
246+
let mut auto_traits: SmallVec<[String; 4]> = auto_traits
247+
.into_iter()
248+
.map(|def_id| {
249+
let mut name = String::with_capacity(20);
250+
push_item_name(tcx, def_id, true, &mut name);
251+
name
252+
})
253+
.collect();
254+
auto_traits.sort_unstable();
255+
256+
for auto_trait in auto_traits {
257+
output.push_str(&auto_trait);
258+
push_auto_trait_separator(cpp_like_names, output);
259+
}
260+
261+
pop_auto_trait_separator(output);
262+
}
263+
246264
if cpp_like_names {
247-
push_close_angle_bracket(tcx, output);
265+
push_close_angle_bracket(cpp_like_names, output);
266+
} else if has_enclosing_parens {
267+
output.push(')');
248268
}
249269
}
250270
ty::FnDef(..) | ty::FnPtr(_) => {
@@ -296,10 +316,9 @@ pub fn push_debuginfo_type_name<'tcx>(
296316
if !sig.inputs().is_empty() {
297317
for &parameter_type in sig.inputs() {
298318
push_debuginfo_type_name(tcx, parameter_type, true, output, visited);
299-
output.push_str(", ");
319+
push_arg_separator(cpp_like_names, output);
300320
}
301-
output.pop();
302-
output.pop();
321+
pop_arg_separator(output);
303322
}
304323

305324
if sig.c_variadic {
@@ -405,7 +424,25 @@ pub fn push_debuginfo_type_name<'tcx>(
405424
output.push_str(&format!(", {}", variant));
406425
}
407426
}
408-
push_close_angle_bracket(tcx, output);
427+
push_close_angle_bracket(true, output);
428+
}
429+
430+
const NON_CPP_AUTO_TRAIT_SEPARATOR: &str = " + ";
431+
432+
fn push_auto_trait_separator(cpp_like_names: bool, output: &mut String) {
433+
if cpp_like_names {
434+
push_arg_separator(cpp_like_names, output);
435+
} else {
436+
output.push_str(NON_CPP_AUTO_TRAIT_SEPARATOR);
437+
}
438+
}
439+
440+
fn pop_auto_trait_separator(output: &mut String) {
441+
if output.ends_with(NON_CPP_AUTO_TRAIT_SEPARATOR) {
442+
output.truncate(output.len() - NON_CPP_AUTO_TRAIT_SEPARATOR.len());
443+
} else {
444+
pop_arg_separator(output);
445+
}
409446
}
410447
}
411448

@@ -466,13 +503,15 @@ fn push_generic_params_internal<'tcx>(
466503
substs: SubstsRef<'tcx>,
467504
output: &mut String,
468505
visited: &mut FxHashSet<Ty<'tcx>>,
469-
) {
506+
) -> bool {
470507
if substs.non_erasable_generics().next().is_none() {
471-
return;
508+
return false;
472509
}
473510

474511
debug_assert_eq!(substs, tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substs));
475512

513+
let cpp_like_names = cpp_like_names(tcx);
514+
476515
output.push('<');
477516

478517
for type_parameter in substs.non_erasable_generics() {
@@ -486,13 +525,12 @@ fn push_generic_params_internal<'tcx>(
486525
other => bug!("Unexpected non-erasable generic: {:?}", other),
487526
}
488527

489-
output.push_str(", ");
528+
push_arg_separator(cpp_like_names, output);
490529
}
530+
pop_arg_separator(output);
531+
push_close_angle_bracket(cpp_like_names, output);
491532

492-
output.pop();
493-
output.pop();
494-
495-
push_close_angle_bracket(tcx, output);
533+
true
496534
}
497535

498536
fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: &'tcx ty::Const<'tcx>, output: &mut String) {
@@ -541,20 +579,50 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: &'tcx ty::Const<'tcx>, output:
541579
}
542580

543581
pub fn push_generic_params<'tcx>(tcx: TyCtxt<'tcx>, substs: SubstsRef<'tcx>, output: &mut String) {
582+
let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name");
544583
let mut visited = FxHashSet::default();
545584
push_generic_params_internal(tcx, substs, output, &mut visited);
546585
}
547586

548-
fn push_close_angle_bracket<'tcx>(tcx: TyCtxt<'tcx>, output: &mut String) {
587+
fn push_close_angle_bracket(cpp_like_names: bool, output: &mut String) {
549588
// MSVC debugger always treats `>>` as a shift, even when parsing templates,
550589
// so add a space to avoid confusion.
551-
if cpp_like_names(tcx) && output.ends_with('>') {
590+
if cpp_like_names && output.ends_with('>') {
552591
output.push(' ')
553592
};
554593

555594
output.push('>');
556595
}
557596

597+
fn pop_close_angle_bracket(output: &mut String) {
598+
assert!(output.ends_with('>'), "'output' does not end with '>': {}", output);
599+
output.pop();
600+
if output.ends_with(' ') {
601+
output.pop();
602+
}
603+
}
604+
605+
fn push_arg_separator(cpp_like_names: bool, output: &mut String) {
606+
// Natvis does not always like having spaces between parts of the type name
607+
// and this causes issues when we need to write a typename in natvis, for example
608+
// as part of a cast like the `HashMap` visualizer does.
609+
if cpp_like_names {
610+
output.push(',');
611+
} else {
612+
output.push_str(", ");
613+
};
614+
}
615+
616+
fn pop_arg_separator(output: &mut String) {
617+
if output.ends_with(' ') {
618+
output.pop();
619+
}
620+
621+
assert!(output.ends_with(','));
622+
623+
output.pop();
624+
}
625+
558626
fn cpp_like_names(tcx: TyCtxt<'_>) -> bool {
559627
tcx.sess.target.is_like_msvc
560628
}

src/test/debuginfo/function-names.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -52,21 +52,21 @@
5252
// cdb-command:x a!function_names::*::impl_function*
5353
// cdb-check:[...] a!function_names::Mod1::TestStruct2::impl_function (void)
5454
// cdb-check:[...] a!function_names::TestStruct1::impl_function (void)
55-
// cdb-check:[...] a!function_names::GenericStruct<i32, i32>::impl_function<i32, i32> (void)
55+
// cdb-check:[...] a!function_names::GenericStruct<i32,i32>::impl_function<i32,i32> (void)
5656

5757
// Trait implementations
5858
// cdb-command:x a!function_names::*::trait_function*
5959
// cdb-check:[...] a!function_names::impl$3::trait_function<i32> (void)
60+
// cdb-check:[...] a!function_names::impl$6::trait_function<i32,1> (void)
6061
// cdb-check:[...] a!function_names::impl$1::trait_function (void)
61-
// cdb-check:[...] a!function_names::impl$6::trait_function<i32, 1> (void)
6262
// cdb-check:[...] a!function_names::impl$5::trait_function3<function_names::TestStruct1> (void)
6363
// cdb-check:[...] a!function_names::Mod1::impl$1::trait_function (void)
6464

6565
// Closure
6666
// cdb-command:x a!function_names::*::closure*
67+
// cdb-check:[...] a!function_names::impl$2::impl_function::closure$0<i32,i32> (void)
6768
// cdb-check:[...] a!function_names::main::closure$0 (void)
6869
// cdb-check:[...] a!function_names::generic_func::closure$0<i32> (void)
69-
// cdb-check:[...] a!function_names::impl$2::impl_function::closure$0<i32, i32> (void)
7070

7171
// Generator
7272
// cdb-command:x a!function_names::*::generator*

0 commit comments

Comments
 (0)