Line data Source code
1 : // Copyright (C) 2020-2026 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
|