]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/rust/backend/rust-mangle-v0.cc
Update copyright years.
[thirdparty/gcc.git] / gcc / rust / backend / rust-mangle-v0.cc
CommitLineData
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
30namespace Rust {
31namespace Compile {
32
33struct 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
66static std::string
67v0_path (Rust::Compile::Context *ctx, const TyTy::BaseType *ty,
68 const Resolver::CanonicalPath &path);
69
70static std::string
71v0_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
80static std::string
81v0_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
106static std::string
107v0_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
145static std::string
146v0_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// "_"
167static std::string
168v0_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) + '_'
183static std::string
184v0_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
193static std::string
194v0_disambiguator (uint64_t dis)
195{
196 return v0_opt_integer_62 ("s", dis);
197}
198
199static std::string
200v0_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
215static std::string
216v0_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.
231static std::string
232v0_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
269static V0Path
270v0_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
281static V0Path
282v0_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
299static V0Path
300v0_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
310static V0Path
311v0_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
320static V0Path
321v0_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
356static V0Path
357v0_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
368static std::string
369v0_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
486std::string
487v0_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