aboutsummaryrefslogtreecommitdiff
path: root/gcc/rust/resolve/rust-name-resolution-context.h
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/rust/resolve/rust-name-resolution-context.h')
-rw-r--r--gcc/rust/resolve/rust-name-resolution-context.h191
1 files changed, 186 insertions, 5 deletions
diff --git a/gcc/rust/resolve/rust-name-resolution-context.h b/gcc/rust/resolve/rust-name-resolution-context.h
index 74f110d..6d990fd 100644
--- a/gcc/rust/resolve/rust-name-resolution-context.h
+++ b/gcc/rust/resolve/rust-name-resolution-context.h
@@ -22,6 +22,8 @@
#include "optional.h"
#include "rust-forever-stack.h"
#include "rust-hir-map.h"
+#include "rust-rib.h"
+#include "rust-stacked-contexts.h"
namespace Rust {
namespace Resolver2_0 {
@@ -155,6 +157,63 @@ public:
NodeId id;
};
+struct Binding
+{
+ enum class Kind
+ {
+ Product,
+ Or,
+ } kind;
+
+ std::unordered_set<Identifier> set;
+
+ Binding (Binding::Kind kind) : kind (kind) {}
+};
+
+/**
+ * Used to identify the source of a binding, and emit the correct error message.
+ */
+enum class BindingSource
+{
+ Match,
+ Let,
+ IfLet,
+ For,
+ /* Closure param or function param */
+ Param
+};
+
+class BindingLayer
+{
+ BindingSource source;
+ std::vector<Binding> bindings;
+
+ bool bind_test (Identifier ident, Binding::Kind kind);
+
+public:
+ void push (Binding::Kind kind);
+
+ BindingLayer (BindingSource source);
+
+ /**
+ * Identifies if the identifier has been used in a product binding context.
+ * eg. `let (a, a) = test();`
+ */
+ bool is_and_bound (Identifier ident);
+
+ /**
+ * Identifies if the identifier has been used in a or context.
+ * eg. `let (a, 1) | (a, 2) = test()`
+ */
+ bool is_or_bound (Identifier ident);
+
+ void insert_ident (Identifier ident);
+
+ void merge ();
+
+ BindingSource get_source () const;
+};
+
// Now our resolver, which keeps track of all the `ForeverStack`s we could want
class NameResolutionContext
{
@@ -171,9 +230,15 @@ public:
tl::expected<NodeId, DuplicateNameError> insert (Identifier name, NodeId id,
Namespace ns);
+ tl::expected<NodeId, DuplicateNameError> insert_variant (Identifier name,
+ NodeId id);
+
tl::expected<NodeId, DuplicateNameError>
insert_shadowable (Identifier name, NodeId id, Namespace ns);
+ tl::expected<NodeId, DuplicateNameError>
+ insert_globbed (Identifier name, NodeId id, Namespace ns);
+
/**
* Run a lambda in a "scoped" context, meaning that a new `Rib` will be pushed
* before executing the lambda and then popped. This is useful for all kinds
@@ -181,8 +246,8 @@ public:
* function. This variant of the function enters a new scope in *all*
* namespaces, while the second variant enters a scope in *one* namespace.
*
- * @param rib New `Rib` to create when entering this scope. A function `Rib`,
- * or an item `Rib`... etc
+ * @param rib_kind New `Rib` to create when entering this scope. A function
+ * `Rib`, or an item `Rib`... etc
* @param scope_id node ID of the scope we are entering, e.g the block's
* `NodeId`.
* @param lambda Function to run within that scope
@@ -192,9 +257,10 @@ public:
*/
// FIXME: Do we want to handle something in particular for expected within the
// scoped lambda?
- void scoped (Rib rib, NodeId scope_id, std::function<void (void)> lambda,
+ void scoped (Rib::Kind rib_kind, NodeId scope_id,
+ std::function<void (void)> lambda,
tl::optional<Identifier> path = {});
- void scoped (Rib rib, Namespace ns, NodeId scope_id,
+ void scoped (Rib::Kind rib_kind, Namespace ns, NodeId scope_id,
std::function<void (void)> lambda,
tl::optional<Identifier> path = {});
@@ -204,12 +270,127 @@ public:
ForeverStack<Namespace::Labels> labels;
Analysis::Mappings &mappings;
+ StackedContexts<BindingLayer> bindings;
// TODO: Rename
// TODO: Use newtype pattern for Usage and Definition
void map_usage (Usage usage, Definition definition);
- tl::optional<NodeId> lookup (NodeId usage);
+ tl::optional<NodeId> lookup (NodeId usage) const;
+
+ template <typename S>
+ tl::optional<Rib::Definition>
+ resolve_path (const std::vector<S> &segments,
+ bool has_opening_scope_resolution,
+ std::vector<Error> &collect_errors, Namespace ns)
+ {
+ std::function<void (const S &, NodeId)> insert_segment_resolution
+ = [this] (const S &seg, NodeId id) {
+ auto seg_id = unwrap_segment_node_id (seg);
+ if (resolved_nodes.find (Usage (seg_id)) == resolved_nodes.end ())
+ map_usage (Usage (seg_id), Definition (id));
+ };
+ switch (ns)
+ {
+ case Namespace::Values:
+ return values.resolve_path (segments, has_opening_scope_resolution,
+ insert_segment_resolution, collect_errors);
+ case Namespace::Types:
+ return types.resolve_path (segments, has_opening_scope_resolution,
+ insert_segment_resolution, collect_errors);
+ case Namespace::Macros:
+ return macros.resolve_path (segments, has_opening_scope_resolution,
+ insert_segment_resolution, collect_errors);
+ case Namespace::Labels:
+ return labels.resolve_path (segments, has_opening_scope_resolution,
+ insert_segment_resolution, collect_errors);
+ default:
+ rust_unreachable ();
+ }
+ }
+
+ template <typename S, typename... Args>
+ tl::optional<Rib::Definition>
+ resolve_path (const std::vector<S> &segments,
+ bool has_opening_scope_resolution,
+ tl::optional<std::vector<Error> &> collect_errors,
+ Namespace ns_first, Args... ns_args)
+ {
+ std::initializer_list<Namespace> namespaces = {ns_first, ns_args...};
+
+ for (auto ns : namespaces)
+ {
+ std::vector<Error> collect_errors_inner;
+ if (auto ret = resolve_path (segments, has_opening_scope_resolution,
+ collect_errors_inner, ns))
+ return ret;
+ if (!collect_errors_inner.empty ())
+ {
+ if (collect_errors.has_value ())
+ {
+ std::move (collect_errors_inner.begin (),
+ collect_errors_inner.end (),
+ std::back_inserter (collect_errors.value ()));
+ }
+ else
+ {
+ for (auto &e : collect_errors_inner)
+ e.emit ();
+ }
+ return tl::nullopt;
+ }
+ }
+
+ return tl::nullopt;
+ }
+
+ template <typename... Args>
+ tl::optional<Rib::Definition>
+ resolve_path (const AST::SimplePath &path,
+ tl::optional<std::vector<Error> &> collect_errors,
+ Namespace ns_first, Args... ns_args)
+ {
+ return resolve_path (path.get_segments (),
+ path.has_opening_scope_resolution (), collect_errors,
+ ns_first, ns_args...);
+ }
+
+ template <typename... Args>
+ tl::optional<Rib::Definition>
+ resolve_path (const AST::PathInExpression &path,
+ tl::optional<std::vector<Error> &> collect_errors,
+ Namespace ns_first, Args... ns_args)
+ {
+ return resolve_path (path.get_segments (), path.opening_scope_resolution (),
+ collect_errors, ns_first, ns_args...);
+ }
+
+ template <typename... Args>
+ tl::optional<Rib::Definition>
+ resolve_path (const AST::TypePath &path,
+ tl::optional<std::vector<Error> &> collect_errors,
+ Namespace ns_first, Args... ns_args)
+ {
+ return resolve_path (path.get_segments (),
+ path.has_opening_scope_resolution_op (),
+ collect_errors, ns_first, ns_args...);
+ }
+
+ template <typename P, typename... Args>
+ tl::optional<Rib::Definition> resolve_path (const P &path, Namespace ns_first,
+ Args... ns_args)
+ {
+ return resolve_path (path, tl::nullopt, ns_first, ns_args...);
+ }
+
+ template <typename P, typename... Args>
+ tl::optional<Rib::Definition>
+ resolve_path (const P &path_segments, bool has_opening_scope_resolution,
+ Namespace ns_first, Args... ns_args)
+ {
+ return resolve_path (path_segments, has_opening_scope_resolution,
+ tl::nullopt, ns_first, ns_args...);
+ }
private:
/* Map of "usage" nodes which have been resolved to a "definition" node */