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