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 : 114 : std::string as_string () const
50 : : {
51 : 114 : if (prefix == "N")
52 : 128 : return generic_prefix + prefix + ns + path + disambiguator + ident
53 : 128 : + generic_postfix;
54 : 50 : else if (prefix == "M")
55 : 0 : return prefix + impl_path + impl_type;
56 : 50 : else if (prefix == "X")
57 : 0 : return prefix + impl_type + trait_type;
58 : 50 : else if (prefix == "C")
59 : 50 : return prefix + disambiguator + ident;
60 : : else
61 : 0 : rust_unreachable ();
62 : : }
63 : : };
64 : :
65 : : static std::string
66 : : v0_path (Rust::Compile::Context *ctx, 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 : 16 : v0_numeric_prefix (const TyTy::BaseType *ty)
81 : : {
82 : 16 : 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 : 42 : };
87 : :
88 : 16 : auto ty_kind = ty->get_kind ();
89 : 16 : auto ty_str = ty->as_string ();
90 : 16 : auto numeric_iter = num_prefixes.end ();
91 : :
92 : : // Special numeric types
93 : 16 : if (ty_kind == TyTy::TypeKind::ISIZE)
94 : 0 : return "i";
95 : 16 : else if (ty_kind == TyTy::TypeKind::USIZE)
96 : 4 : return "j";
97 : :
98 : 12 : numeric_iter = num_prefixes.find (ty_str);
99 : 12 : if (numeric_iter != num_prefixes.end ())
100 : 12 : return numeric_iter->second;
101 : :
102 : 0 : rust_unreachable ();
103 : 16 : }
104 : :
105 : : static std::string
106 : 20 : v0_simple_type_prefix (const TyTy::BaseType *ty)
107 : : {
108 : 20 : 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 : 16 : case TyTy::TypeKind::UINT: // Fallthrough
131 : 16 : case TyTy::TypeKind::INT: // Fallthrough
132 : 16 : case TyTy::TypeKind::FLOAT: // Fallthrough
133 : 16 : case TyTy::TypeKind::ISIZE: // Fallthrough
134 : 16 : case TyTy::TypeKind::USIZE:
135 : 16 : return v0_numeric_prefix (ty);
136 : :
137 : 4 : default:
138 : 4 : return "";
139 : : }
140 : :
141 : : rust_unreachable ();
142 : : }
143 : :
144 : : static std::string
145 : 4 : v0_complex_type_prefix (Context *ctx, const TyTy::BaseType *ty)
146 : : {
147 : : // FIXME: ref, slice, dyn, etc.
148 : : // TODO: generics
149 : 4 : switch (ty->get_kind ())
150 : : {
151 : 4 : case TyTy::TypeKind::ADT: {
152 : 4 : const TyTy::ADTType *adt = static_cast<const TyTy::ADTType *> (ty);
153 : 4 : return v0_path (ctx, ty, adt->get_ident ().path);
154 : : }
155 : 0 : break;
156 : 0 : default:
157 : 0 : return "";
158 : : }
159 : : }
160 : :
161 : : // Returns an underscore-terminated base62 integer.
162 : : // This corresponds to the `<base-62-number>` grammar in the v0 mangling RFC:
163 : : // - 0 is encoded as "_"
164 : : // - any other value is encoded as itself minus one in base 62, followed by
165 : : // "_"
166 : : static std::string
167 : 2 : v0_integer_62 (uint64_t x)
168 : : {
169 : 2 : std::stringstream s;
170 : 2 : if (x > 0)
171 : 4 : s << base62_integer (x - 1);
172 : :
173 : 2 : s << "_";
174 : 2 : return s.str ();
175 : 2 : }
176 : :
177 : : // Returns a tag-prefixed base62 integer when the
178 : : // integer is greater than 0:
179 : : // - 0 is encoded as "" (nothing)
180 : : // - any other value is encoded as <tag> + v0_integer_62(itself), that is
181 : : // <tag> + base62(itself - 1) + '_'
182 : : static std::string
183 : 52 : v0_opt_integer_62 (std::string tag, uint64_t x)
184 : : {
185 : 52 : if (x > 0)
186 : : {
187 : 2 : return tag + v0_integer_62 (x);
188 : : }
189 : 50 : return "";
190 : : }
191 : :
192 : : static std::string
193 : 52 : v0_disambiguator (uint64_t dis)
194 : : {
195 : 52 : return v0_opt_integer_62 ("s", dis);
196 : : }
197 : :
198 : : static std::string
199 : 20 : v0_type_prefix (Context *ctx, const TyTy::BaseType *ty)
200 : : {
201 : 20 : std::string ty_prefix;
202 : :
203 : 20 : ty_prefix = v0_simple_type_prefix (ty);
204 : 20 : if (!ty_prefix.empty ())
205 : : return ty_prefix;
206 : :
207 : 4 : ty_prefix = v0_complex_type_prefix (ctx, ty);
208 : 4 : if (!ty_prefix.empty ())
209 : : return ty_prefix;
210 : :
211 : 0 : rust_unreachable ();
212 : : }
213 : :
214 : : static std::string
215 : 12 : v0_generic_args (Context *ctx, const TyTy::BaseType *ty)
216 : : {
217 : 12 : std::stringstream ss;
218 : 12 : const TyTy::FnType *fnty = static_cast<const TyTy::FnType *> (ty);
219 : 12 : TyTy::SubstitutionArgumentMappings &subst_ref
220 : 12 : = const_cast<TyTy::FnType *> (fnty)->get_substitution_arguments ();
221 : 32 : for (TyTy::SubstitutionArg &map : subst_ref.get_mappings ())
222 : : {
223 : 40 : ss << v0_type_prefix (ctx, map.get_tyty ());
224 : : }
225 : 12 : return ss.str ();
226 : 12 : }
227 : :
228 : : // Returns an mangled identifier. This corresponds to the
229 : : // `<identifier>` grammar in the v0 mangling RFC.
230 : : static std::string
231 : 112 : v0_identifier (const std::string &identifier)
232 : : {
233 : 112 : std::stringstream mangled;
234 : : // The grammar for unicode identifier is contained in
235 : : // <undisambiguated-identifier>, right under the <identifier> one. If the
236 : : // identifier contains unicode values, then an extra "u" needs to be added to
237 : : // the mangling string and `punycode` must be used to encode the characters.
238 : :
239 : 112 : if (!is_ascii_only (identifier))
240 : 12 : mangled << "u";
241 : :
242 : 112 : tl::optional<Utf8String> uident_opt
243 : 112 : = Utf8String::make_utf8_string (identifier);
244 : 112 : rust_assert (uident_opt.has_value ());
245 : 112 : tl::optional<std::string> punycode_opt
246 : 112 : = encode_punycode (uident_opt.value ());
247 : 112 : rust_assert (punycode_opt.has_value ());
248 : :
249 : 112 : std::string punycode = punycode_opt.value ();
250 : :
251 : : // remove a tailing hyphen
252 : 112 : if (punycode.back () == '-')
253 : 100 : punycode.pop_back ();
254 : :
255 : : // replace a hyphen in punycode with a underscore
256 : 112 : std::replace (punycode.begin (), punycode.end (), '-', '_');
257 : :
258 : 224 : mangled << std::to_string (punycode.size ());
259 : :
260 : : // Add extra '_'
261 : 112 : if (punycode[0] == '_' || ('0' <= punycode[0] && punycode[0] <= '9'))
262 : 12 : mangled << "_";
263 : :
264 : 112 : mangled << punycode;
265 : 112 : return mangled.str ();
266 : 336 : }
267 : :
268 : : static V0Path
269 : 4 : v0_type_path (V0Path path, std::string ident)
270 : : {
271 : 4 : V0Path v0path;
272 : 4 : v0path.prefix = "N";
273 : 4 : v0path.ns = "t";
274 : 4 : v0path.path = path.as_string ();
275 : 4 : v0path.ident = ident;
276 : : // TODO: Need <generic-arg>?
277 : 4 : return v0path;
278 : : }
279 : :
280 : : static V0Path
281 : 46 : v0_function_path (
282 : : V0Path path, Rust::Compile::Context *ctx, const TyTy::BaseType *ty,
283 : : const std::vector<std::unique_ptr<HIR::GenericParam>> &generic_params,
284 : : std::string ident)
285 : : {
286 : 46 : V0Path v0path;
287 : 46 : v0path.prefix = "N";
288 : 46 : v0path.ns = "v";
289 : 46 : v0path.path = path.as_string ();
290 : 46 : v0path.ident = ident;
291 : 46 : if (!generic_params.empty ())
292 : : {
293 : 12 : v0path.generic_prefix = "I";
294 : 24 : v0path.generic_postfix = v0_generic_args (ctx, ty) + "E";
295 : : }
296 : 46 : return v0path;
297 : : }
298 : :
299 : : static V0Path
300 : 12 : v0_scope_path (V0Path path, std::string ident, std::string ns)
301 : : {
302 : 12 : V0Path v0path;
303 : 12 : v0path.prefix = "N";
304 : 12 : v0path.ns = ns;
305 : 12 : v0path.path = path.as_string ();
306 : 12 : v0path.ident = ident;
307 : 12 : return v0path;
308 : : }
309 : :
310 : : static V0Path
311 : 50 : v0_crate_path (CrateNum crate_num, std::string ident)
312 : : {
313 : 50 : V0Path v0path;
314 : 50 : v0path.prefix = "C";
315 : 50 : v0path.disambiguator = v0_disambiguator (crate_num);
316 : 50 : v0path.ident = ident;
317 : 50 : return v0path;
318 : : }
319 : :
320 : : static V0Path
321 : 0 : v0_inherent_or_trait_impl_path (Rust::Compile::Context *ctx,
322 : : HIR::ImplBlock *impl_block)
323 : : {
324 : 0 : V0Path v0path;
325 : 0 : bool ok;
326 : :
327 : : // lookup impl type
328 : 0 : TyTy::BaseType *impl_ty = nullptr;
329 : 0 : ok = ctx->get_tyctx ()->lookup_type (
330 : 0 : impl_block->get_type ().get_mappings ().get_hirid (), &impl_ty);
331 : 0 : rust_assert (ok);
332 : :
333 : : // FIXME: dummy value for now
334 : 0 : v0path.impl_path = "C5crate";
335 : 0 : v0path.impl_type = v0_type_prefix (ctx, impl_ty);
336 : :
337 : 0 : if (impl_block->has_trait_ref ())
338 : : {
339 : : // trait impl: X <impl-path> <type> <path>
340 : 0 : v0path.prefix = "X";
341 : :
342 : 0 : TyTy::BaseType *trait_ty = nullptr;
343 : 0 : ok = ctx->get_tyctx ()->lookup_type (
344 : 0 : impl_block->get_trait_ref ().get_mappings ().get_hirid (), &trait_ty);
345 : 0 : rust_assert (ok);
346 : :
347 : 0 : v0path.trait_type = v0_type_prefix (ctx, trait_ty);
348 : : }
349 : : else
350 : : // inherent impl: M <impl-path> <type>
351 : 0 : v0path.prefix = "M";
352 : :
353 : 0 : return v0path;
354 : : }
355 : :
356 : : static V0Path
357 : 2 : v0_closure (V0Path path, HirId closure)
358 : : {
359 : 2 : V0Path v0path;
360 : 2 : v0path.prefix = "N";
361 : 2 : v0path.ns = "C";
362 : 2 : v0path.disambiguator = v0_disambiguator (closure);
363 : 2 : v0path.path = path.as_string ();
364 : 2 : v0path.ident = "0";
365 : 2 : return v0path;
366 : : }
367 : :
368 : : static std::string
369 : 50 : v0_path (Rust::Compile::Context *ctx, const TyTy::BaseType *ty,
370 : : const Resolver::CanonicalPath &cpath)
371 : : {
372 : 50 : auto &mappings = Analysis::Mappings::get ();
373 : :
374 : 50 : V0Path v0path = {};
375 : :
376 : 50 : cpath.iterate_segs ([&] (const Resolver::CanonicalPath &seg) {
377 : 114 : tl::optional<HirId> hid = mappings.lookup_node_to_hir (seg.get_node_id ());
378 : 114 : if (!hid.has_value ())
379 : : {
380 : : // FIXME: generic arg in canonical path? (e.g. <i32> in crate::S<i32>)
381 : 0 : rust_unreachable ();
382 : : }
383 : :
384 : 114 : auto hir_id = hid.value ();
385 : :
386 : 114 : if (auto impl_item = mappings.lookup_hir_implitem (hir_id))
387 : : {
388 : 0 : switch (impl_item->first->get_impl_item_type ())
389 : : {
390 : 0 : case HIR::ImplItem::FUNCTION: {
391 : 0 : HIR::Function *fn
392 : 0 : = static_cast<HIR::Function *> (impl_item->first);
393 : 0 : v0path
394 : 0 : = v0_function_path (v0path, ctx, ty, fn->get_generic_params (),
395 : 0 : v0_identifier (seg.get ()));
396 : : }
397 : 0 : break;
398 : 0 : case HIR::ImplItem::CONSTANT:
399 : 0 : v0path = v0_scope_path (v0path, v0_identifier (seg.get ()), "v");
400 : 0 : break;
401 : 0 : default:
402 : 0 : rust_internal_error_at (UNDEF_LOCATION, "Attempt to mangle '%s'",
403 : 0 : cpath.get ().c_str ());
404 : : break;
405 : : }
406 : : }
407 : 114 : else if (auto trait_item = mappings.lookup_hir_trait_item (hir_id))
408 : : {
409 : 0 : switch (trait_item.value ()->get_item_kind ())
410 : : {
411 : 0 : case HIR::TraitItem::FUNC: {
412 : 0 : auto fn = static_cast<HIR::TraitItemFunc *> (*trait_item);
413 : 0 : rust_unreachable ();
414 : : v0path = v0_function_path (v0path, ctx, ty,
415 : : fn->get_decl ().get_generic_params (),
416 : : v0_identifier (seg.get ()));
417 : : }
418 : : break;
419 : 0 : case HIR::TraitItem::CONST:
420 : 0 : v0path = v0_scope_path (v0path, v0_identifier (seg.get ()), "v");
421 : 0 : break;
422 : 0 : default:
423 : 0 : rust_internal_error_at (UNDEF_LOCATION, "Attempt to mangle '%s'",
424 : 0 : cpath.get ().c_str ());
425 : 0 : break;
426 : : }
427 : : }
428 : 114 : else if (auto item = mappings.lookup_hir_item (hir_id))
429 : 62 : switch (item.value ()->get_item_kind ())
430 : : {
431 : 46 : case HIR::Item::ItemKind::Function: {
432 : 46 : HIR::Function *fn = static_cast<HIR::Function *> (*item);
433 : 46 : v0path
434 : 92 : = v0_function_path (v0path, ctx, ty, fn->get_generic_params (),
435 : 138 : v0_identifier (seg.get ()));
436 : : }
437 : 46 : break;
438 : 12 : case HIR::Item::ItemKind::Module:
439 : 12 : v0path = v0_scope_path (v0path, v0_identifier (seg.get ()), "t");
440 : 12 : break;
441 : 0 : case HIR::Item::ItemKind::Trait: // FIXME: correct?
442 : 0 : case HIR::Item::ItemKind::Static:
443 : 0 : case HIR::Item::ItemKind::Constant:
444 : 0 : v0path = v0_scope_path (v0path, v0_identifier (seg.get ()), "v");
445 : 0 : break;
446 : 4 : case HIR::Item::ItemKind::Struct:
447 : 4 : case HIR::Item::ItemKind::Enum:
448 : 4 : case HIR::Item::ItemKind::Union:
449 : 4 : v0path = v0_type_path (v0path, v0_identifier (seg.get ()));
450 : 4 : break;
451 : 0 : case HIR::Item::ItemKind::Impl:
452 : : // Trait impl or inherent impl.
453 : 0 : {
454 : 0 : HIR::ImplBlock *impl_block = static_cast<HIR::ImplBlock *> (*item);
455 : 0 : v0path = v0_inherent_or_trait_impl_path (ctx, impl_block);
456 : : }
457 : 0 : break;
458 : 0 : case HIR::Item::ItemKind::ExternBlock:
459 : 0 : case HIR::Item::ItemKind::ExternCrate:
460 : 0 : case HIR::Item::ItemKind::UseDeclaration:
461 : 0 : case HIR::Item::ItemKind::TypeAlias:
462 : 0 : case HIR::Item::ItemKind::EnumItem: // FIXME: correct?
463 : 0 : rust_internal_error_at (UNDEF_LOCATION, "Attempt to mangle '%s'",
464 : 0 : cpath.get ().c_str ());
465 : : break;
466 : : }
467 : 52 : else if (auto expr = mappings.lookup_hir_expr (hir_id))
468 : : {
469 : 2 : rust_assert (expr.value ()->get_expression_type ()
470 : : == HIR::Expr::ExprType::Closure);
471 : : // Use HIR ID as disambiguator.
472 : 2 : v0path = v0_closure (v0path, hir_id);
473 : : }
474 : : else
475 : : // Not HIR item, impl item, trait impl item, nor expr. Assume a crate.
476 : 50 : v0path
477 : 50 : = v0_crate_path (cpath.get_crate_num (), v0_identifier (seg.get ()));
478 : :
479 : 114 : return true;
480 : : });
481 : :
482 : 50 : return v0path.as_string ();
483 : 50 : }
484 : :
485 : : std::string
486 : 46 : v0_mangle_item (Rust::Compile::Context *ctx, const TyTy::BaseType *ty,
487 : : const Resolver::CanonicalPath &path)
488 : : {
489 : 46 : rust_debug ("Start mangling: %s", path.get ().c_str ());
490 : :
491 : : // TODO: get Instanciating CrateNum
492 : : // auto &mappings = Analysis::Mappings::get ();
493 : : // std::string crate_name;
494 : : // bool ok = mappings->get_crate_name (path.get_crate_num (), crate_name);
495 : : // rust_assert (ok);
496 : :
497 : 46 : std::stringstream mangled;
498 : 46 : mangled << "_R";
499 : 92 : mangled << v0_path (ctx, ty, path);
500 : :
501 : 46 : rust_debug ("=> %s", mangled.str ().c_str ());
502 : :
503 : 46 : return mangled.str ();
504 : 46 : }
505 : :
506 : : } // namespace Compile
507 : : } // namespace Rust
|