]>
Commit | Line | Data |
---|---|---|
487cf647 | 1 | //! Type Names for Debug Info. |
d9579d0f | 2 | |
5e7ed085 | 3 | // Notes on targeting MSVC: |
136023e0 XL |
4 | // In general, MSVC's debugger attempts to parse all arguments as C++ expressions, |
5 | // even if the argument is explicitly a symbol name. | |
6 | // As such, there are many things that cause parsing issues: | |
7 | // * `#` is treated as a special character for macros. | |
8 | // * `{` or `<` at the beginning of a name is treated as an operator. | |
9 | // * `>>` is always treated as a right-shift. | |
10 | // * `[` in a name is treated like a regex bracket expression (match any char | |
11 | // within the brackets). | |
12 | // * `"` is treated as the start of a string. | |
13 | ||
532ac7d7 | 14 | use rustc_data_structures::fx::FxHashSet; |
136023e0 | 15 | use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; |
dfeec247 | 16 | use rustc_hir::def_id::DefId; |
136023e0 | 17 | use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathData}; |
5099ac24 | 18 | use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Mutability}; |
5e7ed085 | 19 | use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; |
136023e0 | 20 | use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; |
f2b60f7d FG |
21 | use rustc_middle::ty::{self, ExistentialProjection, ParamEnv, Ty, TyCtxt}; |
22 | use rustc_target::abi::Integer; | |
136023e0 | 23 | use smallvec::SmallVec; |
d9579d0f | 24 | |
1b1a35ee XL |
25 | use std::fmt::Write; |
26 | ||
5e7ed085 FG |
27 | use crate::debuginfo::wants_c_like_enum_debuginfo; |
28 | ||
487cf647 FG |
29 | /// Compute the name of the type as it should be stored in debuginfo. Does not do |
30 | /// any caching, i.e., calling the function twice with the same type will also do | |
31 | /// the work twice. The `qualified` parameter only affects the first level of the | |
32 | /// type name, further levels (i.e., type parameters) are always fully qualified. | |
dc9dc135 XL |
33 | pub fn compute_debuginfo_type_name<'tcx>( |
34 | tcx: TyCtxt<'tcx>, | |
35 | t: Ty<'tcx>, | |
36 | qualified: bool, | |
37 | ) -> String { | |
136023e0 XL |
38 | let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name"); |
39 | ||
d9579d0f | 40 | let mut result = String::with_capacity(64); |
532ac7d7 | 41 | let mut visited = FxHashSet::default(); |
48663c56 | 42 | push_debuginfo_type_name(tcx, t, qualified, &mut result, &mut visited); |
d9579d0f AL |
43 | result |
44 | } | |
45 | ||
46 | // Pushes the name of the type as it should be stored in debuginfo on the | |
47 | // `output` String. See also compute_debuginfo_type_name(). | |
136023e0 | 48 | fn push_debuginfo_type_name<'tcx>( |
dc9dc135 XL |
49 | tcx: TyCtxt<'tcx>, |
50 | t: Ty<'tcx>, | |
51 | qualified: bool, | |
52 | output: &mut String, | |
53 | visited: &mut FxHashSet<Ty<'tcx>>, | |
54 | ) { | |
3b2f2976 XL |
55 | // When targeting MSVC, emit C++ style type names for compatibility with |
56 | // .natvis visualizers (and perhaps other existing native debuggers?) | |
a2a8927a | 57 | let cpp_like_debuginfo = cpp_like_debuginfo(tcx); |
3b2f2976 | 58 | |
1b1a35ee | 59 | match *t.kind() { |
b7449926 XL |
60 | ty::Bool => output.push_str("bool"), |
61 | ty::Char => output.push_str("char"), | |
487cf647 FG |
62 | ty::Str => { |
63 | if cpp_like_debuginfo { | |
64 | output.push_str("str$") | |
65 | } else { | |
66 | output.push_str("str") | |
67 | } | |
68 | } | |
136023e0 | 69 | ty::Never => { |
a2a8927a | 70 | if cpp_like_debuginfo { |
136023e0 XL |
71 | output.push_str("never$"); |
72 | } else { | |
73 | output.push('!'); | |
74 | } | |
75 | } | |
60c5eb7d XL |
76 | ty::Int(int_ty) => output.push_str(int_ty.name_str()), |
77 | ty::Uint(uint_ty) => output.push_str(uint_ty.name_str()), | |
78 | ty::Float(float_ty) => output.push_str(float_ty.name_str()), | |
48663c56 | 79 | ty::Foreign(def_id) => push_item_name(tcx, def_id, qualified, output), |
b7449926 | 80 | ty::Adt(def, substs) => { |
5e7ed085 FG |
81 | // `layout_for_cpp_like_fallback` will be `Some` if we want to use the fallback encoding. |
82 | let layout_for_cpp_like_fallback = if cpp_like_debuginfo && def.is_enum() { | |
83 | match tcx.layout_of(ParamEnv::reveal_all().and(t)) { | |
84 | Ok(layout) => { | |
85 | if !wants_c_like_enum_debuginfo(layout) { | |
86 | Some(layout) | |
87 | } else { | |
88 | // This is a C-like enum so we don't want to use the fallback encoding | |
89 | // for the name. | |
90 | None | |
91 | } | |
92 | } | |
93 | Err(e) => { | |
94 | // Computing the layout can still fail here, e.g. if the target architecture | |
95 | // cannot represent the type. See https://github.com/rust-lang/rust/issues/94961. | |
9c376795 | 96 | // FIXME: migrate once `rustc_middle::mir::interpret::InterpError` is translatable. |
5e7ed085 FG |
97 | tcx.sess.fatal(&format!("{}", e)); |
98 | } | |
99 | } | |
17df50a5 | 100 | } else { |
5e7ed085 FG |
101 | // We are not emitting cpp-like debuginfo or this isn't even an enum. |
102 | None | |
103 | }; | |
104 | ||
105 | if let Some(ty_and_layout) = layout_for_cpp_like_fallback { | |
106 | msvc_enum_fallback( | |
5e7ed085 FG |
107 | ty_and_layout, |
108 | &|output, visited| { | |
109 | push_item_name(tcx, def.did(), true, output); | |
110 | push_generic_params_internal(tcx, substs, output, visited); | |
111 | }, | |
112 | output, | |
113 | visited, | |
114 | ); | |
115 | } else { | |
116 | push_item_name(tcx, def.did(), qualified, output); | |
136023e0 | 117 | push_generic_params_internal(tcx, substs, output, visited); |
17df50a5 | 118 | } |
dfeec247 | 119 | } |
b7449926 | 120 | ty::Tuple(component_types) => { |
a2a8927a | 121 | if cpp_like_debuginfo { |
136023e0 | 122 | output.push_str("tuple$<"); |
f035d41b XL |
123 | } else { |
124 | output.push('('); | |
125 | } | |
126 | ||
f9f354fc | 127 | for component_type in component_types { |
5e7ed085 | 128 | push_debuginfo_type_name(tcx, component_type, true, output, visited); |
a2a8927a | 129 | push_arg_separator(cpp_like_debuginfo, output); |
d9579d0f AL |
130 | } |
131 | if !component_types.is_empty() { | |
136023e0 | 132 | pop_arg_separator(output); |
d9579d0f | 133 | } |
f035d41b | 134 | |
a2a8927a XL |
135 | if cpp_like_debuginfo { |
136 | push_close_angle_bracket(cpp_like_debuginfo, output); | |
f035d41b XL |
137 | } else { |
138 | output.push(')'); | |
139 | } | |
dfeec247 XL |
140 | } |
141 | ty::RawPtr(ty::TypeAndMut { ty: inner_type, mutbl }) => { | |
a2a8927a | 142 | if cpp_like_debuginfo { |
136023e0 | 143 | match mutbl { |
5099ac24 FG |
144 | Mutability::Not => output.push_str("ptr_const$<"), |
145 | Mutability::Mut => output.push_str("ptr_mut$<"), | |
136023e0 XL |
146 | } |
147 | } else { | |
3b2f2976 | 148 | output.push('*'); |
136023e0 | 149 | match mutbl { |
5099ac24 FG |
150 | Mutability::Not => output.push_str("const "), |
151 | Mutability::Mut => output.push_str("mut "), | |
136023e0 | 152 | } |
d9579d0f AL |
153 | } |
154 | ||
136023e0 | 155 | push_debuginfo_type_name(tcx, inner_type, qualified, output, visited); |
3b2f2976 | 156 | |
a2a8927a XL |
157 | if cpp_like_debuginfo { |
158 | push_close_angle_bracket(cpp_like_debuginfo, output); | |
3b2f2976 | 159 | } |
dfeec247 | 160 | } |
b7449926 | 161 | ty::Ref(_, inner_type, mutbl) => { |
487cf647 | 162 | if cpp_like_debuginfo { |
136023e0 | 163 | match mutbl { |
5099ac24 FG |
164 | Mutability::Not => output.push_str("ref$<"), |
165 | Mutability::Mut => output.push_str("ref_mut$<"), | |
136023e0 | 166 | } |
487cf647 FG |
167 | } else { |
168 | output.push('&'); | |
169 | output.push_str(mutbl.prefix_str()); | |
3b2f2976 | 170 | } |
d9579d0f | 171 | |
136023e0 | 172 | push_debuginfo_type_name(tcx, inner_type, qualified, output, visited); |
3b2f2976 | 173 | |
487cf647 | 174 | if cpp_like_debuginfo { |
a2a8927a | 175 | push_close_angle_bracket(cpp_like_debuginfo, output); |
3b2f2976 | 176 | } |
dfeec247 | 177 | } |
b7449926 | 178 | ty::Array(inner_type, len) => { |
a2a8927a | 179 | if cpp_like_debuginfo { |
136023e0 XL |
180 | output.push_str("array$<"); |
181 | push_debuginfo_type_name(tcx, inner_type, true, output, visited); | |
923072b8 | 182 | match len.kind() { |
136023e0 | 183 | ty::ConstKind::Param(param) => write!(output, ",{}>", param.name).unwrap(), |
9ffffee4 FG |
184 | _ => write!( |
185 | output, | |
186 | ",{}>", | |
187 | len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()) | |
188 | ) | |
189 | .unwrap(), | |
136023e0 XL |
190 | } |
191 | } else { | |
192 | output.push('['); | |
193 | push_debuginfo_type_name(tcx, inner_type, true, output, visited); | |
923072b8 | 194 | match len.kind() { |
136023e0 | 195 | ty::ConstKind::Param(param) => write!(output, "; {}]", param.name).unwrap(), |
9ffffee4 FG |
196 | _ => write!( |
197 | output, | |
198 | "; {}]", | |
199 | len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()) | |
200 | ) | |
201 | .unwrap(), | |
136023e0 XL |
202 | } |
203 | } | |
dfeec247 | 204 | } |
b7449926 | 205 | ty::Slice(inner_type) => { |
a2a8927a | 206 | if cpp_like_debuginfo { |
487cf647 | 207 | output.push_str("slice2$<"); |
3b2f2976 XL |
208 | } else { |
209 | output.push('['); | |
210 | } | |
211 | ||
48663c56 | 212 | push_debuginfo_type_name(tcx, inner_type, true, output, visited); |
3b2f2976 | 213 | |
a2a8927a XL |
214 | if cpp_like_debuginfo { |
215 | push_close_angle_bracket(cpp_like_debuginfo, output); | |
3b2f2976 XL |
216 | } else { |
217 | output.push(']'); | |
218 | } | |
dfeec247 | 219 | } |
b7449926 | 220 | ty::Dynamic(ref trait_data, ..) => { |
136023e0 XL |
221 | let auto_traits: SmallVec<[DefId; 4]> = trait_data.auto_traits().collect(); |
222 | ||
a2a8927a | 223 | let has_enclosing_parens = if cpp_like_debuginfo { |
136023e0 XL |
224 | output.push_str("dyn$<"); |
225 | false | |
226 | } else { | |
227 | if trait_data.len() > 1 && auto_traits.len() != 0 { | |
228 | // We need enclosing parens because there is more than one trait | |
229 | output.push_str("(dyn "); | |
230 | true | |
231 | } else { | |
232 | output.push_str("dyn "); | |
233 | false | |
234 | } | |
235 | }; | |
236 | ||
0731742a | 237 | if let Some(principal) = trait_data.principal() { |
fc512014 XL |
238 | let principal = |
239 | tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), principal); | |
136023e0 XL |
240 | push_item_name(tcx, principal.def_id, qualified, output); |
241 | let principal_has_generic_params = | |
242 | push_generic_params_internal(tcx, principal.substs, output, visited); | |
243 | ||
244 | let projection_bounds: SmallVec<[_; 4]> = trait_data | |
245 | .projection_bounds() | |
246 | .map(|bound| { | |
9c376795 | 247 | let ExistentialProjection { def_id: item_def_id, term, .. } = |
5e7ed085 | 248 | tcx.erase_late_bound_regions(bound); |
5099ac24 FG |
249 | // FIXME(associated_const_equality): allow for consts here |
250 | (item_def_id, term.ty().unwrap()) | |
136023e0 XL |
251 | }) |
252 | .collect(); | |
253 | ||
254 | if projection_bounds.len() != 0 { | |
255 | if principal_has_generic_params { | |
256 | // push_generic_params_internal() above added a `>` but we actually | |
5e7ed085 | 257 | // want to add more items to that list, so remove that again... |
136023e0 | 258 | pop_close_angle_bracket(output); |
5e7ed085 FG |
259 | // .. and add a comma to separate the regular generic args from the |
260 | // associated types. | |
261 | push_arg_separator(cpp_like_debuginfo, output); | |
262 | } else { | |
263 | // push_generic_params_internal() did not add `<...>`, so we open | |
264 | // angle brackets here. | |
265 | output.push('<'); | |
136023e0 XL |
266 | } |
267 | ||
268 | for (item_def_id, ty) in projection_bounds { | |
a2a8927a | 269 | if cpp_like_debuginfo { |
136023e0 XL |
270 | output.push_str("assoc$<"); |
271 | push_item_name(tcx, item_def_id, false, output); | |
a2a8927a | 272 | push_arg_separator(cpp_like_debuginfo, output); |
136023e0 | 273 | push_debuginfo_type_name(tcx, ty, true, output, visited); |
a2a8927a | 274 | push_close_angle_bracket(cpp_like_debuginfo, output); |
136023e0 XL |
275 | } else { |
276 | push_item_name(tcx, item_def_id, false, output); | |
277 | output.push('='); | |
278 | push_debuginfo_type_name(tcx, ty, true, output, visited); | |
279 | } | |
5e7ed085 | 280 | push_arg_separator(cpp_like_debuginfo, output); |
136023e0 XL |
281 | } |
282 | ||
5e7ed085 | 283 | pop_arg_separator(output); |
a2a8927a | 284 | push_close_angle_bracket(cpp_like_debuginfo, output); |
136023e0 XL |
285 | } |
286 | ||
287 | if auto_traits.len() != 0 { | |
a2a8927a | 288 | push_auto_trait_separator(cpp_like_debuginfo, output); |
136023e0 XL |
289 | } |
290 | } | |
291 | ||
292 | if auto_traits.len() != 0 { | |
293 | let mut auto_traits: SmallVec<[String; 4]> = auto_traits | |
294 | .into_iter() | |
295 | .map(|def_id| { | |
296 | let mut name = String::with_capacity(20); | |
297 | push_item_name(tcx, def_id, true, &mut name); | |
298 | name | |
299 | }) | |
300 | .collect(); | |
301 | auto_traits.sort_unstable(); | |
302 | ||
303 | for auto_trait in auto_traits { | |
304 | output.push_str(&auto_trait); | |
a2a8927a | 305 | push_auto_trait_separator(cpp_like_debuginfo, output); |
136023e0 XL |
306 | } |
307 | ||
308 | pop_auto_trait_separator(output); | |
309 | } | |
310 | ||
a2a8927a XL |
311 | if cpp_like_debuginfo { |
312 | push_close_angle_bracket(cpp_like_debuginfo, output); | |
136023e0 XL |
313 | } else if has_enclosing_parens { |
314 | output.push(')'); | |
0731742a | 315 | } |
dfeec247 | 316 | } |
b7449926 | 317 | ty::FnDef(..) | ty::FnPtr(_) => { |
532ac7d7 XL |
318 | // We've encountered a weird 'recursive type' |
319 | // Currently, the only way to generate such a type | |
320 | // is by using 'impl trait': | |
321 | // | |
322 | // fn foo() -> impl Copy { foo } | |
323 | // | |
324 | // There's not really a sensible name we can generate, | |
325 | // since we don't include 'impl trait' types (e.g. ty::Opaque) | |
326 | // in the output | |
327 | // | |
328 | // Since we need to generate *something*, we just | |
329 | // use a dummy string that should make it clear | |
330 | // that something unusual is going on | |
331 | if !visited.insert(t) { | |
a2a8927a | 332 | output.push_str(if cpp_like_debuginfo { |
136023e0 XL |
333 | "recursive_type$" |
334 | } else { | |
335 | "<recursive_type>" | |
336 | }); | |
532ac7d7 XL |
337 | return; |
338 | } | |
339 | ||
136023e0 XL |
340 | let sig = |
341 | tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), t.fn_sig(tcx)); | |
d9579d0f | 342 | |
a2a8927a | 343 | if cpp_like_debuginfo { |
136023e0 XL |
344 | // Format as a C++ function pointer: return_type (*)(params...) |
345 | if sig.output().is_unit() { | |
346 | output.push_str("void"); | |
347 | } else { | |
348 | push_debuginfo_type_name(tcx, sig.output(), true, output, visited); | |
349 | } | |
350 | output.push_str(" (*)("); | |
351 | } else { | |
352 | output.push_str(sig.unsafety.prefix_str()); | |
353 | ||
354 | if sig.abi != rustc_target::spec::abi::Abi::Rust { | |
355 | output.push_str("extern \""); | |
356 | output.push_str(sig.abi.name()); | |
357 | output.push_str("\" "); | |
358 | } | |
d9579d0f | 359 | |
136023e0 XL |
360 | output.push_str("fn("); |
361 | } | |
d9579d0f | 362 | |
476ff2be SL |
363 | if !sig.inputs().is_empty() { |
364 | for ¶meter_type in sig.inputs() { | |
48663c56 | 365 | push_debuginfo_type_name(tcx, parameter_type, true, output, visited); |
a2a8927a | 366 | push_arg_separator(cpp_like_debuginfo, output); |
d9579d0f | 367 | } |
136023e0 | 368 | pop_arg_separator(output); |
d9579d0f AL |
369 | } |
370 | ||
532ac7d7 | 371 | if sig.c_variadic { |
476ff2be | 372 | if !sig.inputs().is_empty() { |
d9579d0f AL |
373 | output.push_str(", ..."); |
374 | } else { | |
375 | output.push_str("..."); | |
376 | } | |
377 | } | |
378 | ||
379 | output.push(')'); | |
380 | ||
a2a8927a | 381 | if !cpp_like_debuginfo && !sig.output().is_unit() { |
5bcae85e | 382 | output.push_str(" -> "); |
48663c56 | 383 | push_debuginfo_type_name(tcx, sig.output(), true, output, visited); |
d9579d0f | 384 | } |
532ac7d7 | 385 | |
532ac7d7 XL |
386 | // We only keep the type in 'visited' |
387 | // for the duration of the body of this method. | |
388 | // It's fine for a particular function type | |
389 | // to show up multiple times in one overall type | |
390 | // (e.g. MyType<fn() -> u8, fn() -> u8> | |
391 | // | |
392 | // We only care about avoiding recursing | |
393 | // directly back to the type we're currently | |
394 | // processing | |
5099ac24 | 395 | visited.remove(&t); |
dfeec247 | 396 | } |
5099ac24 FG |
397 | ty::Closure(def_id, substs) | ty::Generator(def_id, substs, ..) => { |
398 | // Name will be "{closure_env#0}<T1, T2, ...>", "{generator_env#0}<T1, T2, ...>", or | |
399 | // "{async_fn_env#0}<T1, T2, ...>", etc. | |
5e7ed085 | 400 | // In the case of cpp-like debuginfo, the name additionally gets wrapped inside of |
f2b60f7d | 401 | // an artificial `enum2$<>` type, as defined in msvc_enum_fallback(). |
5e7ed085 FG |
402 | if cpp_like_debuginfo && t.is_generator() { |
403 | let ty_and_layout = tcx.layout_of(ParamEnv::reveal_all().and(t)).unwrap(); | |
404 | msvc_enum_fallback( | |
5e7ed085 FG |
405 | ty_and_layout, |
406 | &|output, visited| { | |
407 | push_closure_or_generator_name(tcx, def_id, substs, true, output, visited); | |
408 | }, | |
409 | output, | |
410 | visited, | |
411 | ); | |
412 | } else { | |
413 | push_closure_or_generator_name(tcx, def_id, substs, qualified, output, visited); | |
136023e0 | 414 | } |
ea8adc8c | 415 | } |
3dfed10e XL |
416 | // Type parameters from polymorphized functions. |
417 | ty::Param(_) => { | |
5e7ed085 | 418 | write!(output, "{:?}", t).unwrap(); |
3dfed10e | 419 | } |
f035d41b | 420 | ty::Error(_) |
dfeec247 XL |
421 | | ty::Infer(_) |
422 | | ty::Placeholder(..) | |
9c376795 | 423 | | ty::Alias(..) |
dfeec247 | 424 | | ty::Bound(..) |
9ffffee4 | 425 | | ty::GeneratorWitnessMIR(..) |
3dfed10e | 426 | | ty::GeneratorWitness(..) => { |
dfeec247 XL |
427 | bug!( |
428 | "debuginfo: Trying to create type name for \ | |
429 | unexpected type: {:?}", | |
430 | t | |
431 | ); | |
d9579d0f AL |
432 | } |
433 | } | |
434 | ||
17df50a5 XL |
435 | /// MSVC names enums differently than other platforms so that the debugging visualization |
436 | // format (natvis) is able to understand enums and render the active variant correctly in the | |
f2b60f7d FG |
437 | // debugger. For more information, look in |
438 | // rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs. | |
a2a8927a | 439 | fn msvc_enum_fallback<'tcx>( |
5e7ed085 FG |
440 | ty_and_layout: TyAndLayout<'tcx>, |
441 | push_inner: &dyn Fn(/*output*/ &mut String, /*visited*/ &mut FxHashSet<Ty<'tcx>>), | |
17df50a5 XL |
442 | output: &mut String, |
443 | visited: &mut FxHashSet<Ty<'tcx>>, | |
444 | ) { | |
5e7ed085 | 445 | debug_assert!(!wants_c_like_enum_debuginfo(ty_and_layout)); |
f2b60f7d | 446 | output.push_str("enum2$<"); |
5e7ed085 | 447 | push_inner(output, visited); |
136023e0 XL |
448 | push_close_angle_bracket(true, output); |
449 | } | |
450 | ||
451 | const NON_CPP_AUTO_TRAIT_SEPARATOR: &str = " + "; | |
452 | ||
a2a8927a XL |
453 | fn push_auto_trait_separator(cpp_like_debuginfo: bool, output: &mut String) { |
454 | if cpp_like_debuginfo { | |
455 | push_arg_separator(cpp_like_debuginfo, output); | |
17df50a5 | 456 | } else { |
136023e0 | 457 | output.push_str(NON_CPP_AUTO_TRAIT_SEPARATOR); |
17df50a5 XL |
458 | } |
459 | } | |
460 | ||
136023e0 XL |
461 | fn pop_auto_trait_separator(output: &mut String) { |
462 | if output.ends_with(NON_CPP_AUTO_TRAIT_SEPARATOR) { | |
463 | output.truncate(output.len() - NON_CPP_AUTO_TRAIT_SEPARATOR.len()); | |
464 | } else { | |
465 | pop_arg_separator(output); | |
466 | } | |
467 | } | |
468 | } | |
469 | ||
5099ac24 FG |
470 | pub enum VTableNameKind { |
471 | // Is the name for the const/static holding the vtable? | |
472 | GlobalVariable, | |
473 | // Is the name for the type of the vtable? | |
474 | Type, | |
475 | } | |
476 | ||
477 | /// Computes a name for the global variable storing a vtable (or the type of that global variable). | |
c295e0f8 XL |
478 | /// |
479 | /// The name is of the form: | |
480 | /// | |
481 | /// `<path::to::SomeType as path::to::SomeTrait>::{vtable}` | |
482 | /// | |
483 | /// or, when generating C++-like names: | |
484 | /// | |
485 | /// `impl$<path::to::SomeType, path::to::SomeTrait>::vtable$` | |
5099ac24 FG |
486 | /// |
487 | /// If `kind` is `VTableNameKind::Type` then the last component is `{vtable_ty}` instead of just | |
488 | /// `{vtable}`, so that the type and the corresponding global variable get assigned different | |
489 | /// names. | |
c295e0f8 XL |
490 | pub fn compute_debuginfo_vtable_name<'tcx>( |
491 | tcx: TyCtxt<'tcx>, | |
492 | t: Ty<'tcx>, | |
493 | trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>, | |
5099ac24 | 494 | kind: VTableNameKind, |
c295e0f8 | 495 | ) -> String { |
a2a8927a | 496 | let cpp_like_debuginfo = cpp_like_debuginfo(tcx); |
c295e0f8 XL |
497 | |
498 | let mut vtable_name = String::with_capacity(64); | |
499 | ||
a2a8927a | 500 | if cpp_like_debuginfo { |
c295e0f8 XL |
501 | vtable_name.push_str("impl$<"); |
502 | } else { | |
503 | vtable_name.push('<'); | |
504 | } | |
505 | ||
506 | let mut visited = FxHashSet::default(); | |
507 | push_debuginfo_type_name(tcx, t, true, &mut vtable_name, &mut visited); | |
508 | ||
a2a8927a | 509 | if cpp_like_debuginfo { |
c295e0f8 XL |
510 | vtable_name.push_str(", "); |
511 | } else { | |
512 | vtable_name.push_str(" as "); | |
513 | } | |
514 | ||
515 | if let Some(trait_ref) = trait_ref { | |
516 | let trait_ref = | |
517 | tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), trait_ref); | |
518 | push_item_name(tcx, trait_ref.def_id, true, &mut vtable_name); | |
519 | visited.clear(); | |
520 | push_generic_params_internal(tcx, trait_ref.substs, &mut vtable_name, &mut visited); | |
521 | } else { | |
9c376795 | 522 | vtable_name.push('_'); |
c295e0f8 XL |
523 | } |
524 | ||
a2a8927a | 525 | push_close_angle_bracket(cpp_like_debuginfo, &mut vtable_name); |
c295e0f8 | 526 | |
5099ac24 FG |
527 | let suffix = match (cpp_like_debuginfo, kind) { |
528 | (true, VTableNameKind::GlobalVariable) => "::vtable$", | |
529 | (false, VTableNameKind::GlobalVariable) => "::{vtable}", | |
530 | (true, VTableNameKind::Type) => "::vtable_type$", | |
531 | (false, VTableNameKind::Type) => "::{vtable_type}", | |
532 | }; | |
c295e0f8 XL |
533 | |
534 | vtable_name.reserve_exact(suffix.len()); | |
535 | vtable_name.push_str(suffix); | |
536 | ||
537 | vtable_name | |
538 | } | |
539 | ||
a2a8927a | 540 | pub fn push_item_name(tcx: TyCtxt<'_>, def_id: DefId, qualified: bool, output: &mut String) { |
136023e0 XL |
541 | let def_key = tcx.def_key(def_id); |
542 | if qualified { | |
543 | if let Some(parent) = def_key.parent { | |
544 | push_item_name(tcx, DefId { krate: def_id.krate, index: parent }, true, output); | |
545 | output.push_str("::"); | |
546 | } | |
547 | } | |
548 | ||
549 | push_unqualified_item_name(tcx, def_id, def_key.disambiguated_data, output); | |
550 | } | |
551 | ||
5099ac24 FG |
552 | fn generator_kind_label(generator_kind: Option<GeneratorKind>) -> &'static str { |
553 | match generator_kind { | |
554 | Some(GeneratorKind::Async(AsyncGeneratorKind::Block)) => "async_block", | |
555 | Some(GeneratorKind::Async(AsyncGeneratorKind::Closure)) => "async_closure", | |
556 | Some(GeneratorKind::Async(AsyncGeneratorKind::Fn)) => "async_fn", | |
557 | Some(GeneratorKind::Gen) => "generator", | |
558 | None => "closure", | |
559 | } | |
560 | } | |
561 | ||
562 | fn push_disambiguated_special_name( | |
563 | label: &str, | |
564 | disambiguator: u32, | |
565 | cpp_like_debuginfo: bool, | |
566 | output: &mut String, | |
567 | ) { | |
568 | if cpp_like_debuginfo { | |
569 | write!(output, "{}${}", label, disambiguator).unwrap(); | |
570 | } else { | |
571 | write!(output, "{{{}#{}}}", label, disambiguator).unwrap(); | |
572 | } | |
573 | } | |
574 | ||
136023e0 | 575 | fn push_unqualified_item_name( |
a2a8927a | 576 | tcx: TyCtxt<'_>, |
136023e0 XL |
577 | def_id: DefId, |
578 | disambiguated_data: DisambiguatedDefPathData, | |
579 | output: &mut String, | |
580 | ) { | |
581 | match disambiguated_data.data { | |
582 | DefPathData::CrateRoot => { | |
a2a8927a | 583 | output.push_str(tcx.crate_name(def_id.krate).as_str()); |
136023e0 | 584 | } |
5099ac24 FG |
585 | DefPathData::ClosureExpr => { |
586 | let label = generator_kind_label(tcx.generator_kind(def_id)); | |
587 | ||
588 | push_disambiguated_special_name( | |
589 | label, | |
590 | disambiguated_data.disambiguator, | |
591 | cpp_like_debuginfo(tcx), | |
592 | output, | |
593 | ); | |
54a0048b | 594 | } |
136023e0 XL |
595 | _ => match disambiguated_data.data.name() { |
596 | DefPathDataName::Named(name) => { | |
a2a8927a | 597 | output.push_str(name.as_str()); |
136023e0 XL |
598 | } |
599 | DefPathDataName::Anon { namespace } => { | |
5099ac24 FG |
600 | push_disambiguated_special_name( |
601 | namespace.as_str(), | |
602 | disambiguated_data.disambiguator, | |
603 | cpp_like_debuginfo(tcx), | |
604 | output, | |
605 | ); | |
136023e0 XL |
606 | } |
607 | }, | |
608 | }; | |
609 | } | |
610 | ||
136023e0 XL |
611 | fn push_generic_params_internal<'tcx>( |
612 | tcx: TyCtxt<'tcx>, | |
613 | substs: SubstsRef<'tcx>, | |
614 | output: &mut String, | |
615 | visited: &mut FxHashSet<Ty<'tcx>>, | |
616 | ) -> bool { | |
617 | if substs.non_erasable_generics().next().is_none() { | |
618 | return false; | |
d9579d0f AL |
619 | } |
620 | ||
136023e0 XL |
621 | debug_assert_eq!(substs, tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substs)); |
622 | ||
a2a8927a | 623 | let cpp_like_debuginfo = cpp_like_debuginfo(tcx); |
136023e0 XL |
624 | |
625 | output.push('<'); | |
626 | ||
627 | for type_parameter in substs.non_erasable_generics() { | |
628 | match type_parameter { | |
629 | GenericArgKind::Type(type_parameter) => { | |
630 | push_debuginfo_type_name(tcx, type_parameter, true, output, visited); | |
631 | } | |
632 | GenericArgKind::Const(ct) => { | |
633 | push_const_param(tcx, ct, output); | |
634 | } | |
635 | other => bug!("Unexpected non-erasable generic: {:?}", other), | |
d9579d0f AL |
636 | } |
637 | ||
a2a8927a | 638 | push_arg_separator(cpp_like_debuginfo, output); |
136023e0 XL |
639 | } |
640 | pop_arg_separator(output); | |
a2a8927a | 641 | push_close_angle_bracket(cpp_like_debuginfo, output); |
136023e0 XL |
642 | |
643 | true | |
644 | } | |
d9579d0f | 645 | |
5099ac24 | 646 | fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut String) { |
923072b8 | 647 | match ct.kind() { |
136023e0 XL |
648 | ty::ConstKind::Param(param) => { |
649 | write!(output, "{}", param.name) | |
d9579d0f | 650 | } |
5099ac24 | 651 | _ => match ct.ty().kind() { |
136023e0 | 652 | ty::Int(ity) => { |
5099ac24 | 653 | let bits = ct.eval_bits(tcx, ty::ParamEnv::reveal_all(), ct.ty()); |
136023e0 XL |
654 | let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128; |
655 | write!(output, "{}", val) | |
656 | } | |
657 | ty::Uint(_) => { | |
5099ac24 | 658 | let val = ct.eval_bits(tcx, ty::ParamEnv::reveal_all(), ct.ty()); |
136023e0 XL |
659 | write!(output, "{}", val) |
660 | } | |
661 | ty::Bool => { | |
662 | let val = ct.try_eval_bool(tcx, ty::ParamEnv::reveal_all()).unwrap(); | |
663 | write!(output, "{}", val) | |
664 | } | |
665 | _ => { | |
666 | // If we cannot evaluate the constant to a known type, we fall back | |
667 | // to emitting a stable hash value of the constant. This isn't very pretty | |
668 | // but we get a deterministic, virtually unique value for the constant. | |
064997fb | 669 | // |
136023e0 XL |
670 | // Let's only emit 64 bits of the hash value. That should be plenty for |
671 | // avoiding collisions and will make the emitted type names shorter. | |
064997fb FG |
672 | let hash_short = tcx.with_stable_hashing_context(|mut hcx| { |
673 | let mut hasher = StableHasher::new(); | |
674 | let ct = ct.eval(tcx, ty::ParamEnv::reveal_all()); | |
675 | hcx.while_hashing_spans(false, |hcx| { | |
676 | ct.to_valtree().hash_stable(hcx, &mut hasher) | |
677 | }); | |
2b03887a FG |
678 | let hash: u64 = hasher.finish(); |
679 | hash | |
064997fb | 680 | }); |
136023e0 | 681 | |
a2a8927a | 682 | if cpp_like_debuginfo(tcx) { |
923072b8 | 683 | write!(output, "CONST${:x}", hash_short) |
136023e0 | 684 | } else { |
923072b8 | 685 | write!(output, "{{CONST#{:x}}}", hash_short) |
136023e0 XL |
686 | } |
687 | } | |
688 | }, | |
689 | } | |
690 | .unwrap(); | |
691 | } | |
d9579d0f | 692 | |
136023e0 XL |
693 | pub fn push_generic_params<'tcx>(tcx: TyCtxt<'tcx>, substs: SubstsRef<'tcx>, output: &mut String) { |
694 | let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name"); | |
695 | let mut visited = FxHashSet::default(); | |
696 | push_generic_params_internal(tcx, substs, output, &mut visited); | |
697 | } | |
698 | ||
5e7ed085 FG |
699 | fn push_closure_or_generator_name<'tcx>( |
700 | tcx: TyCtxt<'tcx>, | |
701 | def_id: DefId, | |
702 | substs: SubstsRef<'tcx>, | |
703 | qualified: bool, | |
704 | output: &mut String, | |
705 | visited: &mut FxHashSet<Ty<'tcx>>, | |
706 | ) { | |
707 | // Name will be "{closure_env#0}<T1, T2, ...>", "{generator_env#0}<T1, T2, ...>", or | |
708 | // "{async_fn_env#0}<T1, T2, ...>", etc. | |
709 | let def_key = tcx.def_key(def_id); | |
710 | let generator_kind = tcx.generator_kind(def_id); | |
711 | ||
712 | if qualified { | |
713 | let parent_def_id = DefId { index: def_key.parent.unwrap(), ..def_id }; | |
714 | push_item_name(tcx, parent_def_id, true, output); | |
715 | output.push_str("::"); | |
716 | } | |
717 | ||
718 | let mut label = String::with_capacity(20); | |
719 | write!(&mut label, "{}_env", generator_kind_label(generator_kind)).unwrap(); | |
720 | ||
721 | push_disambiguated_special_name( | |
722 | &label, | |
723 | def_key.disambiguated_data.disambiguator, | |
724 | cpp_like_debuginfo(tcx), | |
725 | output, | |
726 | ); | |
727 | ||
728 | // We also need to add the generic arguments of the async fn/generator or | |
729 | // the enclosing function (for closures or async blocks), so that we end | |
730 | // up with a unique name for every instantiation. | |
731 | ||
732 | // Find the generics of the enclosing function, as defined in the source code. | |
733 | let enclosing_fn_def_id = tcx.typeck_root_def_id(def_id); | |
734 | let generics = tcx.generics_of(enclosing_fn_def_id); | |
735 | ||
736 | // Truncate the substs to the length of the above generics. This will cut off | |
737 | // anything closure- or generator-specific. | |
738 | let substs = substs.truncate_to(tcx, generics); | |
739 | push_generic_params_internal(tcx, substs, output, visited); | |
740 | } | |
741 | ||
a2a8927a | 742 | fn push_close_angle_bracket(cpp_like_debuginfo: bool, output: &mut String) { |
136023e0 XL |
743 | // MSVC debugger always treats `>>` as a shift, even when parsing templates, |
744 | // so add a space to avoid confusion. | |
a2a8927a | 745 | if cpp_like_debuginfo && output.ends_with('>') { |
136023e0 XL |
746 | output.push(' ') |
747 | }; | |
748 | ||
749 | output.push('>'); | |
750 | } | |
751 | ||
752 | fn pop_close_angle_bracket(output: &mut String) { | |
753 | assert!(output.ends_with('>'), "'output' does not end with '>': {}", output); | |
754 | output.pop(); | |
755 | if output.ends_with(' ') { | |
d9579d0f | 756 | output.pop(); |
136023e0 XL |
757 | } |
758 | } | |
d9579d0f | 759 | |
a2a8927a | 760 | fn push_arg_separator(cpp_like_debuginfo: bool, output: &mut String) { |
136023e0 XL |
761 | // Natvis does not always like having spaces between parts of the type name |
762 | // and this causes issues when we need to write a typename in natvis, for example | |
763 | // as part of a cast like the `HashMap` visualizer does. | |
a2a8927a | 764 | if cpp_like_debuginfo { |
136023e0 XL |
765 | output.push(','); |
766 | } else { | |
767 | output.push_str(", "); | |
768 | }; | |
769 | } | |
770 | ||
771 | fn pop_arg_separator(output: &mut String) { | |
772 | if output.ends_with(' ') { | |
773 | output.pop(); | |
d9579d0f | 774 | } |
136023e0 XL |
775 | |
776 | assert!(output.ends_with(',')); | |
777 | ||
778 | output.pop(); | |
779 | } | |
780 | ||
a2a8927a XL |
781 | /// Check if we should generate C++ like names and debug information. |
782 | pub fn cpp_like_debuginfo(tcx: TyCtxt<'_>) -> bool { | |
136023e0 | 783 | tcx.sess.target.is_like_msvc |
d9579d0f | 784 | } |