LCOV - code coverage report
Current view: top level - gcc/rust/resolve - rust-forever-stack.hxx (source / functions) Coverage Total Hit
Test: gcc.info Lines: 81.9 % 248 203
Test Date: 2026-08-22 16:33:35 Functions: 74.5 % 94 70
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 "expected.h"
      20              : #include "rust-ast.h"
      21              : #include "rust-diagnostics.h"
      22              : #include "rust-forever-stack.h"
      23              : #include "rust-edition.h"
      24              : #include "rust-rib.h"
      25              : #include "rust-unwrap-segment.h"
      26              : #include "optional.h"
      27              : 
      28              : namespace Rust {
      29              : namespace Resolver2_0 {
      30              : 
      31              : bool
      32     33591867 : ForeverStackBase::Node::is_root () const
      33              : {
      34      2841301 :   return !parent.has_value ();
      35              : }
      36              : 
      37              : bool
      38        75934 : ForeverStackBase::Node::is_prelude () const
      39              : {
      40        75934 :   return rib_values.kind == Rib::Kind::Prelude;
      41              : }
      42              : 
      43              : bool
      44        19476 : ForeverStackBase::Node::is_leaf () const
      45              : {
      46        19476 :   return children.empty ();
      47              : }
      48              : 
      49              : void
      50          160 : ForeverStackBase::Node::insert_child (Link link, Node child)
      51              : {
      52          160 :   children.insert ({link, child});
      53              : 
      54              :   // Do we want to error if the child already exists? Probably not, right?
      55              :   // That's kinda the point, isn't it. So this method always succeeds, right?
      56          160 : }
      57              : 
      58              : template <Namespace N>
      59              : void
      60     30745908 : ForeverStack<N>::push (Rib::Kind rib_kind, NodeId id,
      61              :                        tl::optional<Identifier> path)
      62              : {
      63     35952024 :   push_inner (rib_kind, Link (id, path));
      64     30745908 : }
      65              : 
      66              : template <Namespace N>
      67              : void
      68     30745908 : ForeverStack<N>::push_inner (Rib::Kind rib_kind, Link link)
      69              : {
      70     30745908 :   if (rib_kind == Rib::Kind::Prelude)
      71              :     {
      72              :       // If you push_inner into the prelude from outside the root, you will pop
      73              :       // back into the root, which could screw up a traversal.
      74        19476 :       rust_assert (&cursor_reference.get () == &root);
      75              :       // Prelude doesn't have an access path
      76        19476 :       rust_assert (!link.path);
      77        19476 :       update_cursor (this->lang_prelude);
      78        19476 :       return;
      79              :     }
      80              :   // If the link does not exist, we create it and emplace a new `Node` with the
      81              :   // current node as its parent. `unordered_map::emplace` returns a pair with
      82              :   // the iterator and a boolean. If the value already exists, the iterator
      83              :   // points to it. Otherwise, it points to the newly emplaced value, so we can
      84              :   // just update our cursor().
      85     61452864 :   auto emplace = cursor ().children.emplace (
      86     30726432 :     std::make_pair (link, Node (rib_kind, link.id, cursor ())));
      87              : 
      88     30726432 :   auto it = emplace.first;
      89     30726432 :   auto existed = !emplace.second;
      90              : 
      91     30912597 :   rust_debug ("inserting link: Link(%d [%s]): existed? %s", link.id,
      92              :               link.path.has_value () ? link.path.value ().as_string ().c_str ()
      93              :                                      : "<anon>",
      94              :               existed ? "yes" : "no");
      95              : 
      96              :   // We update the cursor
      97     30726432 :   update_cursor (it->second);
      98              : }
      99              : 
     100              : template <Namespace N>
     101              : void
     102     30745872 : ForeverStack<N>::pop ()
     103              : {
     104     30745872 :   rust_assert (!cursor ().is_root ());
     105              : 
     106     30745872 :   rust_debug ("popping link");
     107              : 
     108     38859128 :   for (const auto &kv : cursor ().rib (N).get_values ())
     109      8113256 :     rust_debug ("current_rib: k: %s, v: %s", kv.first.c_str (),
     110              :                 kv.second.to_string ().c_str ());
     111              : 
     112     30745872 :   if (cursor ().parent.has_value ())
     113    612759236 :     for (const auto &kv : cursor ().parent.value ().rib (N).get_values ())
     114    582013364 :       rust_debug ("new cursor: k: %s, v: %s", kv.first.c_str (),
     115              :                   kv.second.to_string ().c_str ());
     116              : 
     117     30745872 :   update_cursor (cursor ().parent.value ());
     118     30745872 : }
     119              : 
     120              : static tl::expected<NodeId, DuplicateNameError>
     121      1429555 : insert_inner (Rib &rib, std::string name, Rib::Definition definition)
     122              : {
     123      2859110 :   return rib.insert (name, definition);
     124              : }
     125              : 
     126              : template <Namespace N>
     127              : tl::expected<NodeId, DuplicateNameError>
     128      1370156 : ForeverStack<N>::insert (Identifier name, NodeId node)
     129              : {
     130      1370156 :   auto &innermost_rib = peek ();
     131              : 
     132              :   // So what do we do here - if the Rib has already been pushed in an earlier
     133              :   // pass, we might end up in a situation where it is okay to re-add new names.
     134              :   // Do we just ignore that here? Do we keep track of if the Rib is new or not?
     135              :   // should our cursor have info on the current node like "is it newly pushed"?
     136      1370156 :   return insert_inner (innermost_rib, name.as_string (),
     137      2740312 :                        Rib::Definition::NonShadowable (node));
     138              : }
     139              : 
     140              : template <Namespace N>
     141              : tl::expected<NodeId, DuplicateNameError>
     142        45618 : ForeverStack<N>::insert_shadowable (Identifier name, NodeId node)
     143              : {
     144        45618 :   auto &innermost_rib = peek ();
     145              : 
     146        45618 :   return insert_inner (innermost_rib, name.as_string (),
     147        91236 :                        Rib::Definition::Shadowable (node));
     148              : }
     149              : 
     150              : template <Namespace N>
     151              : tl::expected<NodeId, DuplicateNameError>
     152            0 : ForeverStack<N>::insert_globbed (Identifier name, NodeId node)
     153              : {
     154            0 :   auto &innermost_rib = peek ();
     155              : 
     156            0 :   return insert_inner (innermost_rib, name.as_string (),
     157            0 :                        Rib::Definition::Globbed (node));
     158              : }
     159              : 
     160              : template <Namespace N>
     161              : tl::expected<NodeId, DuplicateNameError>
     162         1275 : ForeverStack<N>::insert_at_root (Identifier name, NodeId node)
     163              : {
     164         1275 :   auto &root_rib = root.rib (N);
     165              : 
     166              :   // inserting in the root of the crate is never a shadowing operation, even for
     167              :   // macros
     168         1275 :   return insert_inner (root_rib, name.as_string (),
     169         2550 :                        Rib::Definition::NonShadowable (node));
     170              : }
     171              : 
     172              : // Specialization for Macros and Labels - where we are allowed to shadow
     173              : // existing definitions
     174              : template <>
     175              : inline tl::expected<NodeId, DuplicateNameError>
     176         2054 : ForeverStack<Namespace::Macros>::insert (Identifier name, NodeId node)
     177              : {
     178         2054 :   return insert_inner (peek (), name.as_string (),
     179         4108 :                        Rib::Definition::Shadowable (node));
     180              : }
     181              : 
     182              : template <>
     183              : inline tl::expected<NodeId, DuplicateNameError>
     184           63 : ForeverStack<Namespace::Labels>::insert (Identifier name, NodeId node)
     185              : {
     186           63 :   return insert_inner (peek (), name.as_string (),
     187          126 :                        Rib::Definition::Shadowable (node));
     188              : }
     189              : 
     190              : template <>
     191              : inline tl::expected<NodeId, DuplicateNameError>
     192         7606 : ForeverStack<Namespace::Types>::insert_variant (Identifier name, NodeId node)
     193              : {
     194         7606 :   return insert_inner (peek (), name.as_string (),
     195        15212 :                        Rib::Definition::NonShadowable (node, true));
     196              : }
     197              : 
     198              : template <>
     199              : inline tl::expected<NodeId, DuplicateNameError>
     200         1301 : ForeverStack<Namespace::Values>::insert_variant (Identifier name, NodeId node)
     201              : {
     202         1301 :   return insert_inner (peek (), name.as_string (),
     203         2602 :                        Rib::Definition::NonShadowable (node, true));
     204              : }
     205              : 
     206              : template <Namespace N>
     207              : inline void
     208         1482 : ForeverStack<N>::insert_lang_prelude (Identifier name, NodeId id)
     209              : {
     210         4446 :   insert_inner (lang_prelude.rib (N), name.as_string (),
     211         2964 :                 Rib::Definition::NonShadowable (id, false));
     212         1482 : }
     213              : 
     214              : template <Namespace N>
     215              : Rib &
     216      1685569 : ForeverStack<N>::peek ()
     217              : {
     218      1631753 :   return cursor ().rib (N);
     219              : }
     220              : 
     221              : template <Namespace N>
     222              : const Rib &
     223              : ForeverStack<N>::peek () const
     224              : {
     225              :   return cursor ().rib (N);
     226              : }
     227              : 
     228              : template <Namespace N>
     229              : void
     230           50 : ForeverStack<N>::reverse_iter (std::function<KeepGoing (Node &)> lambda)
     231              : {
     232          100 :   return reverse_iter (cursor (), lambda);
     233              : }
     234              : 
     235              : template <Namespace N>
     236              : void
     237              : ForeverStack<N>::reverse_iter (
     238              :   std::function<KeepGoing (const Node &)> lambda) const
     239              : {
     240              :   return reverse_iter (cursor (), lambda);
     241              : }
     242              : 
     243              : template <Namespace N>
     244              : void
     245      1137779 : ForeverStack<N>::reverse_iter (Node &start,
     246              :                                std::function<KeepGoing (Node &)> lambda)
     247              : {
     248      1137779 :   auto *tmp = &start;
     249              : 
     250      2667256 :   while (true)
     251              :     {
     252      3805035 :       auto keep_going = lambda (*tmp);
     253      3805035 :       if (keep_going == KeepGoing::No)
     254              :         return;
     255              : 
     256      2741877 :       if (tmp->is_root ())
     257              :         return;
     258              : 
     259      2667256 :       tmp = &tmp->parent.value ();
     260              :     }
     261              : }
     262              : 
     263              : template <Namespace N>
     264              : void
     265              : ForeverStack<N>::reverse_iter (
     266              :   const Node &start, std::function<KeepGoing (const Node &)> lambda) const
     267              : {
     268              :   auto *tmp = &start;
     269              : 
     270              :   while (true)
     271              :     {
     272              :       auto keep_going = lambda (*tmp);
     273              :       if (keep_going == KeepGoing::No)
     274              :         return;
     275              : 
     276              :       if (tmp->is_root ())
     277              :         return;
     278              : 
     279              :       tmp = &tmp->parent.value ();
     280              :     }
     281              : }
     282              : 
     283              : template <Namespace N>
     284              : typename ForeverStack<N>::Node &
     285    156573611 : ForeverStack<N>::cursor ()
     286              : {
     287    155645333 :   return cursor_reference;
     288              : }
     289              : 
     290              : template <Namespace N>
     291              : const typename ForeverStack<N>::Node &
     292              : ForeverStack<N>::cursor () const
     293              : {
     294              :   return cursor_reference;
     295              : }
     296              : 
     297              : template <Namespace N>
     298              : void
     299     30745908 : ForeverStack<N>::update_cursor (Node &new_cursor)
     300              : {
     301     30745908 :   cursor_reference = new_cursor;
     302              : }
     303              : 
     304              : template <Namespace N>
     305              : tl::optional<Rib::Definition>
     306      1122024 : ForeverStack<N>::get (Node &start, const Identifier &name)
     307              : {
     308      1122024 :   tl::optional<Rib::Definition> resolved_definition = tl::nullopt;
     309              : 
     310              :   // TODO: Can we improve the API? have `reverse_iter` return an optional?
     311      4910677 :   reverse_iter (start, [&resolved_definition, &name] (Node &current) {
     312              :     // we can't reference associated types/functions like this
     313      3788653 :     if (current.rib (N).kind == Rib::Kind::TraitOrImpl)
     314              :       return KeepGoing::Yes;
     315              : 
     316      3315280 :     auto candidate = current.rib (N).get (name.as_string ());
     317              : 
     318      3315280 :     if (candidate)
     319              :       {
     320       343818 :         if (candidate->is_variant ())
     321              :           return KeepGoing::Yes;
     322              :         // for most namespaces, we do not need to care about various ribs -
     323              :         // they are available from all contexts if defined in the current
     324              :         // scope, or an outermore one. so if we do have a candidate, we can
     325              :         // return it directly and stop iterating
     326       343813 :         resolved_definition = *candidate;
     327              : 
     328       343813 :         return KeepGoing::No;
     329              :       }
     330              :     else
     331              :       {
     332      2971462 :         if (current.rib (N).kind == Rib::Kind::Module)
     333              :           return KeepGoing::No;
     334              :         else
     335      2267872 :           return KeepGoing::Yes;
     336              :       }
     337      3788653 :   });
     338              : 
     339      1122024 :   return resolved_definition;
     340              : }
     341              : 
     342              : template <Namespace N>
     343              : tl::optional<Rib::Definition>
     344       874863 : ForeverStack<N>::get (const Identifier &name)
     345              : {
     346       874863 :   return get (cursor (), name);
     347              : }
     348              : 
     349              : template <Namespace N>
     350              : tl::optional<Rib::Definition>
     351           11 : ForeverStack<N>::get_lang_prelude (const Identifier &name)
     352              : {
     353           11 :   return lang_prelude.rib (N).get (name.as_string ());
     354              : }
     355              : 
     356              : template <Namespace N>
     357              : tl::optional<Rib::Definition>
     358        90566 : ForeverStack<N>::get_lang_prelude (const std::string &name)
     359              : {
     360        90566 :   return lang_prelude.rib (N).get (name);
     361              : }
     362              : 
     363              : template <Namespace N>
     364              : tl::optional<Rib::Definition>
     365              : ForeverStack<N>::get_from_prelude (NodeId prelude, const Identifier &name)
     366              : {
     367              :   auto starting_point = dfs_node (root, prelude);
     368              :   if (!starting_point)
     369              :     return tl::nullopt;
     370              : 
     371              :   return get (*starting_point, name);
     372              : }
     373              : 
     374              : template <>
     375           50 : tl::optional<Rib::Definition> inline ForeverStack<Namespace::Labels>::get (
     376              :   const Identifier &name)
     377              : {
     378           50 :   tl::optional<Rib::Definition> resolved_definition = tl::nullopt;
     379              : 
     380           50 :   reverse_iter ([&resolved_definition, &name] (Node &current) {
     381              :     // looking up for labels cannot go through function ribs
     382              :     // TODO: What other ribs?
     383          187 :     if (current.rib_labels.kind == Rib::Kind::Function)
     384              :       return KeepGoing::No;
     385              : 
     386          182 :     auto candidate = current.rib_labels.get (name.as_string ());
     387              : 
     388              :     // FIXME: Factor this in a function with the generic `get`
     389          182 :     return candidate.map_or (
     390          409 :       [&resolved_definition] (Rib::Definition found) {
     391           45 :         resolved_definition = found;
     392              : 
     393           45 :         return KeepGoing::No;
     394              :       },
     395          182 :       KeepGoing::Yes);
     396          187 :   });
     397              : 
     398           50 :   return resolved_definition;
     399              : }
     400              : 
     401              : /* Check if an iterator points to the last element */
     402              : template <typename I, typename C>
     403              : static bool
     404       144631 : is_last (const I &iterator, const C &collection)
     405              : {
     406       235880 :   return iterator + 1 == collection.end ();
     407              : }
     408              : 
     409              : /* Check if an iterator points to the start of the collection */
     410              : template <typename I, typename C>
     411              : static bool
     412       334897 : is_start (const I &iterator, const C &collection)
     413              : {
     414       334897 :   return iterator == collection.begin ();
     415              : }
     416              : 
     417              : template <Namespace N>
     418              : typename ForeverStack<N>::Node &
     419        15705 : ForeverStack<N>::find_closest_module (Node &starting_point)
     420              : {
     421        15705 :   auto *closest_module = &starting_point;
     422              : 
     423        31900 :   reverse_iter (starting_point, [&closest_module] (Node &current) {
     424        16195 :     if (current.rib (N).kind == Rib::Kind::Module || current.is_root ())
     425              :       {
     426        15705 :         closest_module = &current;
     427        15705 :         return KeepGoing::No;
     428              :       }
     429              : 
     430              :     return KeepGoing::Yes;
     431              :   });
     432              : 
     433        15705 :   return *closest_module;
     434              : }
     435              : 
     436              : /* If a the given condition is met, emit an error about misused leading path
     437              :  * segments */
     438              : static inline bool
     439       177657 : check_leading_kw_at_start (std::vector<Error> &collect_errors,
     440              :                            const ResolutionPath::Segment &segment,
     441              :                            bool condition)
     442              : {
     443       177657 :   if (condition)
     444           11 :     collect_errors.emplace_back (
     445           11 :       segment.locus, ErrorCode::E0433,
     446           11 :       "%qs in paths can only be used in start position", segment.name.c_str ());
     447              : 
     448       177657 :   return condition;
     449              : }
     450              : 
     451              : // we first need to handle the "starting" segments - `super`, `self` or
     452              : // `crate`. we don't need to do anything for `self` and can just skip it. for
     453              : // `crate`, we need to go back to the root of the current stack. for each
     454              : // `super` segment, we go back to the cursor's parent until we reach the
     455              : // correct one or the root.
     456              : template <Namespace N>
     457              : tl::optional<typename std::vector<ResolutionPath::Segment>::const_iterator>
     458        80795 : ForeverStack<N>::find_starting_point (
     459              :   const std::vector<ResolutionPath::Segment> &segments,
     460              :   std::reference_wrapper<Node> &starting_point,
     461              :   std::function<void (Usage, Definition, Namespace)> insert_segment_resolution,
     462              :   std::vector<Error> &collect_errors)
     463              : {
     464        80795 :   auto iterator = segments.begin ();
     465              : 
     466        91255 :   for (; iterator != segments.end (); iterator++)
     467              :     {
     468        91249 :       auto &seg = *iterator;
     469              : 
     470              :       // don't include a final self segment
     471        91249 :       if (is_last (iterator, segments) && seg.is_lower_self_seg ())
     472              :         break;
     473              : 
     474        91249 :       bool is_self_or_crate
     475        91249 :         = seg.is_crate_path_seg () || seg.is_lower_self_seg ();
     476              : 
     477              :       // if we're after the first path segment and meet `self` or `crate`, it's
     478              :       // an error - we should only be seeing `super` keywords at this point
     479        91249 :       if (check_leading_kw_at_start (collect_errors, seg,
     480        91249 :                                      !is_start (iterator, segments)
     481              :                                        && is_self_or_crate))
     482            1 :         return tl::nullopt;
     483              : 
     484        91248 :       if (seg.is_crate_path_seg ())
     485              :         {
     486        29820 :           starting_point = root;
     487        29820 :           insert_segment_resolution (Usage (seg.node_id),
     488        29820 :                                      Definition (starting_point.get ().id), N);
     489        29820 :           iterator++;
     490        29820 :           break;
     491              :         }
     492        61428 :       if (seg.is_lower_self_seg ())
     493              :         {
     494              :           // insert segment resolution
     495         5767 :           starting_point = find_closest_module (starting_point);
     496         5767 :           insert_segment_resolution (Usage (seg.node_id),
     497         5767 :                                      Definition (starting_point.get ().id), N);
     498              :           // don't exit -- we could see some "super" segments
     499         5767 :           continue;
     500              :         }
     501        55661 :       if (seg.is_super_path_seg ())
     502              :         {
     503         4694 :           starting_point = find_closest_module (starting_point);
     504         4694 :           if (starting_point.get ().is_root ())
     505              :             {
     506            1 :               collect_errors.emplace_back (
     507            1 :                 seg.locus, ErrorCode::E0433,
     508              :                 "too many leading %<super%> keywords");
     509            1 :               return tl::nullopt;
     510              :             }
     511              : 
     512         4693 :           starting_point
     513         4693 :             = find_closest_module (starting_point.get ().parent.value ());
     514              : 
     515         4693 :           insert_segment_resolution (Usage (seg.node_id),
     516         4693 :                                      Definition (starting_point.get ().id), N);
     517         4693 :           continue;
     518              :         }
     519              : 
     520              :       // now we've gone through the allowed `crate`, `self` or leading `super`
     521              :       // segments. we can start resolving each segment itself.
     522              :       // if we do see another leading segment, then we can error out.
     523              :       break;
     524              :     }
     525              : 
     526        80793 :   return iterator;
     527              : }
     528              : 
     529              : template <Namespace N>
     530              : tl::optional<typename ForeverStack<N>::DfsResult>
     531              : ForeverStack<N>::dfs (ForeverStack<N>::Node &starting_point, NodeId to_find)
     532              : {
     533              :   auto values = starting_point.rib (N).get_values ();
     534              : 
     535              :   for (auto &kv : values)
     536              :     {
     537              :       for (auto id : kv.second.ids_shadowable)
     538              :         if (id == to_find)
     539              :           return {{starting_point, kv.first}};
     540              :       for (auto id : kv.second.ids_non_shadowable)
     541              :         if (id == to_find)
     542              :           return {{starting_point, kv.first}};
     543              :       for (auto id : kv.second.ids_globbed)
     544              :         if (id == to_find)
     545              :           return {{starting_point, kv.first}};
     546              :     }
     547              : 
     548              :   for (auto &child : starting_point.children)
     549              :     {
     550              :       auto candidate = dfs (child.second, to_find);
     551              : 
     552              :       if (candidate.has_value ())
     553              :         return candidate;
     554              :     }
     555              : 
     556              :   return tl::nullopt;
     557              : }
     558              : 
     559              : template <Namespace N>
     560              : tl::optional<typename ForeverStack<N>::ConstDfsResult>
     561              : ForeverStack<N>::dfs (const ForeverStack<N>::Node &starting_point,
     562              :                       NodeId to_find) const
     563              : {
     564              :   auto values = starting_point.rib (N).get_values ();
     565              : 
     566              :   for (auto &kv : values)
     567              :     {
     568              :       for (auto id : kv.second.ids_shadowable)
     569              :         if (id == to_find)
     570              :           return {{starting_point, kv.first}};
     571              :       for (auto id : kv.second.ids_non_shadowable)
     572              :         if (id == to_find)
     573              :           return {{starting_point, kv.first}};
     574              :       for (auto id : kv.second.ids_globbed)
     575              :         if (id == to_find)
     576              :           return {{starting_point, kv.first}};
     577              :     }
     578              : 
     579              :   for (auto &child : starting_point.children)
     580              :     {
     581              :       auto candidate = dfs (child.second, to_find);
     582              : 
     583              :       if (candidate.has_value ())
     584              :         return candidate;
     585              :     }
     586              : 
     587              :   return tl::nullopt;
     588              : }
     589              : 
     590              : template <Namespace N>
     591              : tl::optional<Rib &>
     592        48925 : ForeverStack<N>::dfs_rib (ForeverStack<N>::Node &starting_point, NodeId to_find)
     593              : {
     594        97450 :   return dfs_node (starting_point, to_find).map ([] (Node &x) -> Rib & {
     595        48525 :     return x.rib (N);
     596        48925 :   });
     597              : }
     598              : 
     599              : template <Namespace N>
     600              : tl::optional<const Rib &>
     601              : ForeverStack<N>::dfs_rib (const ForeverStack<N>::Node &starting_point,
     602              :                           NodeId to_find) const
     603              : {
     604              :   return dfs_node (starting_point, to_find)
     605              :     .map ([] (const Node &x) -> const Rib & { return x.rib (N); });
     606              : }
     607              : 
     608              : template <Namespace N>
     609              : tl::optional<typename ForeverStack<N>::Node &>
     610      4620373 : ForeverStack<N>::dfs_node (ForeverStack<N>::Node &starting_point,
     611              :                            NodeId to_find)
     612              : {
     613      4620373 :   if (auto found = check_cache (to_find))
     614        79937 :     return found;
     615              : 
     616      4540436 :   if (starting_point.id == to_find)
     617              :     {
     618          718 :       cache (to_find, starting_point);
     619              : 
     620          718 :       return starting_point;
     621              :     }
     622              : 
     623      9077379 :   for (auto &child : starting_point.children)
     624              :     {
     625      4539318 :       auto candidate = dfs_node (child.second, to_find);
     626              : 
     627      4539318 :       if (candidate.has_value ())
     628         1657 :         return candidate;
     629              :     }
     630              : 
     631      4538061 :   return tl::nullopt;
     632              : }
     633              : 
     634              : template <Namespace N>
     635              : tl::optional<const typename ForeverStack<N>::Node &>
     636          131 : ForeverStack<N>::dfs_node (const ForeverStack<N>::Node &starting_point,
     637              :                            NodeId to_find) const
     638              : {
     639          131 :   if (starting_point.id == to_find)
     640           16 :     return starting_point;
     641              : 
     642          189 :   for (auto &child : starting_point.children)
     643              :     {
     644          103 :       auto candidate = dfs_node (child.second, to_find);
     645              : 
     646          103 :       if (candidate.has_value ())
     647           29 :         return candidate;
     648              :     }
     649              : 
     650           86 :   return tl::nullopt;
     651              : }
     652              : 
     653              : template <Namespace N>
     654              : tl::optional<typename ForeverStack<N>::Node &>
     655      4620373 : ForeverStack<N>::check_cache (NodeId to_find)
     656              : {
     657      4620373 :   auto entry = dfs_cache.find (to_find);
     658              : 
     659      4620373 :   if (entry != dfs_cache.end ())
     660        79937 :     return entry->second;
     661              : 
     662      4540436 :   return tl::nullopt;
     663              : }
     664              : 
     665              : template <Namespace N>
     666              : void
     667          718 : ForeverStack<N>::cache (NodeId found, typename ForeverStack<N>::Node &result)
     668              : {
     669          718 :   dfs_cache.insert ({found, result});
     670              : }
     671              : 
     672              : template <Namespace N>
     673              : tl::optional<Rib &>
     674           52 : ForeverStack<N>::to_rib (NodeId rib_id)
     675              : {
     676           52 :   return dfs_rib (root, rib_id);
     677              : }
     678              : 
     679              : template <Namespace N>
     680              : tl::optional<const Rib &>
     681              : ForeverStack<N>::to_rib (NodeId rib_id) const
     682              : {
     683              :   return dfs_rib (root, rib_id);
     684              : }
     685              : 
     686              : template <Namespace N>
     687              : void
     688            0 : ForeverStack<N>::stream_rib (std::stringstream &stream, const Rib &rib,
     689              :                              const std::string &next,
     690              :                              const std::string &next_next) const
     691              : {
     692            0 :   std::string rib_kind = Rib::kind_to_string (rib.kind);
     693            0 :   stream << next << "rib [" << rib_kind << "]: {";
     694            0 :   if (rib.get_values ().empty ())
     695              :     {
     696            0 :       stream << "}\n";
     697            0 :       return;
     698              :     }
     699              :   else
     700              :     {
     701            0 :       stream << "\n";
     702              :     }
     703              : 
     704            0 :   for (const auto &kv : rib.get_values ())
     705            0 :     stream << next_next << kv.first << ": " << kv.second.to_string () << "\n";
     706              : 
     707            0 :   stream << next << "},\n";
     708            0 : }
     709              : 
     710              : template <Namespace N>
     711              : void
     712            0 : ForeverStack<N>::stream_node (std::stringstream &stream, unsigned indentation,
     713              :                               const ForeverStack<N>::Node &node,
     714              :                               unsigned depth) const
     715              : {
     716            0 :   auto indent = std::string (indentation, ' ');
     717            0 :   auto next = std::string (indentation + 4, ' ');
     718            0 :   auto next_next = std::string (indentation + 8, ' ');
     719              : 
     720              :   stream << indent << "Node {\n"
     721            0 :          << next << "is_root: " << (node.is_root () ? "true" : "false") << ",\n"
     722            0 :          << next << "is_leaf: " << (node.is_leaf () ? "true" : "false")
     723            0 :          << ",\n";
     724              : 
     725            0 :   stream_rib (stream, node.rib (N), next, next_next);
     726              : 
     727            0 :   stream << indent << "}\n";
     728              : 
     729            0 :   for (auto &kv : node.children)
     730              :     {
     731            0 :       auto link = kv.first;
     732            0 :       auto child = kv.second;
     733            0 :       stream << indent << "Link " << depth << " (" << link.id << ", "
     734            0 :              << (link.path.has_value () ? link.path.value ().as_string ()
     735            0 :                                         : "<anon>")
     736            0 :              << "):\n";
     737              : 
     738            0 :       stream_node (stream, indentation + 4, child, depth + 1);
     739              : 
     740            0 :       stream << '\n';
     741              :     }
     742            0 : }
     743              : 
     744              : template <Namespace N>
     745              : std::string
     746            0 : ForeverStack<N>::as_debug_string () const
     747              : {
     748            0 :   std::stringstream stream;
     749              : 
     750            0 :   stream_node (stream, 0, root);
     751              : 
     752            0 :   return stream.str ();
     753            0 : }
     754              : 
     755              : template <Namespace N>
     756              : bool
     757           14 : ForeverStack<N>::is_module_descendant (NodeId parent, NodeId child) const
     758              : {
     759           14 :   return dfs_node (dfs_node (root, parent).value (), child).has_value ();
     760              : }
     761              : 
     762              : static tl::expected<Definition, LookupFinalizeError>
     763       103002 : find_leaf_definition_inner (const Usage &key,
     764              :                             const std::map<Usage, Definition> &resolved_nodes,
     765              :                             std::set<Usage> &keys_seen)
     766              : {
     767       103002 :   auto original_definition = resolved_nodes.find (key);
     768       103002 :   auto possible_import = Usage (original_definition->second.id);
     769              : 
     770       103002 :   if (original_definition == resolved_nodes.end ())
     771       103002 :     return tl::make_unexpected (LookupFinalizeError::NoDefinition);
     772              : 
     773            0 :   if (!keys_seen.insert (key).second)
     774            0 :     return tl::make_unexpected (LookupFinalizeError::Loop);
     775              : 
     776            0 :   if (resolved_nodes.find (possible_import) == resolved_nodes.end ())
     777            0 :     return original_definition->second;
     778              : 
     779              :   // We're dealing with an import - a reference to another
     780              :   // definition. Go through the chain and update the original key's
     781              :   // corresponding definition.
     782            0 :   return find_leaf_definition_inner (possible_import, resolved_nodes,
     783            0 :                                      keys_seen);
     784              : }
     785              : 
     786              : template <Namespace N>
     787              : tl::expected<Definition, LookupFinalizeError>
     788       103002 : ForeverStack<N>::find_leaf_definition (const NodeId &key) const
     789              : {
     790       103002 :   std::set<Usage> keys_seen;
     791              : 
     792       103002 :   return find_leaf_definition_inner (Usage (key), resolved_nodes, keys_seen);
     793       103002 : }
     794              : 
     795              : #if 0
     796              : template <Namespace N>
     797              : void
     798              : ForeverStack<N>::flatten ()
     799              : {
     800              :   for (auto &k_v : resolved_nodes)
     801              :     {
     802              :       // Loop detection
     803              :       auto keys_seen = std::set<Usage> ();
     804              : 
     805              :       auto result
     806              :         = find_leaf_definition_inner (k_v.first, resolved_nodes, keys_seen);
     807              : 
     808              :       if (!result)
     809              :         {
     810              :           // Trigger an ICE if we haven't found a definition because that's
     811              :           // really weird
     812              :           rust_assert (result.error () != LookupFinalizeError::NoDefinition);
     813              : 
     814              :           // FIXME: This needs to be improved and tested, but later
     815              :           rust_error_at (UNDEF_LOCATION, "import loop");
     816              :           continue;
     817              :         }
     818              : 
     819              :       // Replace the Definition for this Usage in the map. This may be a no-op.
     820              :       k_v.second = result.value ();
     821              :     }
     822              : }
     823              : #endif
     824              : 
     825              : // FIXME: Can we add selftests?
     826              : 
     827              : } // namespace Resolver2_0
     828              : } // 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.