Branch data Line data Source code
1 : : // Copyright (C) 2020-2024 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 : : #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 : 57 : std::string as_string () const
51 : : {
52 : 57 : if (prefix == "N")
53 : 64 : return generic_prefix + prefix + ns + path + disambiguator + ident
54 : 64 : + generic_postfix;
55 : 25 : else if (prefix == "M")
56 : 0 : return prefix + impl_path + impl_type;
57 : 25 : else if (prefix == "X")
58 : 0 : return prefix + impl_type + trait_type;
59 : 25 : else if (prefix == "C")
60 : 25 : return prefix + disambiguator + ident;
61 : : else
62 : 0 : 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 : 0 : v0_tuple_prefix (const TyTy::BaseType *ty)
72 : : {
73 : 0 : if (ty->is_unit ())
74 : 0 : return "u";
75 : :
76 : : // FIXME: ARTHUR: Add rest of algorithm
77 : 0 : return "";
78 : : }
79 : :
80 : : static std::string
81 : 8 : v0_numeric_prefix (const TyTy::BaseType *ty)
82 : : {
83 : 8 : 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 : 21 : };
88 : :
89 : 8 : auto ty_kind = ty->get_kind ();
90 : 8 : auto ty_str = ty->as_string ();
91 : 8 : auto numeric_iter = num_prefixes.end ();
92 : :
93 : : // Special numeric types
94 : 8 : if (ty_kind == TyTy::TypeKind::ISIZE)
95 : 0 : return "i";
96 : 8 : else if (ty_kind == TyTy::TypeKind::USIZE)
97 : 2 : return "j";
98 : :
99 : 6 : numeric_iter = num_prefixes.find (ty_str);
100 : 6 : if (numeric_iter != num_prefixes.end ())
101 : 6 : return numeric_iter->second;
102 : :
103 : 0 : rust_unreachable ();
104 : : }
105 : :
106 : : static std::string
107 : 10 : v0_simple_type_prefix (const TyTy::BaseType *ty)
108 : : {
109 : 10 : switch (ty->get_kind ())
110 : : {
111 : 0 : case TyTy::TypeKind::BOOL:
112 : 0 : return "b";
113 : 0 : case TyTy::TypeKind::CHAR:
114 : 0 : return "c";
115 : 0 : case TyTy::TypeKind::STR:
116 : 0 : return "e";
117 : 0 : case TyTy::TypeKind::NEVER:
118 : 0 : return "z";
119 : :
120 : : // Placeholder types
121 : 0 : case TyTy::TypeKind::ERROR: // Fallthrough
122 : 0 : case TyTy::TypeKind::INFER: // Fallthrough
123 : 0 : case TyTy::TypeKind::PLACEHOLDER: // Fallthrough
124 : 0 : case TyTy::TypeKind::PARAM:
125 : : // FIXME: TyTy::TypeKind::BOUND is also a valid variant in rustc
126 : 0 : return "p";
127 : :
128 : 0 : case TyTy::TypeKind::TUPLE:
129 : 0 : return v0_tuple_prefix (ty);
130 : :
131 : 8 : case TyTy::TypeKind::UINT: // Fallthrough
132 : 8 : case TyTy::TypeKind::INT: // Fallthrough
133 : 8 : case TyTy::TypeKind::FLOAT: // Fallthrough
134 : 8 : case TyTy::TypeKind::ISIZE: // Fallthrough
135 : 8 : case TyTy::TypeKind::USIZE:
136 : 8 : return v0_numeric_prefix (ty);
137 : :
138 : 2 : default:
139 : 2 : return "";
140 : : }
141 : :
142 : : rust_unreachable ();
143 : : }
144 : :
145 : : static std::string
146 : 2 : v0_complex_type_prefix (Context *ctx, const TyTy::BaseType *ty)
147 : : {
148 : : // FIXME: ref, slice, dyn, etc.
149 : : // TODO: generics
150 : 2 : switch (ty->get_kind ())
151 : : {
152 : 2 : case TyTy::TypeKind::ADT: {
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 : 2 : v0_integer_62 (uint64_t x)
169 : : {
170 : 2 : std::stringstream s;
171 : 2 : if (x > 0)
172 : 2 : s << base62_integer (x - 1);
173 : :
174 : 2 : s << "_";
175 : 2 : return s.str ();
176 : 2 : }
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 : 2 : return tag + v0_integer_62 (x);
189 : : }
190 : 24 : 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 : 10 : 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 : 56 : 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 (V0Path path, Rust::Compile::Context *ctx,
283 : : const TyTy::BaseType *ty, HIR::Function *fn,
284 : : std::string ident)
285 : : {
286 : 23 : V0Path v0path;
287 : 23 : v0path.prefix = "N";
288 : 23 : v0path.ns = "v";
289 : 23 : v0path.path = path.as_string ();
290 : 23 : v0path.ident = ident;
291 : 23 : if (!fn->get_generic_params ().empty ())
292 : : {
293 : 6 : v0path.generic_prefix = "I";
294 : 12 : v0path.generic_postfix = v0_generic_args (ctx, ty) + "E";
295 : : }
296 : 23 : return v0path;
297 : : }
298 : :
299 : : static V0Path
300 : 6 : v0_scope_path (V0Path path, std::string ident, std::string ns)
301 : : {
302 : 6 : V0Path v0path;
303 : 6 : v0path.prefix = "N";
304 : 6 : v0path.ns = ns;
305 : 6 : v0path.path = path.as_string ();
306 : 6 : v0path.ident = ident;
307 : 6 : return v0path;
308 : : }
309 : :
310 : : static V0Path
311 : 25 : v0_crate_path (CrateNum crate_num, std::string ident)
312 : : {
313 : 25 : V0Path v0path;
314 : 25 : v0path.prefix = "C";
315 : 25 : v0path.disambiguator = v0_disambiguator (crate_num);
316 : 25 : v0path.ident = ident;
317 : 25 : 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 : 1 : v0_closure (V0Path path, HirId closure)
358 : : {
359 : 1 : V0Path v0path;
360 : 1 : v0path.prefix = "N";
361 : 1 : v0path.ns = "C";
362 : 1 : v0path.disambiguator = v0_disambiguator (closure);
363 : 1 : v0path.path = path.as_string ();
364 : 1 : v0path.ident = "0";
365 : 1 : return v0path;
366 : : }
367 : :
368 : : static std::string
369 : 25 : v0_path (Rust::Compile::Context *ctx, const TyTy::BaseType *ty,
370 : : const Resolver::CanonicalPath &cpath)
371 : : {
372 : 25 : auto mappings = Analysis::Mappings::get ();
373 : :
374 : 25 : V0Path v0path = {};
375 : :
376 : 25 : cpath.iterate_segs ([&] (const Resolver::CanonicalPath &seg) {
377 : 57 : HirId hir_id;
378 : 57 : bool ok = mappings->lookup_node_to_hir (seg.get_node_id (), &hir_id);
379 : 57 : if (!ok)
380 : : {
381 : : // FIXME: generic arg in canonical path? (e.g. <i32> in crate::S<i32>)
382 : 0 : rust_unreachable ();
383 : : }
384 : :
385 : 57 : HirId parent_impl_id = UNKNOWN_HIRID;
386 : 57 : HIR::ImplItem *impl_item
387 : 57 : = mappings->lookup_hir_implitem (hir_id, &parent_impl_id);
388 : 57 : HIR::TraitItem *trait_item = mappings->lookup_hir_trait_item (hir_id);
389 : 57 : HIR::Item *item = mappings->lookup_hir_item (hir_id);
390 : 57 : HIR::Expr *expr = mappings->lookup_hir_expr (hir_id);
391 : :
392 : 57 : if (impl_item != nullptr)
393 : : {
394 : 0 : switch (impl_item->get_impl_item_type ())
395 : : {
396 : 0 : case HIR::ImplItem::FUNCTION: {
397 : 0 : HIR::Function *fn = static_cast<HIR::Function *> (impl_item);
398 : 0 : v0path = v0_function_path (v0path, ctx, ty, fn,
399 : 0 : v0_identifier (seg.get ()));
400 : : }
401 : 0 : break;
402 : 0 : case HIR::ImplItem::CONSTANT:
403 : 0 : v0path = v0_scope_path (v0path, v0_identifier (seg.get ()), "v");
404 : 0 : break;
405 : 0 : default:
406 : 0 : rust_internal_error_at (UNDEF_LOCATION, "Attempt to mangle '%s'",
407 : 0 : cpath.get ().c_str ());
408 : : break;
409 : : }
410 : : }
411 : 57 : else if (trait_item != nullptr)
412 : : {
413 : 0 : switch (trait_item->get_item_kind ())
414 : : {
415 : 0 : case HIR::TraitItem::FUNC: {
416 : 0 : HIR::Function *fn = static_cast<HIR::Function *> (impl_item);
417 : 0 : v0path = v0_function_path (v0path, ctx, ty, fn,
418 : 0 : v0_identifier (seg.get ()));
419 : : }
420 : 0 : break;
421 : 0 : case HIR::TraitItem::CONST:
422 : 0 : v0path = v0_scope_path (v0path, v0_identifier (seg.get ()), "v");
423 : 0 : break;
424 : 0 : default:
425 : 0 : rust_internal_error_at (UNDEF_LOCATION, "Attempt to mangle '%s'",
426 : 0 : cpath.get ().c_str ());
427 : : break;
428 : : }
429 : : }
430 : 57 : else if (item != nullptr)
431 : 31 : switch (item->get_item_kind ())
432 : : {
433 : 23 : case HIR::Item::ItemKind::Function: {
434 : 23 : HIR::Function *fn = static_cast<HIR::Function *> (item);
435 : 46 : v0path = v0_function_path (v0path, ctx, ty, fn,
436 : 69 : v0_identifier (seg.get ()));
437 : : }
438 : 23 : break;
439 : 6 : case HIR::Item::ItemKind::Module:
440 : 6 : v0path = v0_scope_path (v0path, v0_identifier (seg.get ()), "t");
441 : 6 : break;
442 : 0 : case HIR::Item::ItemKind::Trait: // FIXME: correct?
443 : 0 : case HIR::Item::ItemKind::Static:
444 : 0 : case HIR::Item::ItemKind::Constant:
445 : 0 : v0path = v0_scope_path (v0path, v0_identifier (seg.get ()), "v");
446 : 0 : break;
447 : 2 : case HIR::Item::ItemKind::Struct:
448 : 2 : case HIR::Item::ItemKind::Enum:
449 : 2 : case HIR::Item::ItemKind::Union:
450 : 2 : v0path = v0_type_path (v0path, v0_identifier (seg.get ()));
451 : 2 : break;
452 : 0 : case HIR::Item::ItemKind::Impl:
453 : : // Trait impl or inherent impl.
454 : 0 : {
455 : 0 : HIR::ImplBlock *impl_block = static_cast<HIR::ImplBlock *> (item);
456 : 0 : v0path = v0_inherent_or_trait_impl_path (ctx, impl_block);
457 : : }
458 : 0 : break;
459 : 0 : case HIR::Item::ItemKind::ExternBlock:
460 : 0 : case HIR::Item::ItemKind::ExternCrate:
461 : 0 : case HIR::Item::ItemKind::UseDeclaration:
462 : 0 : case HIR::Item::ItemKind::TypeAlias:
463 : 0 : case HIR::Item::ItemKind::EnumItem: // FIXME: correct?
464 : 0 : rust_internal_error_at (UNDEF_LOCATION, "Attempt to mangle '%s'",
465 : 0 : cpath.get ().c_str ());
466 : : break;
467 : : }
468 : 26 : else if (expr != nullptr)
469 : : {
470 : 1 : rust_assert (expr->get_expression_type ()
471 : : == HIR::Expr::ExprType::Closure);
472 : : // Use HIR ID as disambiguator.
473 : 1 : v0path = v0_closure (v0path, hir_id);
474 : : }
475 : : else
476 : : // Not HIR item, impl item, trait impl item, nor expr. Assume a crate.
477 : 25 : v0path
478 : 25 : = v0_crate_path (cpath.get_crate_num (), v0_identifier (seg.get ()));
479 : :
480 : 57 : return true;
481 : : });
482 : :
483 : 25 : return v0path.as_string ();
484 : 25 : }
485 : :
486 : : std::string
487 : 23 : v0_mangle_item (Rust::Compile::Context *ctx, const TyTy::BaseType *ty,
488 : : const Resolver::CanonicalPath &path)
489 : : {
490 : 23 : 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 : 23 : std::stringstream mangled;
499 : 23 : mangled << "_R";
500 : 23 : mangled << v0_path (ctx, ty, path);
501 : :
502 : 23 : rust_debug ("=> %s", mangled.str ().c_str ());
503 : :
504 : 23 : return mangled.str ();
505 : 23 : }
506 : :
507 : : } // namespace Compile
508 : : } // namespace Rust
|