]> git.lizzy.rs Git - rust.git/blob - src/librustc_codegen_ssa/debuginfo/type_names.rs
Rollup merge of #69007 - GuillaumeGomez:clean-up-e0283, r=Dylan-DPC
[rust.git] / src / librustc_codegen_ssa / debuginfo / type_names.rs
1 // Type Names for Debug Info.
2
3 use rustc::ty::{self, subst::SubstsRef, Ty, TyCtxt};
4 use rustc_data_structures::fx::FxHashSet;
5 use rustc_hir as hir;
6 use rustc_hir::def_id::DefId;
7
8 // Compute the name of the type as it should be stored in debuginfo. Does not do
9 // any caching, i.e., calling the function twice with the same type will also do
10 // the work twice. The `qualified` parameter only affects the first level of the
11 // type name, further levels (i.e., type parameters) are always fully qualified.
12 pub fn compute_debuginfo_type_name<'tcx>(
13     tcx: TyCtxt<'tcx>,
14     t: Ty<'tcx>,
15     qualified: bool,
16 ) -> String {
17     let mut result = String::with_capacity(64);
18     let mut visited = FxHashSet::default();
19     push_debuginfo_type_name(tcx, t, qualified, &mut result, &mut visited);
20     result
21 }
22
23 // Pushes the name of the type as it should be stored in debuginfo on the
24 // `output` String. See also compute_debuginfo_type_name().
25 pub fn push_debuginfo_type_name<'tcx>(
26     tcx: TyCtxt<'tcx>,
27     t: Ty<'tcx>,
28     qualified: bool,
29     output: &mut String,
30     visited: &mut FxHashSet<Ty<'tcx>>,
31 ) {
32     // When targeting MSVC, emit C++ style type names for compatibility with
33     // .natvis visualizers (and perhaps other existing native debuggers?)
34     let cpp_like_names = tcx.sess.target.target.options.is_like_msvc;
35
36     match t.kind {
37         ty::Bool => output.push_str("bool"),
38         ty::Char => output.push_str("char"),
39         ty::Str => output.push_str("str"),
40         ty::Never => output.push_str("!"),
41         ty::Int(int_ty) => output.push_str(int_ty.name_str()),
42         ty::Uint(uint_ty) => output.push_str(uint_ty.name_str()),
43         ty::Float(float_ty) => output.push_str(float_ty.name_str()),
44         ty::Foreign(def_id) => push_item_name(tcx, def_id, qualified, output),
45         ty::Adt(def, substs) => {
46             push_item_name(tcx, def.did, qualified, output);
47             push_type_params(tcx, substs, output, visited);
48         }
49         ty::Tuple(component_types) => {
50             output.push('(');
51             for &component_type in component_types {
52                 push_debuginfo_type_name(tcx, component_type.expect_ty(), true, output, visited);
53                 output.push_str(", ");
54             }
55             if !component_types.is_empty() {
56                 output.pop();
57                 output.pop();
58             }
59             output.push(')');
60         }
61         ty::RawPtr(ty::TypeAndMut { ty: inner_type, mutbl }) => {
62             if !cpp_like_names {
63                 output.push('*');
64             }
65             match mutbl {
66                 hir::Mutability::Not => output.push_str("const "),
67                 hir::Mutability::Mut => output.push_str("mut "),
68             }
69
70             push_debuginfo_type_name(tcx, inner_type, true, output, visited);
71
72             if cpp_like_names {
73                 output.push('*');
74             }
75         }
76         ty::Ref(_, inner_type, mutbl) => {
77             if !cpp_like_names {
78                 output.push('&');
79             }
80             output.push_str(mutbl.prefix_str());
81
82             push_debuginfo_type_name(tcx, inner_type, true, output, visited);
83
84             if cpp_like_names {
85                 output.push('*');
86             }
87         }
88         ty::Array(inner_type, len) => {
89             output.push('[');
90             push_debuginfo_type_name(tcx, inner_type, true, output, visited);
91             output.push_str(&format!("; {}", len.eval_usize(tcx, ty::ParamEnv::reveal_all())));
92             output.push(']');
93         }
94         ty::Slice(inner_type) => {
95             if cpp_like_names {
96                 output.push_str("slice<");
97             } else {
98                 output.push('[');
99             }
100
101             push_debuginfo_type_name(tcx, inner_type, true, output, visited);
102
103             if cpp_like_names {
104                 output.push('>');
105             } else {
106                 output.push(']');
107             }
108         }
109         ty::Dynamic(ref trait_data, ..) => {
110             if let Some(principal) = trait_data.principal() {
111                 let principal = tcx
112                     .normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &principal);
113                 push_item_name(tcx, principal.def_id, false, output);
114                 push_type_params(tcx, principal.substs, output, visited);
115             } else {
116                 output.push_str("dyn '_");
117             }
118         }
119         ty::FnDef(..) | ty::FnPtr(_) => {
120             // We've encountered a weird 'recursive type'
121             // Currently, the only way to generate such a type
122             // is by using 'impl trait':
123             //
124             // fn foo() -> impl Copy { foo }
125             //
126             // There's not really a sensible name we can generate,
127             // since we don't include 'impl trait' types (e.g. ty::Opaque)
128             // in the output
129             //
130             // Since we need to generate *something*, we just
131             // use a dummy string that should make it clear
132             // that something unusual is going on
133             if !visited.insert(t) {
134                 output.push_str("<recursive_type>");
135                 return;
136             }
137
138             let sig = t.fn_sig(tcx);
139             output.push_str(sig.unsafety().prefix_str());
140
141             let abi = sig.abi();
142             if abi != rustc_target::spec::abi::Abi::Rust {
143                 output.push_str("extern \"");
144                 output.push_str(abi.name());
145                 output.push_str("\" ");
146             }
147
148             output.push_str("fn(");
149
150             let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig);
151             if !sig.inputs().is_empty() {
152                 for &parameter_type in sig.inputs() {
153                     push_debuginfo_type_name(tcx, parameter_type, true, output, visited);
154                     output.push_str(", ");
155                 }
156                 output.pop();
157                 output.pop();
158             }
159
160             if sig.c_variadic {
161                 if !sig.inputs().is_empty() {
162                     output.push_str(", ...");
163                 } else {
164                     output.push_str("...");
165                 }
166             }
167
168             output.push(')');
169
170             if !sig.output().is_unit() {
171                 output.push_str(" -> ");
172                 push_debuginfo_type_name(tcx, sig.output(), true, output, visited);
173             }
174
175             // We only keep the type in 'visited'
176             // for the duration of the body of this method.
177             // It's fine for a particular function type
178             // to show up multiple times in one overall type
179             // (e.g. MyType<fn() -> u8, fn() -> u8>
180             //
181             // We only care about avoiding recursing
182             // directly back to the type we're currently
183             // processing
184             visited.remove(t);
185         }
186         ty::Closure(def_id, ..) => {
187             output.push_str(&format!(
188                 "closure-{}",
189                 tcx.def_key(def_id).disambiguated_data.disambiguator
190             ));
191         }
192         ty::Generator(def_id, ..) => {
193             output.push_str(&format!(
194                 "generator-{}",
195                 tcx.def_key(def_id).disambiguated_data.disambiguator
196             ));
197         }
198         ty::Error
199         | ty::Infer(_)
200         | ty::Placeholder(..)
201         | ty::UnnormalizedProjection(..)
202         | ty::Projection(..)
203         | ty::Bound(..)
204         | ty::Opaque(..)
205         | ty::GeneratorWitness(..)
206         | ty::Param(_) => {
207             bug!(
208                 "debuginfo: Trying to create type name for \
209                   unexpected type: {:?}",
210                 t
211             );
212         }
213     }
214
215     fn push_item_name(tcx: TyCtxt<'tcx>, def_id: DefId, qualified: bool, output: &mut String) {
216         if qualified {
217             output.push_str(&tcx.crate_name(def_id.krate).as_str());
218             for path_element in tcx.def_path(def_id).data {
219                 output.push_str("::");
220                 output.push_str(&path_element.data.as_symbol().as_str());
221             }
222         } else {
223             output.push_str(&tcx.item_name(def_id).as_str());
224         }
225     }
226
227     // Pushes the type parameters in the given `InternalSubsts` to the output string.
228     // This ignores region parameters, since they can't reliably be
229     // reconstructed for items from non-local crates. For local crates, this
230     // would be possible but with inlining and LTO we have to use the least
231     // common denominator - otherwise we would run into conflicts.
232     fn push_type_params<'tcx>(
233         tcx: TyCtxt<'tcx>,
234         substs: SubstsRef<'tcx>,
235         output: &mut String,
236         visited: &mut FxHashSet<Ty<'tcx>>,
237     ) {
238         if substs.types().next().is_none() {
239             return;
240         }
241
242         output.push('<');
243
244         for type_parameter in substs.types() {
245             push_debuginfo_type_name(tcx, type_parameter, true, output, visited);
246             output.push_str(", ");
247         }
248
249         output.pop();
250         output.pop();
251
252         output.push('>');
253     }
254 }