]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/rust/resolve/rust-forever-stack.hxx
Update copyright years.
[thirdparty/gcc.git] / gcc / rust / resolve / rust-forever-stack.hxx
CommitLineData
767698ff 1// Copyright (C) 2020-2024 Free Software Foundation, Inc.
2327631e
AC
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
79d8fb09
AC
19#include "expected.h"
20#include "rust-ast.h"
21#include "rust-diagnostics.h"
2327631e
AC
22#include "rust-forever-stack.h"
23#include "rust-rib.h"
24#include "optional.h"
25
26namespace Rust {
27namespace Resolver2_0 {
28
29template <Namespace N>
30bool
31ForeverStack<N>::Node::is_root () const
32{
33 return !parent.has_value ();
34}
35
36template <Namespace N>
37bool
38ForeverStack<N>::Node::is_leaf () const
39{
40 return children.empty ();
41}
42
43template <Namespace N>
44void
45ForeverStack<N>::Node::insert_child (Link link, Node child)
46{
47 auto res = children.insert ({link, child});
48
49 // Do we want to error if the child already exists? Probably not, right?
50 // That's kinda the point, isn't it. So this method always succeeds, right?
51}
52
2327631e
AC
53template <Namespace N>
54void
55ForeverStack<N>::push (Rib rib, NodeId id, tl::optional<Identifier> path)
56{
57 push_inner (rib, Link (id, path));
58}
59
60template <Namespace N>
61void
62ForeverStack<N>::push_inner (Rib rib, Link link)
63{
64 // If the link does not exist, we create it and emplace a new `Node` with the
65 // current node as its parent. `unordered_map::emplace` returns a pair with
66 // the iterator and a boolean. If the value already exists, the iterator
67 // points to it. Otherwise, it points to the newly emplaced value, so we can
68 // just update our cursor().
12b2dcb0
AC
69 auto emplace = cursor ().children.emplace (
70 std::make_pair (link, Node (rib, link.id, cursor ())));
2327631e
AC
71
72 auto it = emplace.first;
73 auto existed = !emplace.second;
74
75 rust_debug ("inserting link: Link(%d [%s]): existed? %s", link.id,
76 link.path.has_value () ? link.path.value ().as_string ().c_str ()
77 : "<anon>",
78 existed ? "yes" : "no");
79
80 // We update the cursor
81 update_cursor (it->second);
82}
83
84template <Namespace N>
85void
86ForeverStack<N>::pop ()
87{
6db677ba 88 rust_assert (!cursor ().is_root ());
2327631e
AC
89
90 rust_debug ("popping link");
91
92 for (const auto &kv : cursor ().rib.get_values ())
93 rust_debug ("current_rib: k: %s, v: %d", kv.first.c_str (), kv.second);
94
95 if (cursor ().parent.has_value ())
96 for (const auto &kv : cursor ().parent.value ().rib.get_values ())
97 rust_debug ("new cursor: k: %s, v: %d", kv.first.c_str (), kv.second);
98
99 update_cursor (cursor ().parent.value ());
100}
101
79d8fb09
AC
102static tl::expected<NodeId, DuplicateNameError>
103insert_inner (Rib &rib, std::string name, NodeId node, bool can_shadow)
104{
105 return rib.insert (name, node, can_shadow);
106}
107
2327631e
AC
108template <Namespace N>
109tl::expected<NodeId, DuplicateNameError>
110ForeverStack<N>::insert (Identifier name, NodeId node)
111{
112 auto &innermost_rib = peek ();
113
114 // So what do we do here - if the Rib has already been pushed in an earlier
115 // pass, we might end up in a situation where it is okay to re-add new names.
116 // Do we just ignore that here? Do we keep track of if the Rib is new or not?
117 // should our cursor have info on the current node like "is it newly pushed"?
79d8fb09
AC
118 return insert_inner (innermost_rib, name.as_string (), node, false);
119}
120
121template <Namespace N>
122tl::expected<NodeId, DuplicateNameError>
123ForeverStack<N>::insert_at_root (Identifier name, NodeId node)
124{
125 auto &root_rib = root.rib;
126
127 // inserting in the root of the crate is never a shadowing operation, even for
128 // macros
129 return insert_inner (root_rib, name.as_string (), node, false);
130}
131
132// Specialization for Macros and Labels - where we are allowed to shadow
133// existing definitions
134template <>
135inline tl::expected<NodeId, DuplicateNameError>
136ForeverStack<Namespace::Macros>::insert (Identifier name, NodeId node)
137{
138 return insert_inner (peek (), name.as_string (), node, true);
139}
140
141template <>
142inline tl::expected<NodeId, DuplicateNameError>
143ForeverStack<Namespace::Labels>::insert (Identifier name, NodeId node)
144{
145 return insert_inner (peek (), name.as_string (), node, true);
2327631e
AC
146}
147
148template <Namespace N>
149Rib &
150ForeverStack<N>::peek ()
151{
152 return cursor ().rib;
153}
154
155template <Namespace N>
156const Rib &
157ForeverStack<N>::peek () const
158{
159 return cursor ().rib;
160}
161
162template <Namespace N>
163void
79d8fb09 164ForeverStack<N>::reverse_iter (std::function<KeepGoing (Node &)> lambda)
2327631e 165{
79d8fb09
AC
166 return reverse_iter (cursor (), lambda);
167}
168
169template <Namespace N>
170void
171ForeverStack<N>::reverse_iter (Node &start,
172 std::function<KeepGoing (Node &)> lambda)
173{
174 auto *tmp = &start;
2327631e
AC
175
176 while (true)
177 {
79d8fb09 178 auto keep_going = lambda (*tmp);
2327631e
AC
179 if (keep_going == KeepGoing::No)
180 return;
181
79d8fb09 182 if (tmp->is_root ())
2327631e
AC
183 return;
184
79d8fb09 185 tmp = &tmp->parent.value ();
2327631e
AC
186 }
187}
188
189template <Namespace N>
190typename ForeverStack<N>::Node &
191ForeverStack<N>::cursor ()
192{
193 return cursor_reference;
194}
195
196template <Namespace N>
197const typename ForeverStack<N>::Node &
198ForeverStack<N>::cursor () const
199{
200 return cursor_reference;
201}
202
203template <Namespace N>
204void
205ForeverStack<N>::update_cursor (Node &new_cursor)
206{
207 cursor_reference = new_cursor;
208}
209
210template <Namespace N>
eec00ae2 211tl::optional<NodeId>
446ab9b2 212ForeverStack<N>::get (const Identifier &name)
79d8fb09
AC
213{
214 tl::optional<NodeId> resolved_node = tl::nullopt;
215
216 // TODO: Can we improve the API? have `reverse_iter` return an optional?
217 reverse_iter ([&resolved_node, &name] (Node &current) {
218 auto candidate = current.rib.get (name.as_string ());
219
220 return candidate.map_or (
221 [&resolved_node] (NodeId found) {
446ab9b2
AC
222 // for most namespaces, we do not need to care about various ribs - they
223 // are available from all contexts if defined in the current scope, or
224 // an outermore one. so if we do have a candidate, we can return it
79d8fb09
AC
225 // directly and stop iterating
226 resolved_node = found;
227
228 return KeepGoing::No;
229 },
230 // if there was no candidate, we keep iterating
231 KeepGoing::Yes);
232 });
233
234 return resolved_node;
235}
236
eec00ae2
AC
237template <>
238tl::optional<NodeId> inline ForeverStack<Namespace::Labels>::get (
239 const Identifier &name)
240{
241 tl::optional<NodeId> resolved_node = tl::nullopt;
242
243 reverse_iter ([&resolved_node, &name] (Node &current) {
244 // looking up for labels cannot go through function ribs
245 // TODO: What other ribs?
246 if (current.rib.kind == Rib::Kind::Function)
247 return KeepGoing::No;
248
249 auto candidate = current.rib.get (name.as_string ());
250
251 // FIXME: Factor this in a function with the generic `get`
252 return candidate.map_or (
253 [&resolved_node] (NodeId found) {
254 resolved_node = found;
255
256 return KeepGoing::No;
257 },
258 KeepGoing::Yes);
259 });
260
261 return resolved_node;
262}
263
79d8fb09
AC
264/* Check if an iterator points to the last element */
265template <typename I, typename C>
266static bool
267is_last (const I &iterator, const C &collection)
268{
269 return iterator + 1 == collection.end ();
270}
271
272/* Check if an iterator points to the start of the collection */
273template <typename I, typename C>
274static bool
275is_start (const I &iterator, const C &collection)
276{
277 return iterator == collection.begin ();
278}
279
280template <Namespace N>
281typename ForeverStack<N>::Node &
282ForeverStack<N>::find_closest_module (Node &starting_point)
283{
284 auto *closest_module = &starting_point;
285
286 reverse_iter (starting_point, [&closest_module] (Node &current) {
287 if (current.rib.kind == Rib::Kind::Module || current.is_root ())
288 {
289 closest_module = &current;
290 return KeepGoing::No;
291 }
292
293 return KeepGoing::Yes;
294 });
295
296 return *closest_module;
297}
298
299/* If a the given condition is met, emit an error about misused leading path
300 * segments */
446ab9b2 301template <typename S>
79d8fb09 302static inline bool
446ab9b2 303check_leading_kw_at_start (const S &segment, bool condition)
79d8fb09
AC
304{
305 if (condition)
306 rust_error_at (
307 segment.get_locus (), ErrorCode::E0433,
308 "leading path segment %qs can only be used at the beginning of a path",
309 segment.as_string ().c_str ());
310
311 return condition;
312}
313
314// we first need to handle the "starting" segments - `super`, `self` or
315// `crate`. we don't need to do anything for `self` and can just skip it. for
316// `crate`, we need to go back to the root of the current stack. for each
317// `super` segment, we go back to the cursor's parent until we reach the
318// correct one or the root.
319template <Namespace N>
446ab9b2
AC
320template <typename S>
321tl::optional<typename std::vector<S>::const_iterator>
322ForeverStack<N>::find_starting_point (const std::vector<S> &segments,
323 Node &starting_point)
79d8fb09
AC
324{
325 auto iterator = segments.begin ();
326
327 // If we need to do path segment resolution, then we start
328 // at the closest module. In order to resolve something like `foo::bar!()`, we
329 // need to get back to the surrounding module, and look for a child module
330 // named `foo`.
331 if (segments.size () > 1)
332 starting_point = find_closest_module (starting_point);
333
334 for (; !is_last (iterator, segments); iterator++)
335 {
66e8a161 336 auto &seg = *iterator;
c5925f34
AC
337 auto is_self_or_crate
338 = seg.is_crate_path_seg () || seg.is_lower_self_seg ();
79d8fb09
AC
339
340 // if we're after the first path segment and meet `self` or `crate`, it's
341 // an error - we should only be seeing `super` keywords at this point
342 if (check_leading_kw_at_start (seg, !is_start (iterator, segments)
343 && is_self_or_crate))
344 return tl::nullopt;
345
346 if (seg.is_crate_path_seg ())
347 {
348 starting_point = root;
349 iterator++;
350 break;
351 }
c5925f34 352 if (seg.is_lower_self_seg ())
79d8fb09
AC
353 {
354 // do nothing and exit
355 iterator++;
356 break;
357 }
358 if (seg.is_super_path_seg ())
359 {
360 if (starting_point.is_root ())
361 {
362 rust_error_at (seg.get_locus (), ErrorCode::E0433,
363 "too many leading %<super%> keywords");
364 return tl::nullopt;
365 }
366
367 starting_point = find_closest_module (starting_point.parent.value ());
368 continue;
369 }
370
371 // now we've gone through the allowed `crate`, `self` or leading `super`
372 // segments. we can start resolving each segment itself.
373 // if we do see another leading segment, then we can error out.
374 break;
375 }
376
377 return iterator;
378}
379
380template <Namespace N>
446ab9b2 381template <typename S>
79d8fb09
AC
382tl::optional<typename ForeverStack<N>::Node &>
383ForeverStack<N>::resolve_segments (
446ab9b2
AC
384 Node &starting_point, const std::vector<S> &segments,
385 typename std::vector<S>::const_iterator iterator)
79d8fb09
AC
386{
387 auto *current_node = &starting_point;
388 for (; !is_last (iterator, segments); iterator++)
389 {
390 auto &seg = *iterator;
391 auto str = seg.as_string ();
392 rust_debug ("[ARTHUR]: resolving segment part: %s", str.c_str ());
393
394 // check that we don't encounter *any* leading keywords afterwards
395 if (check_leading_kw_at_start (seg, seg.is_crate_path_seg ()
396 || seg.is_super_path_seg ()
c5925f34 397 || seg.is_lower_self_seg ()))
79d8fb09
AC
398 return tl::nullopt;
399
400 tl::optional<typename ForeverStack<N>::Node &> child = tl::nullopt;
401
402 for (auto &kv : current_node->children)
403 {
404 auto &link = kv.first;
405
406 if (link.path.map_or (
407 [&str] (Identifier path) {
408 auto &path_str = path.as_string ();
409 return str == path_str;
410 },
411 false))
412 {
413 child = kv.second;
414 break;
415 }
416 }
417
418 if (!child.has_value ())
419 {
420 rust_error_at (seg.get_locus (), ErrorCode::E0433,
421 "failed to resolve path segment %qs", str.c_str ());
422 return tl::nullopt;
423 }
424
425 current_node = &child.value ();
426 }
427
428 return *current_node;
429}
430
431template <Namespace N>
5fd3de11 432template <typename S>
79d8fb09 433tl::optional<NodeId>
5fd3de11 434ForeverStack<N>::resolve_path (const std::vector<S> &segments)
79d8fb09 435{
5fd3de11
AC
436 // TODO: What to do if segments.empty() ?
437
446ab9b2 438 // if there's only one segment, we just use `get`
5fd3de11
AC
439 if (segments.size () == 1)
440 return get (segments.back ().as_string ());
446ab9b2 441
79d8fb09 442 auto starting_point = cursor ();
79d8fb09
AC
443
444 return find_starting_point (segments, starting_point)
445 .and_then ([this, &segments, &starting_point] (
5fd3de11 446 typename std::vector<S>::const_iterator iterator) {
79d8fb09
AC
447 return resolve_segments (starting_point, segments, iterator);
448 })
5fd3de11
AC
449 .and_then ([&segments] (Node final_node) {
450 return final_node.rib.get (segments.back ().as_string ());
79d8fb09
AC
451 });
452}
2327631e 453
12b2dcb0
AC
454template <Namespace N>
455tl::optional<std::pair<typename ForeverStack<N>::Node &, std::string>>
456ForeverStack<N>::dfs (ForeverStack<N>::Node &starting_point, NodeId to_find)
457{
458 auto &values = starting_point.rib.get_values ();
459
460 for (auto &kv : values)
461 if (kv.second == to_find)
462 return {{starting_point, kv.first}};
463
464 for (auto &child : starting_point.children)
465 {
466 auto candidate = dfs (child.second, to_find);
467
468 if (candidate.has_value ())
469 return candidate;
470 }
471
472 return tl::nullopt;
473}
474
475template <Namespace N>
476tl::optional<Resolver::CanonicalPath>
477ForeverStack<N>::to_canonical_path (NodeId id)
478{
479 // find the id in the current forever stack, starting from the root,
480 // performing either a BFS or DFS once the Node containing the ID is found, go
481 // back up to the root (parent().parent().parent()...) accumulate link
482 // segments reverse them that's your canonical path
483
484 return dfs (root, id).map ([this, id] (std::pair<Node &, std::string> tuple) {
485 auto containing_node = tuple.first;
486 auto name = tuple.second;
487
488 auto segments = std::vector<Resolver::CanonicalPath> ();
489
490 reverse_iter (containing_node, [&segments] (Node &current) {
491 if (current.is_root ())
492 return KeepGoing::No;
493
494 auto children = current.parent.value ().children;
495 const Link *outer_link = nullptr;
496
497 for (auto &kv : children)
498 {
499 auto &link = kv.first;
500 auto &child = kv.second;
501
502 if (link.id == child.id)
503 {
504 outer_link = &link;
505 break;
506 }
507 }
508
509 rust_assert (outer_link);
510
511 outer_link->path.map ([&segments, outer_link] (Identifier path) {
512 segments.emplace (segments.begin (),
513 Resolver::CanonicalPath::new_seg (outer_link->id,
514 path.as_string ()));
515 });
516
517 return KeepGoing::Yes;
518 });
519
520 auto path = Resolver::CanonicalPath::create_empty ();
521 for (const auto &segment : segments)
522 path = path.append (segment);
523
524 // Finally, append the name
525 path = path.append (Resolver::CanonicalPath::new_seg (id, name));
12b2dcb0
AC
526
527 return path;
528 });
529}
530
531template <Namespace N>
532tl::optional<Rib &>
232f94af 533ForeverStack<N>::dfs_rib (ForeverStack<N>::Node &starting_point, NodeId to_find)
12b2dcb0 534{
232f94af
AC
535 if (starting_point.id == to_find)
536 return starting_point.rib;
537
538 for (auto &child : starting_point.children)
539 {
540 auto candidate = dfs_rib (child.second, to_find);
541
542 if (candidate.has_value ())
543 return candidate;
544 }
545
12b2dcb0
AC
546 return tl::nullopt;
547}
548
232f94af
AC
549template <Namespace N>
550tl::optional<Rib &>
551ForeverStack<N>::to_rib (NodeId rib_id)
552{
553 return dfs_rib (root, rib_id);
554}
555
2327631e
AC
556template <Namespace N>
557void
558ForeverStack<N>::stream_rib (std::stringstream &stream, const Rib &rib,
559 const std::string &next,
560 const std::string &next_next)
561{
562 if (rib.get_values ().empty ())
563 {
564 stream << next << "rib: {},\n";
565 return;
566 }
567
568 stream << next << "rib: {\n";
569
570 for (const auto &kv : rib.get_values ())
571 stream << next_next << kv.first << ": " << kv.second << "\n";
572
573 stream << next << "},\n";
574}
575
576template <Namespace N>
577void
578ForeverStack<N>::stream_node (std::stringstream &stream, unsigned indentation,
579 const ForeverStack<N>::Node &node)
580{
581 auto indent = std::string (indentation, ' ');
582 auto next = std::string (indentation + 4, ' ');
583 auto next_next = std::string (indentation + 8, ' ');
584
585 stream << indent << "Node {\n"
586 << next << "is_root: " << (node.is_root () ? "true" : "false") << ",\n"
587 << next << "is_leaf: " << (node.is_leaf () ? "true" : "false")
588 << ",\n";
589
590 stream_rib (stream, node.rib, next, next_next);
591
592 stream << indent << "}\n";
593
594 for (auto &kv : node.children)
595 {
596 auto link = kv.first;
597 auto child = kv.second;
598 stream << indent << "Link (" << link.id << ", "
599 << (link.path.has_value () ? link.path.value ().as_string ()
600 : "<anon>")
601 << "):\n";
602
603 stream_node (stream, indentation + 4, child);
604
605 stream << '\n';
606 }
607}
608
609template <Namespace N>
610std::string
611ForeverStack<N>::as_debug_string ()
612{
613 std::stringstream stream;
614
615 stream_node (stream, 0, root);
616
617 return stream.str ();
618}
619
620// FIXME: Can we add selftests?
621
622} // namespace Resolver2_0
623} // namespace Rust