LCOV - code coverage report
Current view: top level - gcc/rust/backend - rust-mangle-v0.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 64.5 % 256 165
Test Date: 2026-02-28 14:20:25 Functions: 90.0 % 20 18
Legend: Lines:     hit not hit

            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
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.