diff options
Diffstat (limited to 'gcc/rust/resolve/rust-name-resolution-context.h')
-rw-r--r-- | gcc/rust/resolve/rust-name-resolution-context.h | 432 |
1 files changed, 409 insertions, 23 deletions
diff --git a/gcc/rust/resolve/rust-name-resolution-context.h b/gcc/rust/resolve/rust-name-resolution-context.h index 84c0800..558b3ca 100644 --- a/gcc/rust/resolve/rust-name-resolution-context.h +++ b/gcc/rust/resolve/rust-name-resolution-context.h @@ -23,6 +23,8 @@ #include "rust-forever-stack.h" #include "rust-hir-map.h" #include "rust-rib.h" +#include "rust-stacked-contexts.h" +#include "rust-item.h" namespace Rust { namespace Resolver2_0 { @@ -156,6 +158,326 @@ public: NodeId id; }; +struct IdentifierMode +{ + bool is_ref; + bool is_mut; + + IdentifierMode (bool is_ref, bool is_mut) : is_ref (is_ref), is_mut (is_mut) + {} + + bool operator== (const IdentifierMode &other) + { + return other.is_ref == is_ref && other.is_mut == is_mut; + } + + bool operator!= (const IdentifierMode &other) { return !(*this == other); } +}; + +struct Binding +{ + enum class Kind + { + Product, + Or, + } kind; + + // used to check the correctness of or-bindings + bool has_expected_bindings; + + std::unordered_map<std::string, std::pair<location_t, IdentifierMode>> idents; + + Binding (Binding::Kind kind) : kind (kind), has_expected_bindings (false) {} +}; + +/** + * Used to identify the source of a binding, and emit the correct error message. + */ +enum class BindingSource +{ + Match, + Let, + IfLet, + WhileLet, + 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 (std::string ident, location_t locus, bool is_ref, + bool is_mut); + + void merge (); + + BindingSource get_source () const; +}; + +class NameResolutionContext; +/* + * Used to handle canonical paths + * Similar to ForeverStack, but namespace independent and more specialized + */ +class CanonicalPathRecord +{ +public: + virtual Resolver::CanonicalPath as_path (const NameResolutionContext &) = 0; + + virtual bool is_root () const = 0; + + virtual ~CanonicalPathRecord () = default; +}; + +class CanonicalPathRecordWithParent : public CanonicalPathRecord +{ +public: + CanonicalPathRecordWithParent (CanonicalPathRecord &parent) : parent (&parent) + {} + + CanonicalPathRecord &get_parent () { return *parent; } + + bool is_root () const override final { return false; } + +private: + CanonicalPathRecord *parent; +}; + +class CanonicalPathRecordCrateRoot : public CanonicalPathRecord +{ +public: + CanonicalPathRecordCrateRoot (NodeId node_id, std::string seg) + : node_id (node_id), seg (std::move (seg)) + { + rust_assert (Analysis::Mappings::get ().node_is_crate (node_id)); + crate_num = Analysis::Mappings::get ().lookup_crate_num (node_id).value (); + } + + Resolver::CanonicalPath as_path (const NameResolutionContext &) override; + + bool is_root () const override final { return true; } + +private: + NodeId node_id; + CrateNum crate_num; + std::string seg; +}; + +class CanonicalPathRecordNormal : public CanonicalPathRecordWithParent +{ +public: + CanonicalPathRecordNormal (CanonicalPathRecord &parent, NodeId node_id, + std::string seg) + : CanonicalPathRecordWithParent (parent), node_id (node_id), + seg (std::move (seg)) + { + rust_assert (!Analysis::Mappings::get ().node_is_crate (node_id)); + } + + Resolver::CanonicalPath as_path (const NameResolutionContext &) override; + +private: + NodeId node_id; + std::string seg; +}; + +class CanonicalPathRecordLookup : public CanonicalPathRecord +{ +public: + CanonicalPathRecordLookup (NodeId lookup_id) + : lookup_id (lookup_id), cache (nullptr) + {} + + Resolver::CanonicalPath as_path (const NameResolutionContext &) override; + + bool is_root () const override final { return true; } + +private: + NodeId lookup_id; + CanonicalPathRecord *cache; +}; + +class CanonicalPathRecordImpl : public CanonicalPathRecordWithParent +{ +public: + CanonicalPathRecordImpl (CanonicalPathRecord &parent, NodeId impl_id, + NodeId type_id) + : CanonicalPathRecordWithParent (parent), impl_id (impl_id), + type_record (type_id) + {} + + Resolver::CanonicalPath as_path (const NameResolutionContext &) override; + +private: + NodeId impl_id; + CanonicalPathRecordLookup type_record; +}; + +class CanonicalPathRecordTraitImpl : public CanonicalPathRecordWithParent +{ +public: + CanonicalPathRecordTraitImpl (CanonicalPathRecord &parent, NodeId impl_id, + NodeId type_id, NodeId trait_path_id) + : CanonicalPathRecordWithParent (parent), impl_id (impl_id), + type_record (type_id), trait_path_record (trait_path_id) + {} + + Resolver::CanonicalPath as_path (const NameResolutionContext &) override; + +private: + NodeId impl_id; + CanonicalPathRecordLookup type_record; + CanonicalPathRecordLookup trait_path_record; +}; + +class CanonicalPathCtx +{ +public: + CanonicalPathCtx (const NameResolutionContext &ctx) + : current_record (nullptr), nr_ctx (&ctx) + {} + + Resolver::CanonicalPath get_path (NodeId id) const + { + return get_record (id).as_path (*nr_ctx); + } + + CanonicalPathRecord &get_record (NodeId id) const + { + auto it = records.find (id); + rust_assert (it != records.end ()); + return *it->second; + } + + tl::optional<CanonicalPathRecord *> get_record_opt (NodeId id) const + { + auto it = records.find (id); + if (it == records.end ()) + return tl::nullopt; + else + return it->second.get (); + } + + void insert_record (NodeId id, const Identifier &ident) + { + insert_record (id, ident.as_string ()); + } + + void insert_record (NodeId id, std::string seg) + { + rust_assert (current_record != nullptr); + + auto it = records.find (id); + if (it == records.end ()) + { + auto record = new CanonicalPathRecordNormal (*current_record, id, + std::move (seg)); + bool ok + = records.emplace (id, std::unique_ptr<CanonicalPathRecord> (record)) + .second; + rust_assert (ok); + } + } + + template <typename F> void scope (NodeId id, const Identifier &ident, F &&f) + { + scope (id, ident.as_string (), std::forward<F> (f)); + } + + template <typename F> void scope (NodeId id, std::string seg, F &&f) + { + rust_assert (current_record != nullptr); + + scope_inner (id, std::forward<F> (f), [this, id, &seg] () { + return new CanonicalPathRecordNormal (*current_record, id, + std::move (seg)); + }); + } + + template <typename F> void scope_impl (AST::InherentImpl &impl, F &&f) + { + rust_assert (current_record != nullptr); + + NodeId id = impl.get_node_id (); + scope_inner (id, std::forward<F> (f), [this, id, &impl] () { + return new CanonicalPathRecordImpl (*current_record, id, + impl.get_type ().get_node_id ()); + }); + } + + template <typename F> void scope_impl (AST::TraitImpl &impl, F &&f) + { + rust_assert (current_record != nullptr); + + NodeId id = impl.get_node_id (); + scope_inner (id, std::forward<F> (f), [this, id, &impl] () { + return new CanonicalPathRecordTraitImpl ( + *current_record, id, impl.get_type ().get_node_id (), + impl.get_trait_path ().get_node_id ()); + }); + } + + template <typename F> + void scope_crate (NodeId node_id, std::string crate_name, F &&f) + { + scope_inner (node_id, std::forward<F> (f), [node_id, &crate_name] () { + return new CanonicalPathRecordCrateRoot (node_id, std::move (crate_name)); + }); + } + +private: + template <typename FCreate, typename FCallback> + void scope_inner (NodeId id, FCallback &&f_callback, FCreate &&f_create) + { + auto it = records.find (id); + if (it == records.end ()) + { + CanonicalPathRecord *record = std::forward<FCreate> (f_create) (); + it = records.emplace (id, std::unique_ptr<CanonicalPathRecord> (record)) + .first; + } + + rust_assert (it->second->is_root () + || &static_cast<CanonicalPathRecordWithParent &> (*it->second) + .get_parent () + == current_record); + + CanonicalPathRecord *stash = it->second.get (); + std::swap (stash, current_record); + + std::forward<FCallback> (f_callback) (); + + std::swap (stash, current_record); + } + + std::unordered_map<NodeId, std::unique_ptr<CanonicalPathRecord>> records; + CanonicalPathRecord *current_record; + + const NameResolutionContext *nr_ctx; +}; + // Now our resolver, which keeps track of all the `ForeverStack`s we could want class NameResolutionContext { @@ -212,6 +534,9 @@ public: ForeverStack<Namespace::Labels> labels; Analysis::Mappings &mappings; + StackedContexts<BindingLayer> bindings; + + CanonicalPathCtx canonical_ctx; // TODO: Rename // TODO: Use newtype pattern for Usage and Definition @@ -219,10 +544,15 @@ public: tl::optional<NodeId> lookup (NodeId usage) const; + Resolver::CanonicalPath to_canonical_path (NodeId id) const + { + return canonical_ctx.get_path (id); + } + template <typename S> - tl::optional<Rib::Definition> resolve_path (const std::vector<S> &segments, - bool has_opening_scope_resolution, - Namespace ns) + tl::optional<Rib::Definition> + resolve_path (const std::vector<S> &segments, ResolutionMode mode, + std::vector<Error> &collect_errors, Namespace ns) { std::function<void (const S &, NodeId)> insert_segment_resolution = [this] (const S &seg, NodeId id) { @@ -233,61 +563,117 @@ public: switch (ns) { case Namespace::Values: - return values.resolve_path (segments, has_opening_scope_resolution, - insert_segment_resolution); + return values.resolve_path (segments, mode, insert_segment_resolution, + collect_errors); case Namespace::Types: - return types.resolve_path (segments, has_opening_scope_resolution, - insert_segment_resolution); + return types.resolve_path (segments, mode, insert_segment_resolution, + collect_errors); case Namespace::Macros: - return macros.resolve_path (segments, has_opening_scope_resolution, - insert_segment_resolution); + return macros.resolve_path (segments, mode, insert_segment_resolution, + collect_errors); case Namespace::Labels: - return labels.resolve_path (segments, has_opening_scope_resolution, - insert_segment_resolution); + return labels.resolve_path (segments, mode, 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, - Args... ns_args) + tl::optional<Rib::Definition> + resolve_path (const std::vector<S> &segments, ResolutionMode mode, + tl::optional<std::vector<Error> &> collect_errors, + Namespace ns_first, Args... ns_args) { - std::initializer_list<Namespace> namespaces = {ns_args...}; + std::initializer_list<Namespace> namespaces = {ns_first, ns_args...}; for (auto ns : namespaces) { - if (auto ret - = resolve_path (segments, has_opening_scope_resolution, ns)) + std::vector<Error> collect_errors_inner; + if (auto ret = resolve_path (segments, mode, 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 S, typename... Args> + tl::optional<Rib::Definition> + resolve_path (const std::vector<S> &path_segments, + bool has_opening_scope_resolution, + tl::optional<std::vector<Error> &> collect_errors, + Namespace ns_first, Args... ns_args) + { + auto mode = ResolutionMode::Normal; + if (has_opening_scope_resolution) + { + if (get_rust_edition () == Edition::E2015) + mode = ResolutionMode::FromRoot; + else + mode = ResolutionMode::FromExtern; + } + return resolve_path (path_segments, mode, collect_errors, ns_first, + ns_args...); + } + + template <typename S, typename... Args> + tl::optional<Rib::Definition> + resolve_path (const std::vector<S> &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...); + } + + template <typename S, typename... Args> + tl::optional<Rib::Definition> + resolve_path (const std::vector<S> &path_segments, ResolutionMode mode, + Namespace ns_first, Args... ns_args) + { + return resolve_path (path_segments, mode, tl::nullopt, ns_first, + ns_args...); + } + template <typename... Args> tl::optional<Rib::Definition> resolve_path (const AST::SimplePath &path, - Args... ns_args) + Args &&...args) { return resolve_path (path.get_segments (), - path.has_opening_scope_resolution (), ns_args...); + path.has_opening_scope_resolution (), + std::forward<Args> (args)...); } template <typename... Args> tl::optional<Rib::Definition> resolve_path (const AST::PathInExpression &path, - Args... ns_args) + Args &&...args) { return resolve_path (path.get_segments (), path.opening_scope_resolution (), - ns_args...); + std::forward<Args> (args)...); } template <typename... Args> tl::optional<Rib::Definition> resolve_path (const AST::TypePath &path, - Args... ns_args) + Args &&...args) { return resolve_path (path.get_segments (), - path.has_opening_scope_resolution_op (), ns_args...); + path.has_opening_scope_resolution_op (), + std::forward<Args> (args)...); } private: |