// Copyright (C) 2020-2025 Free Software Foundation, Inc. // This file is part of GCC. // GCC is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free // Software Foundation; either version 3, or (at your option) any later // version. // GCC is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // You should have received a copy of the GNU General Public License // along with GCC; see the file COPYING3. If not see // . #include "rust-name-resolution-context.h" #include "optional.h" #include "rust-mapping-common.h" namespace Rust { namespace Resolver2_0 { BindingLayer::BindingLayer (BindingSource source) : source (source) { push (Binding::Kind::Product); } bool BindingLayer::bind_test (Identifier ident, Binding::Kind kind) { for (auto &bind : bindings) { if (bind.idents.find (ident.as_string ()) != bind.idents.cend () && bind.kind == kind) { return true; } } return false; } void BindingLayer::push (Binding::Kind kind) { bindings.push_back (Binding (kind)); } bool BindingLayer::is_and_bound (Identifier ident) { return bind_test (ident, Binding::Kind::Product); } bool BindingLayer::is_or_bound (Identifier ident) { return bind_test (ident, Binding::Kind::Or); } void BindingLayer::insert_ident (std::string ident, location_t locus, bool is_ref, bool is_mut) { bindings.back ().idents.emplace ( std::move (ident), std::make_pair (locus, IdentifierMode (is_ref, is_mut))); } void BindingLayer::merge () { auto last_binding = std::move (bindings.back ()); bindings.pop_back (); if (bindings.back ().has_expected_bindings) { for (auto &value : bindings.back ().idents) { auto ident = value.first; if (last_binding.idents.find (ident) == last_binding.idents.end ()) { location_t locus = value.second.first; rust_error_at (locus, ErrorCode::E0408, "variable %qs is not bound in all patterns", ident.c_str ()); } } } for (auto &value : last_binding.idents) { auto res = bindings.back ().idents.emplace (value); if (res.second) { if (bindings.back ().has_expected_bindings) { auto &ident = value.first; location_t locus = value.second.first; rust_error_at (locus, ErrorCode::E0408, "variable %qs is not bound in all patterns", ident.c_str ()); } } else { auto this_mode = value.second.second; auto other_mode = res.first->second.second; if (this_mode != other_mode) { auto &ident = value.first; location_t locus = value.second.first; rust_error_at (locus, ErrorCode::E0409, "variable %qs is bound inconsistently across " "pattern alternatives", ident.c_str ()); } } } if (bindings.back ().kind == Binding::Kind::Or) bindings.back ().has_expected_bindings = true; } BindingSource BindingLayer::get_source () const { return source; } Resolver::CanonicalPath CanonicalPathRecordCrateRoot::as_path (const NameResolutionContext &) { auto ret = Resolver::CanonicalPath::new_seg (node_id, seg); ret.set_crate_num (crate_num); return ret; } Resolver::CanonicalPath CanonicalPathRecordNormal::as_path (const NameResolutionContext &ctx) { auto parent_path = get_parent ().as_path (ctx); return parent_path.append (Resolver::CanonicalPath::new_seg (node_id, seg)); } Resolver::CanonicalPath CanonicalPathRecordLookup::as_path (const NameResolutionContext &ctx) { if (!cache) { auto res = ctx.lookup (lookup_id).and_then ( [&ctx] (NodeId id) { return ctx.canonical_ctx.get_record_opt (id); }); if (!res) { // HACK: use a dummy value // this should bring us roughly to parity with nr1.0 // since nr1.0 doesn't seem to handle canonical paths for generics // quite right anyways return Resolver::CanonicalPath::new_seg (UNKNOWN_NODEID, "XXX"); } cache = res.value (); } return cache->as_path (ctx); } Resolver::CanonicalPath CanonicalPathRecordImpl::as_path (const NameResolutionContext &ctx) { auto parent_path = get_parent ().as_path (ctx); return parent_path.append ( Resolver::CanonicalPath::inherent_impl_seg (impl_id, type_record.as_path (ctx))); } Resolver::CanonicalPath CanonicalPathRecordTraitImpl::as_path (const NameResolutionContext &ctx) { auto parent_path = get_parent ().as_path (ctx); return parent_path.append ( Resolver::CanonicalPath::trait_impl_projection_seg ( impl_id, trait_path_record.as_path (ctx), type_record.as_path (ctx))); } NameResolutionContext::NameResolutionContext () : mappings (Analysis::Mappings::get ()), canonical_ctx (*this) {} tl::expected NameResolutionContext::insert (Identifier name, NodeId id, Namespace ns) { switch (ns) { case Namespace::Values: return values.insert (name, id); case Namespace::Types: return types.insert (name, id); case Namespace::Macros: return macros.insert (name, id); case Namespace::Labels: default: // return labels.insert (name, id); rust_unreachable (); } } tl::expected NameResolutionContext::insert_variant (Identifier name, NodeId id) { return types.insert_variant (name, id); } tl::expected NameResolutionContext::insert_shadowable (Identifier name, NodeId id, Namespace ns) { switch (ns) { case Namespace::Values: return values.insert_shadowable (name, id); case Namespace::Types: return types.insert_shadowable (name, id); case Namespace::Macros: return macros.insert_shadowable (name, id); case Namespace::Labels: default: // return labels.insert (name, id); rust_unreachable (); } } tl::expected NameResolutionContext::insert_globbed (Identifier name, NodeId id, Namespace ns) { switch (ns) { case Namespace::Values: return values.insert_globbed (name, id); case Namespace::Types: return types.insert_globbed (name, id); case Namespace::Macros: return macros.insert_globbed (name, id); case Namespace::Labels: default: // return labels.insert (name, id); rust_unreachable (); } } void NameResolutionContext::map_usage (Usage usage, Definition definition) { auto inserted = resolved_nodes.emplace (usage, definition).second; // is that valid? rust_assert (inserted); } tl::optional NameResolutionContext::lookup (NodeId usage) const { auto it = resolved_nodes.find (Usage (usage)); if (it == resolved_nodes.end ()) return tl::nullopt; return it->second.id; } void NameResolutionContext::scoped (Rib::Kind rib_kind, NodeId id, std::function lambda, tl::optional path) { // NOTE: You must be at the root node when pushing the prelude rib. values.push (rib_kind, id, path); types.push (rib_kind, id, path); macros.push (rib_kind, id, path); // labels.push (rib, id); lambda (); values.pop (); types.pop (); macros.pop (); // labels.pop (rib); } void NameResolutionContext::scoped (Rib::Kind rib_kind, Namespace ns, NodeId scope_id, std::function lambda, tl::optional path) { // This could work... I'm not sure why you would want to do this though. rust_assert (rib_kind != Rib::Kind::Prelude); switch (ns) { case Namespace::Values: values.push (rib_kind, scope_id, path); break; case Namespace::Types: types.push (rib_kind, scope_id, path); break; case Namespace::Labels: case Namespace::Macros: gcc_unreachable (); } lambda (); switch (ns) { case Namespace::Values: values.pop (); break; case Namespace::Types: types.pop (); break; case Namespace::Labels: case Namespace::Macros: gcc_unreachable (); } } } // namespace Resolver2_0 } // namespace Rust