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