]>
Commit | Line | Data |
---|---|---|
767698ff | 1 | // Copyright (C) 2020-2024 Free Software Foundation, Inc. |
7b80033e RT |
2 | |
3 | // This file is part of GCC. | |
4 | ||
5 | // GCC is free software; you can redistribute it and/or modify it under | |
6 | // the terms of the GNU General Public License as published by the Free | |
7 | // Software Foundation; either version 3, or (at your option) any later | |
8 | // version. | |
9 | ||
10 | // GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |
11 | // WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
13 | // for more details. | |
14 | ||
15 | // You should have received a copy of the GNU General Public License | |
16 | // along with GCC; see the file COPYING3. If not see | |
17 | // <http://www.gnu.org/licenses/>. | |
18 | ||
19 | #include "rust-mangle.h" | |
20 | #include "optional.h" | |
21 | #include "rust-base62.h" | |
22 | #include "rust-diagnostics.h" | |
23 | #include "rust-system.h" | |
24 | #include "rust-tyty.h" | |
25 | #include "rust-unicode.h" | |
26 | #include "rust-punycode.h" | |
27 | #include "rust-compile-type.h" | |
28 | #include <sstream> | |
29 | ||
30 | namespace Rust { | |
31 | namespace Compile { | |
32 | ||
33 | struct V0Path | |
34 | { | |
35 | std::string prefix = ""; | |
36 | // Used for "N" | |
37 | std::string ns = ""; | |
38 | std::string path = ""; | |
39 | // Used for "N" and "C" | |
40 | std::string ident = ""; | |
41 | std::string disambiguator = ""; | |
42 | // Used for "M" and "X" | |
43 | std::string impl_path = ""; | |
44 | std::string impl_type = ""; | |
45 | std::string trait_type = ""; | |
46 | // Used for generic types | |
47 | std::string generic_postfix = ""; | |
48 | std::string generic_prefix = ""; | |
49 | ||
50 | std::string as_string () const | |
51 | { | |
52 | if (prefix == "N") | |
53 | return generic_prefix + prefix + ns + path + disambiguator + ident | |
54 | + generic_postfix; | |
55 | else if (prefix == "M") | |
56 | return prefix + impl_path + impl_type; | |
57 | else if (prefix == "X") | |
58 | return prefix + impl_type + trait_type; | |
59 | else if (prefix == "C") | |
60 | return prefix + disambiguator + ident; | |
61 | else | |
62 | rust_unreachable (); | |
63 | } | |
64 | }; | |
65 | ||
66 | static std::string | |
67 | v0_path (Rust::Compile::Context *ctx, const TyTy::BaseType *ty, | |
68 | const Resolver::CanonicalPath &path); | |
69 | ||
70 | static std::string | |
71 | v0_tuple_prefix (const TyTy::BaseType *ty) | |
72 | { | |
73 | if (ty->is_unit ()) | |
74 | return "u"; | |
75 | ||
76 | // FIXME: ARTHUR: Add rest of algorithm | |
77 | return ""; | |
78 | } | |
79 | ||
80 | static std::string | |
81 | v0_numeric_prefix (const TyTy::BaseType *ty) | |
82 | { | |
83 | static const std::map<std::string, std::string> num_prefixes = { | |
84 | {"i8", "a"}, {"u8", "h"}, {"i16", "s"}, {"u16", "t"}, | |
85 | {"i32", "l"}, {"u32", "m"}, {"i64", "x"}, {"u64", "y"}, | |
86 | {"isize", "i"}, {"usize", "j"}, {"f32", "f"}, {"f64", "d"}, | |
87 | }; | |
88 | ||
89 | auto ty_kind = ty->get_kind (); | |
90 | auto ty_str = ty->as_string (); | |
91 | auto numeric_iter = num_prefixes.end (); | |
92 | ||
93 | // Special numeric types | |
94 | if (ty_kind == TyTy::TypeKind::ISIZE) | |
95 | return "i"; | |
96 | else if (ty_kind == TyTy::TypeKind::USIZE) | |
97 | return "j"; | |
98 | ||
99 | numeric_iter = num_prefixes.find (ty_str); | |
100 | if (numeric_iter != num_prefixes.end ()) | |
101 | return numeric_iter->second; | |
102 | ||
103 | rust_unreachable (); | |
104 | } | |
105 | ||
106 | static std::string | |
107 | v0_simple_type_prefix (const TyTy::BaseType *ty) | |
108 | { | |
109 | switch (ty->get_kind ()) | |
110 | { | |
111 | case TyTy::TypeKind::BOOL: | |
112 | return "b"; | |
113 | case TyTy::TypeKind::CHAR: | |
114 | return "c"; | |
115 | case TyTy::TypeKind::STR: | |
116 | return "e"; | |
117 | case TyTy::TypeKind::NEVER: | |
118 | return "z"; | |
119 | ||
120 | // Placeholder types | |
121 | case TyTy::TypeKind::ERROR: // Fallthrough | |
122 | case TyTy::TypeKind::INFER: // Fallthrough | |
123 | case TyTy::TypeKind::PLACEHOLDER: // Fallthrough | |
124 | case TyTy::TypeKind::PARAM: | |
125 | // FIXME: TyTy::TypeKind::BOUND is also a valid variant in rustc | |
126 | return "p"; | |
127 | ||
128 | case TyTy::TypeKind::TUPLE: | |
129 | return v0_tuple_prefix (ty); | |
130 | ||
131 | case TyTy::TypeKind::UINT: // Fallthrough | |
132 | case TyTy::TypeKind::INT: // Fallthrough | |
133 | case TyTy::TypeKind::FLOAT: // Fallthrough | |
134 | case TyTy::TypeKind::ISIZE: // Fallthrough | |
135 | case TyTy::TypeKind::USIZE: | |
136 | return v0_numeric_prefix (ty); | |
137 | ||
138 | default: | |
139 | return ""; | |
140 | } | |
141 | ||
142 | rust_unreachable (); | |
143 | } | |
144 | ||
145 | static std::string | |
146 | v0_complex_type_prefix (Context *ctx, const TyTy::BaseType *ty) | |
147 | { | |
148 | // FIXME: ref, slice, dyn, etc. | |
149 | // TODO: generics | |
150 | switch (ty->get_kind ()) | |
151 | { | |
152 | case TyTy::TypeKind::ADT: { | |
153 | const TyTy::ADTType *adt = static_cast<const TyTy::ADTType *> (ty); | |
154 | return v0_path (ctx, ty, adt->get_ident ().path); | |
155 | } | |
156 | break; | |
157 | default: | |
158 | return ""; | |
159 | } | |
160 | } | |
161 | ||
162 | // Returns an underscore-terminated base62 integer. | |
163 | // This corresponds to the `<base-62-number>` grammar in the v0 mangling RFC: | |
164 | // - 0 is encoded as "_" | |
165 | // - any other value is encoded as itself minus one in base 62, followed by | |
166 | // "_" | |
167 | static std::string | |
168 | v0_integer_62 (uint64_t x) | |
169 | { | |
170 | std::stringstream s; | |
171 | if (x > 0) | |
172 | s << base62_integer (x - 1); | |
173 | ||
174 | s << "_"; | |
175 | return s.str (); | |
176 | } | |
177 | ||
178 | // Returns a tag-prefixed base62 integer when the | |
179 | // integer is greater than 0: | |
180 | // - 0 is encoded as "" (nothing) | |
181 | // - any other value is encoded as <tag> + v0_integer_62(itself), that is | |
182 | // <tag> + base62(itself - 1) + '_' | |
183 | static std::string | |
184 | v0_opt_integer_62 (std::string tag, uint64_t x) | |
185 | { | |
186 | if (x > 0) | |
187 | { | |
188 | return tag + v0_integer_62 (x); | |
189 | } | |
190 | return ""; | |
191 | } | |
192 | ||
193 | static std::string | |
194 | v0_disambiguator (uint64_t dis) | |
195 | { | |
196 | return v0_opt_integer_62 ("s", dis); | |
197 | } | |
198 | ||
199 | static std::string | |
200 | v0_type_prefix (Context *ctx, const TyTy::BaseType *ty) | |
201 | { | |
202 | std::string ty_prefix; | |
203 | ||
204 | ty_prefix = v0_simple_type_prefix (ty); | |
205 | if (!ty_prefix.empty ()) | |
206 | return ty_prefix; | |
207 | ||
208 | ty_prefix = v0_complex_type_prefix (ctx, ty); | |
209 | if (!ty_prefix.empty ()) | |
210 | return ty_prefix; | |
211 | ||
212 | rust_unreachable (); | |
213 | } | |
214 | ||
215 | static std::string | |
216 | v0_generic_args (Context *ctx, const TyTy::BaseType *ty) | |
217 | { | |
218 | std::stringstream ss; | |
219 | const TyTy::FnType *fnty = static_cast<const TyTy::FnType *> (ty); | |
220 | TyTy::SubstitutionArgumentMappings &subst_ref | |
221 | = const_cast<TyTy::FnType *> (fnty)->get_substitution_arguments (); | |
222 | for (TyTy::SubstitutionArg &map : subst_ref.get_mappings ()) | |
223 | { | |
224 | ss << v0_type_prefix (ctx, map.get_tyty ()); | |
225 | } | |
226 | return ss.str (); | |
227 | } | |
228 | ||
229 | // Returns an mangled identifier. This corresponds to the | |
230 | // `<identifier>` grammar in the v0 mangling RFC. | |
231 | static std::string | |
232 | v0_identifier (const std::string &identifier) | |
233 | { | |
234 | std::stringstream mangled; | |
235 | // The grammar for unicode identifier is contained in | |
236 | // <undisambiguated-identifier>, right under the <identifier> one. If the | |
237 | // identifier contains unicode values, then an extra "u" needs to be added to | |
238 | // the mangling string and `punycode` must be used to encode the characters. | |
239 | ||
240 | if (!is_ascii_only (identifier)) | |
241 | mangled << "u"; | |
242 | ||
243 | tl::optional<Utf8String> uident_opt | |
244 | = Utf8String::make_utf8_string (identifier); | |
245 | rust_assert (uident_opt.has_value ()); | |
246 | tl::optional<std::string> punycode_opt | |
247 | = encode_punycode (uident_opt.value ()); | |
248 | rust_assert (punycode_opt.has_value ()); | |
249 | ||
250 | std::string punycode = punycode_opt.value (); | |
251 | ||
252 | // remove a tailing hyphen | |
253 | if (punycode.back () == '-') | |
254 | punycode.pop_back (); | |
255 | ||
256 | // replace a hyphen in punycode with a underscore | |
257 | std::replace (punycode.begin (), punycode.end (), '-', '_'); | |
258 | ||
259 | mangled << std::to_string (punycode.size ()); | |
260 | ||
261 | // Add extra '_' | |
262 | if (punycode[0] == '_' || ('0' <= punycode[0] && punycode[0] <= '9')) | |
263 | mangled << "_"; | |
264 | ||
265 | mangled << punycode; | |
266 | return mangled.str (); | |
267 | } | |
268 | ||
269 | static V0Path | |
270 | v0_type_path (V0Path path, std::string ident) | |
271 | { | |
272 | V0Path v0path; | |
273 | v0path.prefix = "N"; | |
274 | v0path.ns = "t"; | |
275 | v0path.path = path.as_string (); | |
276 | v0path.ident = ident; | |
277 | // TODO: Need <generic-arg>? | |
278 | return v0path; | |
279 | } | |
280 | ||
281 | static V0Path | |
282 | v0_function_path (V0Path path, Rust::Compile::Context *ctx, | |
283 | const TyTy::BaseType *ty, HIR::Function *fn, | |
284 | std::string ident) | |
285 | { | |
286 | V0Path v0path; | |
287 | v0path.prefix = "N"; | |
288 | v0path.ns = "v"; | |
289 | v0path.path = path.as_string (); | |
290 | v0path.ident = ident; | |
291 | if (!fn->get_generic_params ().empty ()) | |
292 | { | |
293 | v0path.generic_prefix = "I"; | |
294 | v0path.generic_postfix = v0_generic_args (ctx, ty) + "E"; | |
295 | } | |
296 | return v0path; | |
297 | } | |
298 | ||
299 | static V0Path | |
300 | v0_scope_path (V0Path path, std::string ident, std::string ns) | |
301 | { | |
302 | V0Path v0path; | |
303 | v0path.prefix = "N"; | |
304 | v0path.ns = ns; | |
305 | v0path.path = path.as_string (); | |
306 | v0path.ident = ident; | |
307 | return v0path; | |
308 | } | |
309 | ||
310 | static V0Path | |
311 | v0_crate_path (CrateNum crate_num, std::string ident) | |
312 | { | |
313 | V0Path v0path; | |
314 | v0path.prefix = "C"; | |
315 | v0path.disambiguator = v0_disambiguator (crate_num); | |
316 | v0path.ident = ident; | |
317 | return v0path; | |
318 | } | |
319 | ||
320 | static V0Path | |
321 | v0_inherent_or_trait_impl_path (Rust::Compile::Context *ctx, | |
322 | HIR::ImplBlock *impl_block) | |
323 | { | |
324 | V0Path v0path; | |
325 | bool ok; | |
326 | ||
327 | // lookup impl type | |
328 | TyTy::BaseType *impl_ty = nullptr; | |
329 | ok = ctx->get_tyctx ()->lookup_type ( | |
330 | impl_block->get_type ()->get_mappings ().get_hirid (), &impl_ty); | |
331 | rust_assert (ok); | |
332 | ||
333 | // FIXME: dummy value for now | |
334 | v0path.impl_path = "C5crate"; | |
335 | v0path.impl_type = v0_type_prefix (ctx, impl_ty); | |
336 | ||
337 | if (impl_block->has_trait_ref ()) | |
338 | { | |
339 | // trait impl: X <impl-path> <type> <path> | |
340 | v0path.prefix = "X"; | |
341 | ||
342 | TyTy::BaseType *trait_ty = nullptr; | |
343 | ok = ctx->get_tyctx ()->lookup_type ( | |
344 | impl_block->get_trait_ref ()->get_mappings ().get_hirid (), &trait_ty); | |
345 | rust_assert (ok); | |
346 | ||
347 | v0path.trait_type = v0_type_prefix (ctx, trait_ty); | |
348 | } | |
349 | else | |
350 | // inherent impl: M <impl-path> <type> | |
351 | v0path.prefix = "M"; | |
352 | ||
353 | return v0path; | |
354 | } | |
355 | ||
356 | static V0Path | |
357 | v0_closure (V0Path path, HirId closure) | |
358 | { | |
359 | V0Path v0path; | |
360 | v0path.prefix = "N"; | |
361 | v0path.ns = "C"; | |
362 | v0path.disambiguator = v0_disambiguator (closure); | |
363 | v0path.path = path.as_string (); | |
364 | v0path.ident = "0"; | |
365 | return v0path; | |
366 | } | |
367 | ||
368 | static std::string | |
369 | v0_path (Rust::Compile::Context *ctx, const TyTy::BaseType *ty, | |
370 | const Resolver::CanonicalPath &cpath) | |
371 | { | |
372 | auto mappings = Analysis::Mappings::get (); | |
373 | ||
374 | V0Path v0path = {}; | |
375 | ||
376 | cpath.iterate_segs ([&] (const Resolver::CanonicalPath &seg) { | |
377 | HirId hir_id; | |
378 | bool ok = mappings->lookup_node_to_hir (seg.get_node_id (), &hir_id); | |
379 | if (!ok) | |
380 | { | |
381 | // FIXME: generic arg in canonical path? (e.g. <i32> in crate::S<i32>) | |
382 | rust_unreachable (); | |
383 | } | |
384 | ||
385 | HirId parent_impl_id = UNKNOWN_HIRID; | |
386 | HIR::ImplItem *impl_item | |
387 | = mappings->lookup_hir_implitem (hir_id, &parent_impl_id); | |
388 | HIR::TraitItem *trait_item = mappings->lookup_hir_trait_item (hir_id); | |
389 | HIR::Item *item = mappings->lookup_hir_item (hir_id); | |
390 | HIR::Expr *expr = mappings->lookup_hir_expr (hir_id); | |
391 | ||
392 | if (impl_item != nullptr) | |
393 | { | |
394 | switch (impl_item->get_impl_item_type ()) | |
395 | { | |
396 | case HIR::ImplItem::FUNCTION: { | |
397 | HIR::Function *fn = static_cast<HIR::Function *> (impl_item); | |
398 | v0path = v0_function_path (v0path, ctx, ty, fn, | |
399 | v0_identifier (seg.get ())); | |
400 | } | |
401 | break; | |
402 | case HIR::ImplItem::CONSTANT: | |
403 | v0path = v0_scope_path (v0path, v0_identifier (seg.get ()), "v"); | |
404 | break; | |
405 | default: | |
406 | rust_internal_error_at (UNDEF_LOCATION, "Attempt to mangle '%s'", | |
407 | cpath.get ().c_str ()); | |
408 | break; | |
409 | } | |
410 | } | |
411 | else if (trait_item != nullptr) | |
412 | { | |
413 | switch (trait_item->get_item_kind ()) | |
414 | { | |
415 | case HIR::TraitItem::FUNC: { | |
416 | HIR::Function *fn = static_cast<HIR::Function *> (impl_item); | |
417 | v0path = v0_function_path (v0path, ctx, ty, fn, | |
418 | v0_identifier (seg.get ())); | |
419 | } | |
420 | break; | |
421 | case HIR::TraitItem::CONST: | |
422 | v0path = v0_scope_path (v0path, v0_identifier (seg.get ()), "v"); | |
423 | break; | |
424 | default: | |
425 | rust_internal_error_at (UNDEF_LOCATION, "Attempt to mangle '%s'", | |
426 | cpath.get ().c_str ()); | |
427 | break; | |
428 | } | |
429 | } | |
430 | else if (item != nullptr) | |
431 | switch (item->get_item_kind ()) | |
432 | { | |
433 | case HIR::Item::ItemKind::Function: { | |
434 | HIR::Function *fn = static_cast<HIR::Function *> (item); | |
435 | v0path = v0_function_path (v0path, ctx, ty, fn, | |
436 | v0_identifier (seg.get ())); | |
437 | } | |
438 | break; | |
439 | case HIR::Item::ItemKind::Module: | |
440 | v0path = v0_scope_path (v0path, v0_identifier (seg.get ()), "t"); | |
441 | break; | |
442 | case HIR::Item::ItemKind::Trait: // FIXME: correct? | |
443 | case HIR::Item::ItemKind::Static: | |
444 | case HIR::Item::ItemKind::Constant: | |
445 | v0path = v0_scope_path (v0path, v0_identifier (seg.get ()), "v"); | |
446 | break; | |
447 | case HIR::Item::ItemKind::Struct: | |
448 | case HIR::Item::ItemKind::Enum: | |
449 | case HIR::Item::ItemKind::Union: | |
450 | v0path = v0_type_path (v0path, v0_identifier (seg.get ())); | |
451 | break; | |
452 | case HIR::Item::ItemKind::Impl: | |
453 | // Trait impl or inherent impl. | |
454 | { | |
455 | HIR::ImplBlock *impl_block = static_cast<HIR::ImplBlock *> (item); | |
456 | v0path = v0_inherent_or_trait_impl_path (ctx, impl_block); | |
457 | } | |
458 | break; | |
459 | case HIR::Item::ItemKind::ExternBlock: | |
460 | case HIR::Item::ItemKind::ExternCrate: | |
461 | case HIR::Item::ItemKind::UseDeclaration: | |
462 | case HIR::Item::ItemKind::TypeAlias: | |
463 | case HIR::Item::ItemKind::EnumItem: // FIXME: correct? | |
464 | rust_internal_error_at (UNDEF_LOCATION, "Attempt to mangle '%s'", | |
465 | cpath.get ().c_str ()); | |
466 | break; | |
467 | } | |
468 | else if (expr != nullptr) | |
469 | { | |
470 | rust_assert (expr->get_expression_type () | |
471 | == HIR::Expr::ExprType::Closure); | |
472 | // Use HIR ID as disambiguator. | |
473 | v0path = v0_closure (v0path, hir_id); | |
474 | } | |
475 | else | |
476 | // Not HIR item, impl item, trait impl item, nor expr. Assume a crate. | |
477 | v0path | |
478 | = v0_crate_path (cpath.get_crate_num (), v0_identifier (seg.get ())); | |
479 | ||
480 | return true; | |
481 | }); | |
482 | ||
483 | return v0path.as_string (); | |
484 | } | |
485 | ||
486 | std::string | |
487 | v0_mangle_item (Rust::Compile::Context *ctx, const TyTy::BaseType *ty, | |
488 | const Resolver::CanonicalPath &path) | |
489 | { | |
490 | rust_debug ("Start mangling: %s", path.get ().c_str ()); | |
491 | ||
492 | // TODO: get Instanciating CrateNum | |
493 | // auto mappings = Analysis::Mappings::get (); | |
494 | // std::string crate_name; | |
495 | // bool ok = mappings->get_crate_name (path.get_crate_num (), crate_name); | |
496 | // rust_assert (ok); | |
497 | ||
498 | std::stringstream mangled; | |
499 | mangled << "_R"; | |
500 | mangled << v0_path (ctx, ty, path); | |
501 | ||
502 | rust_debug ("=> %s", mangled.str ().c_str ()); | |
503 | ||
504 | return mangled.str (); | |
505 | } | |
506 | ||
507 | } // namespace Compile | |
508 | } // namespace Rust |