1 | use rustc_data_structures::stable_hasher::{Hash64, HashStable, StableHasher}; |
---|---|
2 | use rustc_hir::def_id::CrateNum; |
3 | use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; |
4 | use rustc_middle::ty::print::{PrettyPrinter, Print, PrintError, Printer}; |
5 | use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeVisitableExt}; |
6 | use rustc_middle::ty::{GenericArg, GenericArgKind}; |
7 | |
8 | use std::fmt::{self, Write}; |
9 | use std::mem::{self, discriminant}; |
10 | |
11 | pub(super) fn mangle<'tcx>( |
12 | tcx: TyCtxt<'tcx>, |
13 | instance: Instance<'tcx>, |
14 | instantiating_crate: Option<CrateNum>, |
15 | ) -> String { |
16 | let def_id = instance.def_id(); |
17 | |
18 | // We want to compute the "type" of this item. Unfortunately, some |
19 | // kinds of items (e.g., closures) don't have an entry in the |
20 | // item-type array. So walk back up the find the closest parent |
21 | // that DOES have an entry. |
22 | let mut ty_def_id = def_id; |
23 | let instance_ty; |
24 | loop { |
25 | let key = tcx.def_key(ty_def_id); |
26 | match key.disambiguated_data.data { |
27 | DefPathData::TypeNs(_) | DefPathData::ValueNs(_) => { |
28 | instance_ty = tcx.type_of(ty_def_id).instantiate_identity(); |
29 | debug!(?instance_ty); |
30 | break; |
31 | } |
32 | _ => { |
33 | // if we're making a symbol for something, there ought |
34 | // to be a value or type-def or something in there |
35 | // *somewhere* |
36 | ty_def_id.index = key.parent.unwrap_or_else(|| { |
37 | bug!( |
38 | "finding type for{:?} , encountered def-id{:?} with no \ |
39 | parent", |
40 | def_id, |
41 | ty_def_id |
42 | ); |
43 | }); |
44 | } |
45 | } |
46 | } |
47 | |
48 | // Erase regions because they may not be deterministic when hashed |
49 | // and should not matter anyhow. |
50 | let instance_ty = tcx.erase_regions(instance_ty); |
51 | |
52 | let hash = get_symbol_hash(tcx, instance, instance_ty, instantiating_crate); |
53 | |
54 | let mut printer = SymbolPrinter { tcx, path: SymbolPath::new(), keep_within_component: false }; |
55 | printer |
56 | .print_def_path( |
57 | def_id, |
58 | if let ty::InstanceDef::DropGlue(_, _) = instance.def { |
59 | // Add the name of the dropped type to the symbol name |
60 | &*instance.args |
61 | } else { |
62 | &[] |
63 | }, |
64 | ) |
65 | .unwrap(); |
66 | |
67 | if let ty::InstanceDef::ThreadLocalShim(..) = instance.def { |
68 | let _ = printer.write_str("{{tls-shim}}"); |
69 | } |
70 | |
71 | if let ty::InstanceDef::VTableShim(..) = instance.def { |
72 | let _ = printer.write_str("{{vtable-shim}}"); |
73 | } |
74 | |
75 | if let ty::InstanceDef::ReifyShim(..) = instance.def { |
76 | let _ = printer.write_str("{{reify-shim}}"); |
77 | } |
78 | |
79 | printer.path.finish(hash) |
80 | } |
81 | |
82 | fn get_symbol_hash<'tcx>( |
83 | tcx: TyCtxt<'tcx>, |
84 | |
85 | // instance this name will be for |
86 | instance: Instance<'tcx>, |
87 | |
88 | // type of the item, without any generic |
89 | // parameters substituted; this is |
90 | // included in the hash as a kind of |
91 | // safeguard. |
92 | item_type: Ty<'tcx>, |
93 | |
94 | instantiating_crate: Option<CrateNum>, |
95 | ) -> Hash64 { |
96 | let def_id = instance.def_id(); |
97 | let args = instance.args; |
98 | debug!("get_symbol_hash(def_id={:?} , parameters={:?} )", def_id, args); |
99 | |
100 | tcx.with_stable_hashing_context(|mut hcx| { |
101 | let mut hasher = StableHasher::new(); |
102 | |
103 | // the main symbol name is not necessarily unique; hash in the |
104 | // compiler's internal def-path, guaranteeing each symbol has a |
105 | // truly unique path |
106 | tcx.def_path_hash(def_id).hash_stable(&mut hcx, &mut hasher); |
107 | |
108 | // Include the main item-type. Note that, in this case, the |
109 | // assertions about `has_param` may not hold, but this item-type |
110 | // ought to be the same for every reference anyway. |
111 | assert!(!item_type.has_erasable_regions()); |
112 | hcx.while_hashing_spans(false, |hcx| { |
113 | item_type.hash_stable(hcx, &mut hasher); |
114 | |
115 | // If this is a function, we hash the signature as well. |
116 | // This is not *strictly* needed, but it may help in some |
117 | // situations, see the `run-make/a-b-a-linker-guard` test. |
118 | if let ty::FnDef(..) = item_type.kind() { |
119 | item_type.fn_sig(tcx).hash_stable(hcx, &mut hasher); |
120 | } |
121 | |
122 | // also include any type parameters (for generic items) |
123 | args.hash_stable(hcx, &mut hasher); |
124 | |
125 | if let Some(instantiating_crate) = instantiating_crate { |
126 | tcx.def_path_hash(instantiating_crate.as_def_id()) |
127 | .stable_crate_id() |
128 | .hash_stable(hcx, &mut hasher); |
129 | } |
130 | |
131 | // We want to avoid accidental collision between different types of instances. |
132 | // Especially, `VTableShim`s and `ReifyShim`s may overlap with their original |
133 | // instances without this. |
134 | discriminant(&instance.def).hash_stable(hcx, &mut hasher); |
135 | }); |
136 | |
137 | // 64 bits should be enough to avoid collisions. |
138 | hasher.finish::<Hash64>() |
139 | }) |
140 | } |
141 | |
142 | // Follow C++ namespace-mangling style, see |
143 | // https://en.wikipedia.org/wiki/Name_mangling for more info. |
144 | // |
145 | // It turns out that on macOS you can actually have arbitrary symbols in |
146 | // function names (at least when given to LLVM), but this is not possible |
147 | // when using unix's linker. Perhaps one day when we just use a linker from LLVM |
148 | // we won't need to do this name mangling. The problem with name mangling is |
149 | // that it seriously limits the available characters. For example we can't |
150 | // have things like &T in symbol names when one would theoretically |
151 | // want them for things like impls of traits on that type. |
152 | // |
153 | // To be able to work on all platforms and get *some* reasonable output, we |
154 | // use C++ name-mangling. |
155 | #[derive(Debug)] |
156 | struct SymbolPath { |
157 | result: String, |
158 | temp_buf: String, |
159 | } |
160 | |
161 | impl SymbolPath { |
162 | fn new() -> Self { |
163 | let mut result: SymbolPath = |
164 | SymbolPath { result: String::with_capacity(64), temp_buf: String::with_capacity(16) }; |
165 | result.result.push_str(string:"_ZN"); // _Z == Begin name-sequence, N == nested |
166 | result |
167 | } |
168 | |
169 | fn finalize_pending_component(&mut self) { |
170 | if !self.temp_buf.is_empty() { |
171 | let _ = write!(self.result, "{}{} ", self.temp_buf.len(), self.temp_buf); |
172 | self.temp_buf.clear(); |
173 | } |
174 | } |
175 | |
176 | fn finish(mut self, hash: Hash64) -> String { |
177 | self.finalize_pending_component(); |
178 | // E = end name-sequence |
179 | let _ = write!(self.result, "17h{hash:016x} E"); |
180 | self.result |
181 | } |
182 | } |
183 | |
184 | struct SymbolPrinter<'tcx> { |
185 | tcx: TyCtxt<'tcx>, |
186 | path: SymbolPath, |
187 | |
188 | // When `true`, `finalize_pending_component` isn't used. |
189 | // This is needed when recursing into `path_qualified`, |
190 | // or `path_generic_args`, as any nested paths are |
191 | // logically within one component. |
192 | keep_within_component: bool, |
193 | } |
194 | |
195 | // HACK(eddyb) this relies on using the `fmt` interface to get |
196 | // `PrettyPrinter` aka pretty printing of e.g. types in paths, |
197 | // symbol names should have their own printing machinery. |
198 | |
199 | impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> { |
200 | fn tcx(&self) -> TyCtxt<'tcx> { |
201 | self.tcx |
202 | } |
203 | |
204 | fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> { |
205 | Ok(()) |
206 | } |
207 | |
208 | fn print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError> { |
209 | match *ty.kind() { |
210 | // Print all nominal types as paths (unlike `pretty_print_type`). |
211 | ty::FnDef(def_id, args) |
212 | | ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, args, .. }) |
213 | | ty::Closure(def_id, args) |
214 | | ty::Coroutine(def_id, args) => self.print_def_path(def_id, args), |
215 | |
216 | // The `pretty_print_type` formatting of array size depends on |
217 | // -Zverbose-internals flag, so we cannot reuse it here. |
218 | ty::Array(ty, size) => { |
219 | self.write_str("[")?; |
220 | self.print_type(ty)?; |
221 | self.write_str("; ")?; |
222 | if let Some(size) = size.try_to_target_usize(self.tcx()) { |
223 | write!(self, "{size} ")? |
224 | } else if let ty::ConstKind::Param(param) = size.kind() { |
225 | param.print(self)? |
226 | } else { |
227 | self.write_str("_")? |
228 | } |
229 | self.write_str("]")?; |
230 | Ok(()) |
231 | } |
232 | |
233 | ty::Alias(ty::Inherent, _) => panic!("unexpected inherent projection"), |
234 | |
235 | _ => self.pretty_print_type(ty), |
236 | } |
237 | } |
238 | |
239 | fn print_dyn_existential( |
240 | &mut self, |
241 | predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, |
242 | ) -> Result<(), PrintError> { |
243 | let mut first = true; |
244 | for p in predicates { |
245 | if !first { |
246 | write!(self, "+")?; |
247 | } |
248 | first = false; |
249 | p.print(self)?; |
250 | } |
251 | Ok(()) |
252 | } |
253 | |
254 | fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> { |
255 | // only print integers |
256 | match (ct.kind(), ct.ty().kind()) { |
257 | (ty::ConstKind::Value(ty::ValTree::Leaf(scalar)), ty::Int(_) | ty::Uint(_)) => { |
258 | // The `pretty_print_const` formatting depends on -Zverbose-internals |
259 | // flag, so we cannot reuse it here. |
260 | let signed = matches!(ct.ty().kind(), ty::Int(_)); |
261 | write!( |
262 | self, |
263 | "{:#?} ", |
264 | ty::ConstInt::new(scalar, signed, ct.ty().is_ptr_sized_integral()) |
265 | )?; |
266 | } |
267 | _ => self.write_str("_")?, |
268 | } |
269 | Ok(()) |
270 | } |
271 | |
272 | fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> { |
273 | self.write_str(self.tcx.crate_name(cnum).as_str())?; |
274 | Ok(()) |
275 | } |
276 | fn path_qualified( |
277 | &mut self, |
278 | self_ty: Ty<'tcx>, |
279 | trait_ref: Option<ty::TraitRef<'tcx>>, |
280 | ) -> Result<(), PrintError> { |
281 | // Similar to `pretty_path_qualified`, but for the other |
282 | // types that are printed as paths (see `print_type` above). |
283 | match self_ty.kind() { |
284 | ty::FnDef(..) | ty::Alias(..) | ty::Closure(..) | ty::Coroutine(..) |
285 | if trait_ref.is_none() => |
286 | { |
287 | self.print_type(self_ty) |
288 | } |
289 | |
290 | _ => self.pretty_path_qualified(self_ty, trait_ref), |
291 | } |
292 | } |
293 | |
294 | fn path_append_impl( |
295 | &mut self, |
296 | print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, |
297 | _disambiguated_data: &DisambiguatedDefPathData, |
298 | self_ty: Ty<'tcx>, |
299 | trait_ref: Option<ty::TraitRef<'tcx>>, |
300 | ) -> Result<(), PrintError> { |
301 | self.pretty_path_append_impl( |
302 | |cx| { |
303 | print_prefix(cx)?; |
304 | |
305 | if cx.keep_within_component { |
306 | // HACK(eddyb) print the path similarly to how `FmtPrinter` prints it. |
307 | cx.write_str("::")?; |
308 | } else { |
309 | cx.path.finalize_pending_component(); |
310 | } |
311 | |
312 | Ok(()) |
313 | }, |
314 | self_ty, |
315 | trait_ref, |
316 | ) |
317 | } |
318 | fn path_append( |
319 | &mut self, |
320 | print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, |
321 | disambiguated_data: &DisambiguatedDefPathData, |
322 | ) -> Result<(), PrintError> { |
323 | print_prefix(self)?; |
324 | |
325 | // Skip `::{{extern}}` blocks and `::{{constructor}}` on tuple/unit structs. |
326 | if let DefPathData::ForeignMod | DefPathData::Ctor = disambiguated_data.data { |
327 | return Ok(()); |
328 | } |
329 | |
330 | if self.keep_within_component { |
331 | // HACK(eddyb) print the path similarly to how `FmtPrinter` prints it. |
332 | self.write_str("::")?; |
333 | } else { |
334 | self.path.finalize_pending_component(); |
335 | } |
336 | |
337 | write!(self, "{} ", disambiguated_data.data)?; |
338 | |
339 | Ok(()) |
340 | } |
341 | fn path_generic_args( |
342 | &mut self, |
343 | print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>, |
344 | args: &[GenericArg<'tcx>], |
345 | ) -> Result<(), PrintError> { |
346 | print_prefix(self)?; |
347 | |
348 | let args = |
349 | args.iter().cloned().filter(|arg| !matches!(arg.unpack(), GenericArgKind::Lifetime(_))); |
350 | |
351 | if args.clone().next().is_some() { |
352 | self.generic_delimiters(|cx| cx.comma_sep(args)) |
353 | } else { |
354 | Ok(()) |
355 | } |
356 | } |
357 | } |
358 | |
359 | impl<'tcx> PrettyPrinter<'tcx> for SymbolPrinter<'tcx> { |
360 | fn should_print_region(&self, _region: ty::Region<'_>) -> bool { |
361 | false |
362 | } |
363 | fn comma_sep<T>(&mut self, mut elems: impl Iterator<Item = T>) -> Result<(), PrintError> |
364 | where |
365 | T: Print<'tcx, Self>, |
366 | { |
367 | if let Some(first) = elems.next() { |
368 | first.print(self)?; |
369 | for elem in elems { |
370 | self.write_str(",")?; |
371 | elem.print(self)?; |
372 | } |
373 | } |
374 | Ok(()) |
375 | } |
376 | |
377 | fn generic_delimiters( |
378 | &mut self, |
379 | f: impl FnOnce(&mut Self) -> Result<(), PrintError>, |
380 | ) -> Result<(), PrintError> { |
381 | write!(self, "<")?; |
382 | |
383 | let kept_within_component = mem::replace(&mut self.keep_within_component, true); |
384 | f(self)?; |
385 | self.keep_within_component = kept_within_component; |
386 | |
387 | write!(self, ">")?; |
388 | |
389 | Ok(()) |
390 | } |
391 | } |
392 | |
393 | impl fmt::Write for SymbolPrinter<'_> { |
394 | fn write_str(&mut self, s: &str) -> fmt::Result { |
395 | // Name sanitation. LLVM will happily accept identifiers with weird names, but |
396 | // gas doesn't! |
397 | // gas accepts the following characters in symbols: a-z, A-Z, 0-9, ., _, $ |
398 | // NVPTX assembly has more strict naming rules than gas, so additionally, dots |
399 | // are replaced with '$' there. |
400 | |
401 | for c in s.chars() { |
402 | if self.path.temp_buf.is_empty() { |
403 | match c { |
404 | 'a'..= 'z'| 'A'..= 'Z'| '_'=> {} |
405 | _ => { |
406 | // Underscore-qualify anything that didn't start as an ident. |
407 | self.path.temp_buf.push('_'); |
408 | } |
409 | } |
410 | } |
411 | match c { |
412 | // Escape these with $ sequences |
413 | '@'=> self.path.temp_buf.push_str( "$SP$"), |
414 | '*'=> self.path.temp_buf.push_str( "$BP$"), |
415 | '&'=> self.path.temp_buf.push_str( "$RF$"), |
416 | '<'=> self.path.temp_buf.push_str( "$LT$"), |
417 | '>'=> self.path.temp_buf.push_str( "$GT$"), |
418 | '('=> self.path.temp_buf.push_str( "$LP$"), |
419 | ')'=> self.path.temp_buf.push_str( "$RP$"), |
420 | ','=> self.path.temp_buf.push_str( "$C$"), |
421 | |
422 | '-'| ':'| '.'if self.tcx.has_strict_asm_symbol_naming() => { |
423 | // NVPTX doesn't support these characters in symbol names. |
424 | self.path.temp_buf.push('$') |
425 | } |
426 | |
427 | // '.' doesn't occur in types and functions, so reuse it |
428 | // for ':' and '-' |
429 | '-'| ':'=> self.path.temp_buf.push( '.'), |
430 | |
431 | // Avoid crashing LLVM in certain (LTO-related) situations, see #60925. |
432 | 'm'if self.path.temp_buf.ends_with( ".llv") => self.path.temp_buf.push_str( "$u6d$"), |
433 | |
434 | // These are legal symbols |
435 | 'a'..= 'z'| 'A'..= 'Z'| '0'..= '9'| '_'| '.'| '$'=> self.path.temp_buf.push(c), |
436 | |
437 | _ => { |
438 | self.path.temp_buf.push('$'); |
439 | for c in c.escape_unicode().skip(1) { |
440 | match c { |
441 | '{'=> {} |
442 | '}'=> self.path.temp_buf.push( '$'), |
443 | c => self.path.temp_buf.push(c), |
444 | } |
445 | } |
446 | } |
447 | } |
448 | } |
449 | |
450 | Ok(()) |
451 | } |
452 | } |
453 |
Definitions
Learn Rust with the experts
Find out more