]>
Commit | Line | Data |
---|---|---|
064997fb FG |
1 | // For more information about type metadata and type metadata identifiers for cross-language LLVM |
2 | // CFI support, see Type metadata in the design document in the tracking issue #89653. | |
3 | ||
4 | // FIXME(rcvalle): Identify C char and integer type uses and encode them with their respective | |
5 | // builtin type encodings as specified by the Itanium C++ ABI for extern function types with the "C" | |
6 | // calling convention to use this encoding for cross-language LLVM CFI. | |
7 | ||
8 | use bitflags::bitflags; | |
9 | use core::fmt::Display; | |
10 | use rustc_data_structures::base_n; | |
11 | use rustc_data_structures::fx::FxHashMap; | |
12 | use rustc_hir as hir; | |
13 | use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; | |
14 | use rustc_middle::ty::{ | |
487cf647 FG |
15 | self, Const, ExistentialPredicate, FloatTy, FnSig, IntTy, List, Region, RegionKind, TermKind, |
16 | Ty, TyCtxt, UintTy, | |
064997fb FG |
17 | }; |
18 | use rustc_span::def_id::DefId; | |
19 | use rustc_span::symbol::sym; | |
20 | use rustc_target::abi::call::{Conv, FnAbi}; | |
21 | use rustc_target::spec::abi::Abi; | |
22 | use std::fmt::Write as _; | |
23 | ||
24 | /// Type and extended type qualifiers. | |
25 | #[derive(Eq, Hash, PartialEq)] | |
26 | enum TyQ { | |
27 | None, | |
28 | Const, | |
29 | Mut, | |
30 | } | |
31 | ||
32 | /// Substitution dictionary key. | |
33 | #[derive(Eq, Hash, PartialEq)] | |
34 | enum DictKey<'tcx> { | |
35 | Ty(Ty<'tcx>, TyQ), | |
36 | Region(Region<'tcx>), | |
37 | Const(Const<'tcx>), | |
38 | Predicate(ExistentialPredicate<'tcx>), | |
39 | } | |
40 | ||
41 | bitflags! { | |
42 | /// Options for typeid_for_fnabi and typeid_for_fnsig. | |
43 | pub struct TypeIdOptions: u32 { | |
44 | const NO_OPTIONS = 0; | |
45 | const GENERALIZE_POINTERS = 1; | |
46 | const GENERALIZE_REPR_C = 2; | |
47 | } | |
48 | } | |
49 | ||
50 | /// Options for encode_ty. | |
51 | type EncodeTyOptions = TypeIdOptions; | |
52 | ||
53 | /// Options for transform_ty. | |
54 | type TransformTyOptions = TypeIdOptions; | |
55 | ||
56 | /// Converts a number to a disambiguator (see | |
57 | /// <https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html>). | |
58 | fn to_disambiguator(num: u64) -> String { | |
59 | if let Some(num) = num.checked_sub(1) { | |
60 | format!("s{}_", base_n::encode(num as u128, 62)) | |
61 | } else { | |
62 | "s_".to_string() | |
63 | } | |
64 | } | |
65 | ||
66 | /// Converts a number to a sequence number (see | |
67 | /// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.seq-id>). | |
68 | fn to_seq_id(num: usize) -> String { | |
69 | if let Some(num) = num.checked_sub(1) { | |
70 | base_n::encode(num as u128, 36).to_uppercase() | |
71 | } else { | |
72 | "".to_string() | |
73 | } | |
74 | } | |
75 | ||
76 | /// Substitutes a component if found in the substitution dictionary (see | |
77 | /// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression>). | |
78 | fn compress<'tcx>( | |
79 | dict: &mut FxHashMap<DictKey<'tcx>, usize>, | |
80 | key: DictKey<'tcx>, | |
81 | comp: &mut String, | |
82 | ) { | |
83 | match dict.get(&key) { | |
84 | Some(num) => { | |
85 | comp.clear(); | |
86 | let _ = write!(comp, "S{}_", to_seq_id(*num)); | |
87 | } | |
88 | None => { | |
89 | dict.insert(key, dict.len()); | |
90 | } | |
91 | } | |
92 | } | |
93 | ||
94 | // FIXME(rcvalle): Move to compiler/rustc_middle/src/ty/sty.rs after C types work is done, possibly | |
95 | // along with other is_c_type methods. | |
96 | /// Returns whether a `ty::Ty` is `c_void`. | |
97 | fn is_c_void_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { | |
98 | match ty.kind() { | |
99 | ty::Adt(adt_def, ..) => { | |
100 | let def_id = adt_def.0.did; | |
101 | let crate_name = tcx.crate_name(def_id.krate); | |
102 | if tcx.item_name(def_id).as_str() == "c_void" | |
103 | && (crate_name == sym::core || crate_name == sym::std || crate_name == sym::libc) | |
104 | { | |
105 | true | |
106 | } else { | |
107 | false | |
108 | } | |
109 | } | |
110 | _ => false, | |
111 | } | |
112 | } | |
113 | ||
114 | /// Encodes a const using the Itanium C++ ABI as a literal argument (see | |
115 | /// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling.literal>). | |
116 | fn encode_const<'tcx>( | |
117 | tcx: TyCtxt<'tcx>, | |
118 | c: Const<'tcx>, | |
119 | dict: &mut FxHashMap<DictKey<'tcx>, usize>, | |
120 | options: EncodeTyOptions, | |
121 | ) -> String { | |
122 | // L<element-type>[n]<element-value>E as literal argument | |
123 | let mut s = String::from('L'); | |
124 | ||
125 | // Element type | |
126 | s.push_str(&encode_ty(tcx, c.ty(), dict, options)); | |
127 | ||
128 | // The only allowed types of const parameters are bool, u8, u16, u32, u64, u128, usize i8, i16, | |
129 | // i32, i64, i128, isize, and char. The bool value false is encoded as 0 and true as 1. | |
130 | fn push_signed_value<T: Display + PartialOrd>(s: &mut String, value: T, zero: T) { | |
131 | if value < zero { | |
132 | s.push('n') | |
133 | }; | |
134 | let _ = write!(s, "{}", value); | |
135 | } | |
136 | ||
137 | fn push_unsigned_value<T: Display>(s: &mut String, value: T) { | |
138 | let _ = write!(s, "{}", value); | |
139 | } | |
140 | ||
141 | if let Some(scalar_int) = c.kind().try_to_scalar_int() { | |
142 | let signed = c.ty().is_signed(); | |
143 | match scalar_int.size().bits() { | |
144 | 8 if signed => push_signed_value(&mut s, scalar_int.try_to_i8().unwrap(), 0), | |
145 | 16 if signed => push_signed_value(&mut s, scalar_int.try_to_i16().unwrap(), 0), | |
146 | 32 if signed => push_signed_value(&mut s, scalar_int.try_to_i32().unwrap(), 0), | |
147 | 64 if signed => push_signed_value(&mut s, scalar_int.try_to_i64().unwrap(), 0), | |
148 | 128 if signed => push_signed_value(&mut s, scalar_int.try_to_i128().unwrap(), 0), | |
149 | 8 => push_unsigned_value(&mut s, scalar_int.try_to_u8().unwrap()), | |
150 | 16 => push_unsigned_value(&mut s, scalar_int.try_to_u16().unwrap()), | |
151 | 32 => push_unsigned_value(&mut s, scalar_int.try_to_u32().unwrap()), | |
152 | 64 => push_unsigned_value(&mut s, scalar_int.try_to_u64().unwrap()), | |
153 | 128 => push_unsigned_value(&mut s, scalar_int.try_to_u128().unwrap()), | |
154 | _ => { | |
155 | bug!("encode_const: unexpected size `{:?}`", scalar_int.size().bits()); | |
156 | } | |
157 | }; | |
158 | } else { | |
159 | bug!("encode_const: unexpected type `{:?}`", c.ty()); | |
160 | } | |
161 | ||
162 | // Close the "L..E" pair | |
163 | s.push('E'); | |
164 | ||
165 | compress(dict, DictKey::Const(c), &mut s); | |
166 | ||
167 | s | |
168 | } | |
169 | ||
170 | /// Encodes a FnSig using the Itanium C++ ABI with vendor extended type qualifiers and types for | |
171 | /// Rust types that are not used at the FFI boundary. | |
172 | fn encode_fnsig<'tcx>( | |
173 | tcx: TyCtxt<'tcx>, | |
174 | fn_sig: &FnSig<'tcx>, | |
175 | dict: &mut FxHashMap<DictKey<'tcx>, usize>, | |
176 | options: TypeIdOptions, | |
177 | ) -> String { | |
178 | // Function types are delimited by an "F..E" pair | |
179 | let mut s = String::from("F"); | |
180 | ||
181 | let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits()) | |
182 | .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits())); | |
183 | match fn_sig.abi { | |
184 | Abi::C { .. } => { | |
185 | encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C); | |
186 | } | |
187 | _ => { | |
188 | encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C); | |
189 | } | |
190 | } | |
191 | ||
192 | // Encode the return type | |
193 | let transform_ty_options = TransformTyOptions::from_bits(options.bits()) | |
194 | .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits())); | |
195 | let ty = transform_ty(tcx, fn_sig.output(), transform_ty_options); | |
196 | s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options)); | |
197 | ||
198 | // Encode the parameter types | |
199 | let tys = fn_sig.inputs(); | |
200 | if !tys.is_empty() { | |
201 | for ty in tys { | |
202 | let ty = transform_ty(tcx, *ty, transform_ty_options); | |
203 | s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options)); | |
204 | } | |
205 | ||
206 | if fn_sig.c_variadic { | |
207 | s.push('z'); | |
208 | } | |
209 | } else { | |
210 | if fn_sig.c_variadic { | |
211 | s.push('z'); | |
212 | } else { | |
213 | // Empty parameter lists, whether declared as () or conventionally as (void), are | |
214 | // encoded with a void parameter specifier "v". | |
215 | s.push('v') | |
216 | } | |
217 | } | |
218 | ||
219 | // Close the "F..E" pair | |
220 | s.push('E'); | |
221 | ||
222 | s | |
223 | } | |
224 | ||
225 | /// Encodes a predicate using the Itanium C++ ABI with vendor extended type qualifiers and types for | |
226 | /// Rust types that are not used at the FFI boundary. | |
227 | fn encode_predicate<'tcx>( | |
228 | tcx: TyCtxt<'tcx>, | |
487cf647 | 229 | predicate: ty::PolyExistentialPredicate<'tcx>, |
064997fb FG |
230 | dict: &mut FxHashMap<DictKey<'tcx>, usize>, |
231 | options: EncodeTyOptions, | |
232 | ) -> String { | |
233 | // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>, as vendor | |
234 | // extended type. | |
235 | let mut s = String::new(); | |
236 | match predicate.as_ref().skip_binder() { | |
237 | ty::ExistentialPredicate::Trait(trait_ref) => { | |
238 | let name = encode_ty_name(tcx, trait_ref.def_id); | |
239 | let _ = write!(s, "u{}{}", name.len(), &name); | |
240 | s.push_str(&encode_substs(tcx, trait_ref.substs, dict, options)); | |
241 | } | |
242 | ty::ExistentialPredicate::Projection(projection) => { | |
243 | let name = encode_ty_name(tcx, projection.item_def_id); | |
244 | let _ = write!(s, "u{}{}", name.len(), &name); | |
245 | s.push_str(&encode_substs(tcx, projection.substs, dict, options)); | |
f2b60f7d FG |
246 | match projection.term.unpack() { |
247 | TermKind::Ty(ty) => s.push_str(&encode_ty(tcx, ty, dict, options)), | |
248 | TermKind::Const(c) => s.push_str(&encode_const(tcx, c, dict, options)), | |
064997fb FG |
249 | } |
250 | } | |
251 | ty::ExistentialPredicate::AutoTrait(def_id) => { | |
252 | let name = encode_ty_name(tcx, *def_id); | |
253 | let _ = write!(s, "u{}{}", name.len(), &name); | |
254 | } | |
255 | }; | |
256 | compress(dict, DictKey::Predicate(*predicate.as_ref().skip_binder()), &mut s); | |
257 | s | |
258 | } | |
259 | ||
260 | /// Encodes predicates using the Itanium C++ ABI with vendor extended type qualifiers and types for | |
261 | /// Rust types that are not used at the FFI boundary. | |
262 | fn encode_predicates<'tcx>( | |
263 | tcx: TyCtxt<'tcx>, | |
487cf647 | 264 | predicates: &List<ty::PolyExistentialPredicate<'tcx>>, |
064997fb FG |
265 | dict: &mut FxHashMap<DictKey<'tcx>, usize>, |
266 | options: EncodeTyOptions, | |
267 | ) -> String { | |
268 | // <predicate1[..predicateN]>E as part of vendor extended type | |
269 | let mut s = String::new(); | |
487cf647 | 270 | let predicates: Vec<ty::PolyExistentialPredicate<'tcx>> = |
064997fb FG |
271 | predicates.iter().map(|predicate| predicate).collect(); |
272 | for predicate in predicates { | |
273 | s.push_str(&encode_predicate(tcx, predicate, dict, options)); | |
274 | } | |
275 | s | |
276 | } | |
277 | ||
278 | /// Encodes a region using the Itanium C++ ABI as a vendor extended type. | |
279 | fn encode_region<'tcx>( | |
280 | _tcx: TyCtxt<'tcx>, | |
281 | region: Region<'tcx>, | |
282 | dict: &mut FxHashMap<DictKey<'tcx>, usize>, | |
283 | _options: EncodeTyOptions, | |
284 | ) -> String { | |
285 | // u6region[I[<region-disambiguator>][<region-index>]E] as vendor extended type | |
286 | let mut s = String::new(); | |
287 | match region.kind() { | |
288 | RegionKind::ReLateBound(debruijn, r) => { | |
289 | s.push_str("u6regionI"); | |
290 | // Debruijn index, which identifies the binder, as region disambiguator | |
291 | let num = debruijn.index() as u64; | |
292 | if num > 0 { | |
293 | s.push_str(&to_disambiguator(num)); | |
294 | } | |
295 | // Index within the binder | |
296 | let _ = write!(s, "{}", r.var.index() as u64); | |
297 | s.push('E'); | |
298 | compress(dict, DictKey::Region(region), &mut s); | |
299 | } | |
300 | RegionKind::ReErased => { | |
301 | s.push_str("u6region"); | |
302 | compress(dict, DictKey::Region(region), &mut s); | |
303 | } | |
304 | RegionKind::ReEarlyBound(..) | |
305 | | RegionKind::ReFree(..) | |
306 | | RegionKind::ReStatic | |
307 | | RegionKind::ReVar(..) | |
f2b60f7d | 308 | | RegionKind::RePlaceholder(..) => { |
064997fb FG |
309 | bug!("encode_region: unexpected `{:?}`", region.kind()); |
310 | } | |
311 | } | |
312 | s | |
313 | } | |
314 | ||
315 | /// Encodes substs using the Itanium C++ ABI with vendor extended type qualifiers and types for Rust | |
316 | /// types that are not used at the FFI boundary. | |
317 | fn encode_substs<'tcx>( | |
318 | tcx: TyCtxt<'tcx>, | |
319 | substs: SubstsRef<'tcx>, | |
320 | dict: &mut FxHashMap<DictKey<'tcx>, usize>, | |
321 | options: EncodeTyOptions, | |
322 | ) -> String { | |
323 | // [I<subst1..substN>E] as part of vendor extended type | |
324 | let mut s = String::new(); | |
325 | let substs: Vec<GenericArg<'_>> = substs.iter().map(|subst| subst).collect(); | |
326 | if !substs.is_empty() { | |
327 | s.push('I'); | |
328 | for subst in substs { | |
329 | match subst.unpack() { | |
330 | GenericArgKind::Lifetime(region) => { | |
331 | s.push_str(&encode_region(tcx, region, dict, options)); | |
332 | } | |
333 | GenericArgKind::Type(ty) => { | |
334 | s.push_str(&encode_ty(tcx, ty, dict, options)); | |
335 | } | |
336 | GenericArgKind::Const(c) => { | |
337 | s.push_str(&encode_const(tcx, c, dict, options)); | |
338 | } | |
339 | } | |
340 | } | |
341 | s.push('E'); | |
342 | } | |
343 | s | |
344 | } | |
345 | ||
346 | /// Encodes a ty:Ty name, including its crate and path disambiguators and names. | |
347 | fn encode_ty_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> String { | |
348 | // Encode <name> for use in u<length><name>[I<element-type1..element-typeN>E], where | |
349 | // <element-type> is <subst>, using v0's <path> without v0's extended form of paths: | |
350 | // | |
351 | // N<namespace-tagN>..N<namespace-tag1> | |
352 | // C<crate-disambiguator><crate-name> | |
353 | // <path-disambiguator1><path-name1>..<path-disambiguatorN><path-nameN> | |
354 | // | |
355 | // With additional tags for DefPathData::Impl and DefPathData::ForeignMod. For instance: | |
356 | // | |
357 | // pub type Type1 = impl Send; | |
358 | // let _: Type1 = <Struct1<i32>>::foo; | |
359 | // fn foo1(_: Type1) { } | |
360 | // | |
361 | // pub type Type2 = impl Send; | |
362 | // let _: Type2 = <Trait1<i32>>::foo; | |
363 | // fn foo2(_: Type2) { } | |
364 | // | |
365 | // pub type Type3 = impl Send; | |
366 | // let _: Type3 = <i32 as Trait1<i32>>::foo; | |
367 | // fn foo3(_: Type3) { } | |
368 | // | |
369 | // pub type Type4 = impl Send; | |
370 | // let _: Type4 = <Struct1<i32> as Trait1<i32>>::foo; | |
371 | // fn foo3(_: Type4) { } | |
372 | // | |
373 | // Are encoded as: | |
374 | // | |
375 | // _ZTSFvu29NvNIC1234_5crate8{{impl}}3fooIu3i32EE | |
376 | // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3dynIu21NtC1234_5crate6Trait1Iu3i32Eu6regionES_EE | |
377 | // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3i32S_EE | |
378 | // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu22NtC1234_5crate7Struct1Iu3i32ES_EE | |
379 | // | |
380 | // The reason for not using v0's extended form of paths is to use a consistent and simpler | |
381 | // encoding, as the reasoning for using it isn't relevand for type metadata identifiers (i.e., | |
382 | // keep symbol names close to how methods are represented in error messages). See | |
383 | // https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html#methods. | |
384 | let mut s = String::new(); | |
385 | ||
386 | // Start and namespace tags | |
387 | let mut def_path = tcx.def_path(def_id); | |
388 | def_path.data.reverse(); | |
389 | for disambiguated_data in &def_path.data { | |
390 | s.push('N'); | |
391 | s.push_str(match disambiguated_data.data { | |
392 | hir::definitions::DefPathData::Impl => "I", // Not specified in v0's <namespace> | |
393 | hir::definitions::DefPathData::ForeignMod => "F", // Not specified in v0's <namespace> | |
394 | hir::definitions::DefPathData::TypeNs(..) => "t", | |
395 | hir::definitions::DefPathData::ValueNs(..) => "v", | |
396 | hir::definitions::DefPathData::ClosureExpr => "C", | |
397 | hir::definitions::DefPathData::Ctor => "c", | |
398 | hir::definitions::DefPathData::AnonConst => "k", | |
399 | hir::definitions::DefPathData::ImplTrait => "i", | |
400 | hir::definitions::DefPathData::CrateRoot | |
401 | | hir::definitions::DefPathData::Use | |
402 | | hir::definitions::DefPathData::GlobalAsm | |
403 | | hir::definitions::DefPathData::MacroNs(..) | |
404 | | hir::definitions::DefPathData::LifetimeNs(..) => { | |
405 | bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data); | |
406 | } | |
407 | }); | |
408 | } | |
409 | ||
410 | // Crate disambiguator and name | |
411 | s.push('C'); | |
412 | s.push_str(&to_disambiguator(tcx.stable_crate_id(def_path.krate).to_u64())); | |
413 | let crate_name = tcx.crate_name(def_path.krate).to_string(); | |
414 | let _ = write!(s, "{}{}", crate_name.len(), &crate_name); | |
415 | ||
416 | // Disambiguators and names | |
417 | def_path.data.reverse(); | |
418 | for disambiguated_data in &def_path.data { | |
419 | let num = disambiguated_data.disambiguator as u64; | |
420 | if num > 0 { | |
421 | s.push_str(&to_disambiguator(num)); | |
422 | } | |
423 | ||
424 | let name = disambiguated_data.data.to_string(); | |
425 | let _ = write!(s, "{}", name.len()); | |
426 | ||
427 | // Prepend a '_' if name starts with a digit or '_' | |
428 | if let Some(first) = name.as_bytes().get(0) { | |
429 | if first.is_ascii_digit() || *first == b'_' { | |
430 | s.push('_'); | |
431 | } | |
432 | } else { | |
433 | bug!("encode_ty_name: invalid name `{:?}`", name); | |
434 | } | |
435 | ||
436 | s.push_str(&name); | |
437 | } | |
438 | ||
439 | s | |
440 | } | |
441 | ||
442 | /// Encodes a ty:Ty using the Itanium C++ ABI with vendor extended type qualifiers and types for | |
443 | /// Rust types that are not used at the FFI boundary. | |
444 | fn encode_ty<'tcx>( | |
445 | tcx: TyCtxt<'tcx>, | |
446 | ty: Ty<'tcx>, | |
447 | dict: &mut FxHashMap<DictKey<'tcx>, usize>, | |
448 | options: EncodeTyOptions, | |
449 | ) -> String { | |
450 | let mut typeid = String::new(); | |
451 | ||
452 | match ty.kind() { | |
453 | // Primitive types | |
454 | ty::Bool => { | |
455 | typeid.push('b'); | |
456 | } | |
457 | ||
458 | ty::Int(..) | ty::Uint(..) | ty::Float(..) => { | |
459 | // u<length><type-name> as vendor extended type | |
460 | let mut s = String::from(match ty.kind() { | |
461 | ty::Int(IntTy::I8) => "u2i8", | |
462 | ty::Int(IntTy::I16) => "u3i16", | |
463 | ty::Int(IntTy::I32) => "u3i32", | |
464 | ty::Int(IntTy::I64) => "u3i64", | |
465 | ty::Int(IntTy::I128) => "u4i128", | |
466 | ty::Int(IntTy::Isize) => "u5isize", | |
467 | ty::Uint(UintTy::U8) => "u2u8", | |
468 | ty::Uint(UintTy::U16) => "u3u16", | |
469 | ty::Uint(UintTy::U32) => "u3u32", | |
470 | ty::Uint(UintTy::U64) => "u3u64", | |
471 | ty::Uint(UintTy::U128) => "u4u128", | |
472 | ty::Uint(UintTy::Usize) => "u5usize", | |
473 | ty::Float(FloatTy::F32) => "u3f32", | |
474 | ty::Float(FloatTy::F64) => "u3f64", | |
475 | _ => "", | |
476 | }); | |
477 | compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); | |
478 | typeid.push_str(&s); | |
479 | } | |
480 | ||
481 | ty::Char => { | |
482 | // u4char as vendor extended type | |
483 | let mut s = String::from("u4char"); | |
484 | compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); | |
485 | typeid.push_str(&s); | |
486 | } | |
487 | ||
488 | ty::Str => { | |
489 | // u3str as vendor extended type | |
490 | let mut s = String::from("u3str"); | |
491 | compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); | |
492 | typeid.push_str(&s); | |
493 | } | |
494 | ||
495 | ty::Never => { | |
496 | // u5never as vendor extended type | |
497 | let mut s = String::from("u5never"); | |
498 | compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); | |
499 | typeid.push_str(&s); | |
500 | } | |
501 | ||
502 | // Compound types | |
503 | // () in Rust is equivalent to void return type in C | |
504 | _ if ty.is_unit() => { | |
505 | typeid.push('v'); | |
506 | } | |
507 | ||
508 | // Sequence types | |
509 | ty::Tuple(tys) => { | |
510 | // u5tupleI<element-type1..element-typeN>E as vendor extended type | |
511 | let mut s = String::from("u5tupleI"); | |
512 | for ty in tys.iter() { | |
513 | s.push_str(&encode_ty(tcx, ty, dict, options)); | |
514 | } | |
515 | s.push('E'); | |
516 | compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); | |
517 | typeid.push_str(&s); | |
518 | } | |
519 | ||
520 | ty::Array(ty0, len) => { | |
521 | // A<array-length><element-type> | |
522 | let mut s = String::from("A"); | |
523 | let _ = write!(s, "{}", &len.kind().try_to_scalar().unwrap().to_u64().unwrap()); | |
524 | s.push_str(&encode_ty(tcx, *ty0, dict, options)); | |
525 | compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); | |
526 | typeid.push_str(&s); | |
527 | } | |
528 | ||
529 | ty::Slice(ty0) => { | |
530 | // u5sliceI<element-type>E as vendor extended type | |
531 | let mut s = String::from("u5sliceI"); | |
532 | s.push_str(&encode_ty(tcx, *ty0, dict, options)); | |
533 | s.push('E'); | |
534 | compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); | |
535 | typeid.push_str(&s); | |
536 | } | |
537 | ||
538 | // User-defined types | |
539 | ty::Adt(adt_def, substs) => { | |
540 | let mut s = String::new(); | |
541 | let def_id = adt_def.0.did; | |
542 | if options.contains(EncodeTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() { | |
2b03887a | 543 | // For cross-language CFI support, the encoding must be compatible at the FFI |
064997fb FG |
544 | // boundary. For instance: |
545 | // | |
546 | // struct type1 {}; | |
547 | // void foo(struct type1* bar) {} | |
548 | // | |
549 | // Is encoded as: | |
550 | // | |
551 | // _ZTSFvP5type1E | |
552 | // | |
553 | // So, encode any repr(C) user-defined type for extern function types with the "C" | |
554 | // calling convention (or extern types [i.e., ty::Foreign]) as <length><name>, where | |
555 | // <name> is <unscoped-name>. | |
556 | let name = tcx.item_name(def_id).to_string(); | |
557 | let _ = write!(s, "{}{}", name.len(), &name); | |
558 | compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); | |
559 | } else { | |
560 | // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is | |
561 | // <subst>, as vendor extended type. | |
562 | let name = encode_ty_name(tcx, def_id); | |
563 | let _ = write!(s, "u{}{}", name.len(), &name); | |
564 | s.push_str(&encode_substs(tcx, substs, dict, options)); | |
565 | compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); | |
566 | } | |
567 | typeid.push_str(&s); | |
568 | } | |
569 | ||
570 | ty::Foreign(def_id) => { | |
571 | // <length><name>, where <name> is <unscoped-name> | |
572 | let mut s = String::new(); | |
573 | let name = tcx.item_name(*def_id).to_string(); | |
574 | let _ = write!(s, "{}{}", name.len(), &name); | |
575 | compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); | |
576 | typeid.push_str(&s); | |
577 | } | |
578 | ||
579 | // Function types | |
580 | ty::FnDef(def_id, substs) | |
581 | | ty::Closure(def_id, substs) | |
582 | | ty::Generator(def_id, substs, ..) => { | |
583 | // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>, | |
584 | // as vendor extended type. | |
585 | let mut s = String::new(); | |
586 | let name = encode_ty_name(tcx, *def_id); | |
587 | let _ = write!(s, "u{}{}", name.len(), &name); | |
588 | s.push_str(&encode_substs(tcx, substs, dict, options)); | |
589 | compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); | |
590 | typeid.push_str(&s); | |
591 | } | |
592 | ||
593 | // Pointer types | |
594 | ty::Ref(region, ty0, ..) => { | |
595 | // [U3mut]u3refI<element-type>E as vendor extended type qualifier and type | |
596 | let mut s = String::new(); | |
597 | s.push_str("u3refI"); | |
598 | s.push_str(&encode_ty(tcx, *ty0, dict, options)); | |
599 | s.push('E'); | |
600 | compress(dict, DictKey::Ty(tcx.mk_imm_ref(*region, *ty0), TyQ::None), &mut s); | |
601 | if ty.is_mutable_ptr() { | |
602 | s = format!("{}{}", "U3mut", &s); | |
603 | compress(dict, DictKey::Ty(ty, TyQ::Mut), &mut s); | |
604 | } | |
605 | typeid.push_str(&s); | |
606 | } | |
607 | ||
608 | ty::RawPtr(tm) => { | |
609 | // P[K]<element-type> | |
610 | let mut s = String::new(); | |
611 | s.push_str(&encode_ty(tcx, tm.ty, dict, options)); | |
612 | if !ty.is_mutable_ptr() { | |
613 | s = format!("{}{}", "K", &s); | |
614 | compress(dict, DictKey::Ty(tm.ty, TyQ::Const), &mut s); | |
615 | }; | |
616 | s = format!("{}{}", "P", &s); | |
617 | compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); | |
618 | typeid.push_str(&s); | |
619 | } | |
620 | ||
621 | ty::FnPtr(fn_sig) => { | |
622 | // PF<return-type><parameter-type1..parameter-typeN>E | |
623 | let mut s = String::from("P"); | |
624 | s.push_str(&encode_fnsig(tcx, &fn_sig.skip_binder(), dict, TypeIdOptions::NO_OPTIONS)); | |
625 | compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); | |
626 | typeid.push_str(&s); | |
627 | } | |
628 | ||
629 | // Trait types | |
f2b60f7d | 630 | ty::Dynamic(predicates, region, kind) => { |
064997fb FG |
631 | // u3dynI<element-type1[..element-typeN]>E, where <element-type> is <predicate>, as |
632 | // vendor extended type. | |
f2b60f7d FG |
633 | let mut s = String::from(match kind { |
634 | ty::Dyn => "u3dynI", | |
635 | ty::DynStar => "u7dynstarI", | |
636 | }); | |
064997fb FG |
637 | s.push_str(&encode_predicates(tcx, predicates, dict, options)); |
638 | s.push_str(&encode_region(tcx, *region, dict, options)); | |
639 | s.push('E'); | |
640 | compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); | |
641 | typeid.push_str(&s); | |
642 | } | |
643 | ||
644 | // Unexpected types | |
645 | ty::Bound(..) | |
646 | | ty::Error(..) | |
647 | | ty::GeneratorWitness(..) | |
648 | | ty::Infer(..) | |
649 | | ty::Opaque(..) | |
650 | | ty::Param(..) | |
651 | | ty::Placeholder(..) | |
652 | | ty::Projection(..) => { | |
653 | bug!("encode_ty: unexpected `{:?}`", ty.kind()); | |
654 | } | |
655 | }; | |
656 | ||
657 | typeid | |
658 | } | |
659 | ||
660 | // Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms all | |
661 | // c_void types into unit types unconditionally, and generalizes all pointers if | |
662 | // TransformTyOptions::GENERALIZE_POINTERS option is set. | |
663 | fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptions) -> Ty<'tcx> { | |
664 | let mut ty = ty; | |
665 | ||
666 | match ty.kind() { | |
667 | ty::Bool | |
668 | | ty::Int(..) | |
669 | | ty::Uint(..) | |
670 | | ty::Float(..) | |
671 | | ty::Char | |
672 | | ty::Str | |
673 | | ty::Never | |
674 | | ty::Foreign(..) | |
675 | | ty::Dynamic(..) => {} | |
676 | ||
677 | _ if ty.is_unit() => {} | |
678 | ||
679 | ty::Tuple(tys) => { | |
680 | ty = tcx.mk_tup(tys.iter().map(|ty| transform_ty(tcx, ty, options))); | |
681 | } | |
682 | ||
683 | ty::Array(ty0, len) => { | |
684 | let len = len.kind().try_to_scalar().unwrap().to_u64().unwrap(); | |
685 | ty = tcx.mk_array(transform_ty(tcx, *ty0, options), len); | |
686 | } | |
687 | ||
688 | ty::Slice(ty0) => { | |
689 | ty = tcx.mk_slice(transform_ty(tcx, *ty0, options)); | |
690 | } | |
691 | ||
692 | ty::Adt(adt_def, substs) => { | |
693 | if is_c_void_ty(tcx, ty) { | |
694 | ty = tcx.mk_unit(); | |
695 | } else if options.contains(TransformTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() | |
696 | { | |
697 | ty = tcx.mk_adt(*adt_def, ty::List::empty()); | |
698 | } else if adt_def.repr().transparent() && adt_def.is_struct() { | |
699 | let variant = adt_def.non_enum_variant(); | |
700 | let param_env = tcx.param_env(variant.def_id); | |
701 | let field = variant.fields.iter().find(|field| { | |
702 | let ty = tcx.type_of(field.did); | |
703 | let is_zst = | |
704 | tcx.layout_of(param_env.and(ty)).map_or(false, |layout| layout.is_zst()); | |
705 | !is_zst | |
706 | }); | |
707 | if field.is_none() { | |
708 | // Transform repr(transparent) types without non-ZST field into () | |
709 | ty = tcx.mk_unit(); | |
710 | } else { | |
711 | let ty0 = tcx.type_of(field.unwrap().did); | |
712 | // Generalize any repr(transparent) user-defined type that is either a pointer | |
713 | // or reference, and either references itself or any other type that contains or | |
714 | // references itself, to avoid a reference cycle. | |
715 | if ty0.is_any_ptr() && ty0.contains(ty) { | |
716 | ty = transform_ty( | |
717 | tcx, | |
718 | ty0, | |
719 | options | TransformTyOptions::GENERALIZE_POINTERS, | |
720 | ); | |
721 | } else { | |
722 | ty = transform_ty(tcx, ty0, options); | |
723 | } | |
724 | } | |
725 | } else { | |
726 | ty = tcx.mk_adt(*adt_def, transform_substs(tcx, substs, options)); | |
727 | } | |
728 | } | |
729 | ||
730 | ty::FnDef(def_id, substs) => { | |
731 | ty = tcx.mk_fn_def(*def_id, transform_substs(tcx, substs, options)); | |
732 | } | |
733 | ||
734 | ty::Closure(def_id, substs) => { | |
735 | ty = tcx.mk_closure(*def_id, transform_substs(tcx, substs, options)); | |
736 | } | |
737 | ||
738 | ty::Generator(def_id, substs, movability) => { | |
739 | ty = tcx.mk_generator(*def_id, transform_substs(tcx, substs, options), *movability); | |
740 | } | |
741 | ||
742 | ty::Ref(region, ty0, ..) => { | |
743 | if options.contains(TransformTyOptions::GENERALIZE_POINTERS) { | |
744 | if ty.is_mutable_ptr() { | |
745 | ty = tcx.mk_mut_ref(tcx.lifetimes.re_static, tcx.mk_unit()); | |
746 | } else { | |
747 | ty = tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_unit()); | |
748 | } | |
749 | } else { | |
750 | if ty.is_mutable_ptr() { | |
751 | ty = tcx.mk_mut_ref(*region, transform_ty(tcx, *ty0, options)); | |
752 | } else { | |
753 | ty = tcx.mk_imm_ref(*region, transform_ty(tcx, *ty0, options)); | |
754 | } | |
755 | } | |
756 | } | |
757 | ||
758 | ty::RawPtr(tm) => { | |
759 | if options.contains(TransformTyOptions::GENERALIZE_POINTERS) { | |
760 | if ty.is_mutable_ptr() { | |
761 | ty = tcx.mk_mut_ptr(tcx.mk_unit()); | |
762 | } else { | |
763 | ty = tcx.mk_imm_ptr(tcx.mk_unit()); | |
764 | } | |
765 | } else { | |
766 | if ty.is_mutable_ptr() { | |
767 | ty = tcx.mk_mut_ptr(transform_ty(tcx, tm.ty, options)); | |
768 | } else { | |
769 | ty = tcx.mk_imm_ptr(transform_ty(tcx, tm.ty, options)); | |
770 | } | |
771 | } | |
772 | } | |
773 | ||
774 | ty::FnPtr(fn_sig) => { | |
775 | if options.contains(TransformTyOptions::GENERALIZE_POINTERS) { | |
776 | ty = tcx.mk_imm_ptr(tcx.mk_unit()); | |
777 | } else { | |
778 | let parameters: Vec<Ty<'tcx>> = fn_sig | |
779 | .skip_binder() | |
780 | .inputs() | |
781 | .iter() | |
782 | .map(|ty| transform_ty(tcx, *ty, options)) | |
783 | .collect(); | |
784 | let output = transform_ty(tcx, fn_sig.skip_binder().output(), options); | |
785 | ty = tcx.mk_fn_ptr(ty::Binder::bind_with_vars( | |
786 | tcx.mk_fn_sig( | |
787 | parameters.iter(), | |
788 | &output, | |
789 | fn_sig.c_variadic(), | |
790 | fn_sig.unsafety(), | |
791 | fn_sig.abi(), | |
792 | ), | |
793 | fn_sig.bound_vars(), | |
794 | )); | |
795 | } | |
796 | } | |
797 | ||
798 | ty::Bound(..) | |
799 | | ty::Error(..) | |
800 | | ty::GeneratorWitness(..) | |
801 | | ty::Infer(..) | |
802 | | ty::Opaque(..) | |
803 | | ty::Param(..) | |
804 | | ty::Placeholder(..) | |
805 | | ty::Projection(..) => { | |
806 | bug!("transform_ty: unexpected `{:?}`", ty.kind()); | |
807 | } | |
808 | } | |
809 | ||
810 | ty | |
811 | } | |
812 | ||
813 | /// Transforms substs for being encoded and used in the substitution dictionary. | |
814 | fn transform_substs<'tcx>( | |
815 | tcx: TyCtxt<'tcx>, | |
816 | substs: SubstsRef<'tcx>, | |
817 | options: TransformTyOptions, | |
818 | ) -> SubstsRef<'tcx> { | |
819 | let substs: Vec<GenericArg<'tcx>> = substs | |
820 | .iter() | |
821 | .map(|subst| { | |
822 | if let GenericArgKind::Type(ty) = subst.unpack() { | |
823 | if is_c_void_ty(tcx, ty) { | |
824 | tcx.mk_unit().into() | |
825 | } else { | |
826 | transform_ty(tcx, ty, options).into() | |
827 | } | |
828 | } else { | |
829 | subst | |
830 | } | |
831 | }) | |
832 | .collect(); | |
833 | tcx.mk_substs(substs.iter()) | |
834 | } | |
835 | ||
836 | /// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor | |
837 | /// extended type qualifiers and types for Rust types that are not used at the FFI boundary. | |
838 | pub fn typeid_for_fnabi<'tcx>( | |
839 | tcx: TyCtxt<'tcx>, | |
840 | fn_abi: &FnAbi<'tcx, Ty<'tcx>>, | |
841 | options: TypeIdOptions, | |
842 | ) -> String { | |
843 | // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions | |
844 | // its type. | |
845 | let mut typeid = String::from("_Z"); | |
846 | ||
847 | // Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type | |
848 | // metadata identifiers for function pointers. The typeinfo name encoding is a two-character | |
849 | // code (i.e., 'TS') prefixed to the type encoding for the function. | |
850 | typeid.push_str("TS"); | |
851 | ||
852 | // Function types are delimited by an "F..E" pair | |
853 | typeid.push('F'); | |
854 | ||
855 | // A dictionary of substitution candidates used for compression (see | |
856 | // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression). | |
857 | let mut dict: FxHashMap<DictKey<'tcx>, usize> = FxHashMap::default(); | |
858 | ||
859 | let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits()) | |
860 | .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); | |
861 | match fn_abi.conv { | |
862 | Conv::C => { | |
863 | encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C); | |
864 | } | |
865 | _ => { | |
866 | encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C); | |
867 | } | |
868 | } | |
869 | ||
870 | // Encode the return type | |
871 | let transform_ty_options = TransformTyOptions::from_bits(options.bits()) | |
872 | .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); | |
873 | let ty = transform_ty(tcx, fn_abi.ret.layout.ty, transform_ty_options); | |
874 | typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); | |
875 | ||
876 | // Encode the parameter types | |
877 | if !fn_abi.c_variadic { | |
878 | if !fn_abi.args.is_empty() { | |
879 | for arg in fn_abi.args.iter() { | |
880 | let ty = transform_ty(tcx, arg.layout.ty, transform_ty_options); | |
881 | typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); | |
882 | } | |
883 | } else { | |
884 | // Empty parameter lists, whether declared as () or conventionally as (void), are | |
885 | // encoded with a void parameter specifier "v". | |
886 | typeid.push('v'); | |
887 | } | |
888 | } else { | |
f2b60f7d | 889 | for n in 0..fn_abi.fixed_count as usize { |
064997fb FG |
890 | let ty = transform_ty(tcx, fn_abi.args[n].layout.ty, transform_ty_options); |
891 | typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); | |
892 | } | |
893 | ||
894 | typeid.push('z'); | |
895 | } | |
896 | ||
897 | // Close the "F..E" pair | |
898 | typeid.push('E'); | |
899 | ||
900 | typeid | |
901 | } | |
902 | ||
903 | /// Returns a type metadata identifier for the specified FnSig using the Itanium C++ ABI with vendor | |
904 | /// extended type qualifiers and types for Rust types that are not used at the FFI boundary. | |
905 | pub fn typeid_for_fnsig<'tcx>( | |
906 | tcx: TyCtxt<'tcx>, | |
907 | fn_sig: &FnSig<'tcx>, | |
908 | options: TypeIdOptions, | |
909 | ) -> String { | |
910 | // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions | |
911 | // its type. | |
912 | let mut typeid = String::from("_Z"); | |
913 | ||
914 | // Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type | |
915 | // metadata identifiers for function pointers. The typeinfo name encoding is a two-character | |
916 | // code (i.e., 'TS') prefixed to the type encoding for the function. | |
917 | typeid.push_str("TS"); | |
918 | ||
919 | // A dictionary of substitution candidates used for compression (see | |
920 | // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression). | |
921 | let mut dict: FxHashMap<DictKey<'tcx>, usize> = FxHashMap::default(); | |
922 | ||
923 | // Encode the function signature | |
924 | typeid.push_str(&encode_fnsig(tcx, fn_sig, &mut dict, options)); | |
925 | ||
926 | typeid | |
927 | } |