[ty] Use HashTable in PlaceTable (#18819)

This commit is contained in:
Micha Reiser 2025-06-20 15:31:54 +02:00 committed by GitHub
parent 234d248730
commit f544026b81
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 39 additions and 48 deletions

View file

@ -6,7 +6,7 @@ use ruff_db::parsed::parsed_module;
use ruff_index::{IndexSlice, IndexVec}; use ruff_index::{IndexSlice, IndexVec};
use ruff_python_parser::semantic_errors::SemanticSyntaxError; use ruff_python_parser::semantic_errors::SemanticSyntaxError;
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use salsa::Update; use salsa::Update;
use salsa::plumbing::AsId; use salsa::plumbing::AsId;
@ -41,7 +41,7 @@ pub(crate) use self::use_def::{
DeclarationWithConstraint, DeclarationsIterator, DeclarationWithConstraint, DeclarationsIterator,
}; };
type PlaceSet = hashbrown::HashMap<ScopedPlaceId, (), FxBuildHasher>; type PlaceSet = hashbrown::HashTable<ScopedPlaceId>;
/// Returns the semantic index for `file`. /// Returns the semantic index for `file`.
/// ///

View file

@ -3,7 +3,7 @@ use std::hash::{Hash, Hasher};
use std::ops::Range; use std::ops::Range;
use bitflags::bitflags; use bitflags::bitflags;
use hashbrown::hash_map::RawEntryMut; use hashbrown::hash_table::Entry;
use ruff_db::files::File; use ruff_db::files::File;
use ruff_db::parsed::ParsedModuleRef; use ruff_db::parsed::ParsedModuleRef;
use ruff_index::{IndexVec, newtype_index}; use ruff_index::{IndexVec, newtype_index};
@ -18,7 +18,7 @@ use crate::node_key::NodeKey;
use crate::semantic_index::reachability_constraints::ScopedReachabilityConstraintId; use crate::semantic_index::reachability_constraints::ScopedReachabilityConstraintId;
use crate::semantic_index::{PlaceSet, SemanticIndex, semantic_index}; use crate::semantic_index::{PlaceSet, SemanticIndex, semantic_index};
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) enum PlaceExprSubSegment { pub(crate) enum PlaceExprSubSegment {
/// A member access, e.g. `.y` in `x.y` /// A member access, e.g. `.y` in `x.y`
Member(ast::name::Name), Member(ast::name::Name),
@ -339,9 +339,10 @@ impl PlaceExprWithFlags {
} }
} }
#[derive(Clone, Copy, Eq, PartialEq, Debug)] #[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)]
pub struct PlaceExprRef<'a> { pub struct PlaceExprRef<'a> {
pub(crate) root_name: &'a Name, pub(crate) root_name: &'a Name,
/// Sub-segments is empty for a simple target (e.g. `foo`).
pub(crate) sub_segments: &'a [PlaceExprSubSegment], pub(crate) sub_segments: &'a [PlaceExprSubSegment],
} }
@ -608,7 +609,7 @@ impl ScopeKind {
} }
/// [`PlaceExpr`] table for a specific [`Scope`]. /// [`PlaceExpr`] table for a specific [`Scope`].
#[derive(Default, salsa::Update)] #[derive(Default)]
pub struct PlaceTable { pub struct PlaceTable {
/// The place expressions in this scope. /// The place expressions in this scope.
places: IndexVec<ScopedPlaceId, PlaceExprWithFlags>, places: IndexVec<ScopedPlaceId, PlaceExprWithFlags>,
@ -672,14 +673,11 @@ impl PlaceTable {
/// Returns the [`ScopedPlaceId`] of the place named `name`. /// Returns the [`ScopedPlaceId`] of the place named `name`.
pub(crate) fn place_id_by_name(&self, name: &str) -> Option<ScopedPlaceId> { pub(crate) fn place_id_by_name(&self, name: &str) -> Option<ScopedPlaceId> {
let (id, ()) = self self.place_set
.place_set .find(Self::hash_name(name), |id| {
.raw_entry()
.from_hash(Self::hash_name(name), |id| {
self.place_expr(*id).as_name().map(Name::as_str) == Some(name) self.place_expr(*id).as_name().map(Name::as_str) == Some(name)
})?; })
.copied()
Some(*id)
} }
/// Returns the [`ScopedPlaceId`] of the place expression. /// Returns the [`ScopedPlaceId`] of the place expression.
@ -688,14 +686,11 @@ impl PlaceTable {
place_expr: impl Into<PlaceExprRef<'e>>, place_expr: impl Into<PlaceExprRef<'e>>,
) -> Option<ScopedPlaceId> { ) -> Option<ScopedPlaceId> {
let place_expr = place_expr.into(); let place_expr = place_expr.into();
let (id, ()) = self self.place_set
.place_set .find(Self::hash_place_expr(place_expr), |id| {
.raw_entry()
.from_hash(Self::hash_place_expr(place_expr), |id| {
self.place_expr(*id).expr == place_expr self.place_expr(*id).expr == place_expr
})?; })
.copied()
Some(*id)
} }
pub(crate) fn place_id_by_instance_attribute_name(&self, name: &str) -> Option<ScopedPlaceId> { pub(crate) fn place_id_by_instance_attribute_name(&self, name: &str) -> Option<ScopedPlaceId> {
@ -711,16 +706,16 @@ impl PlaceTable {
} }
fn hash_place_expr<'e>(place_expr: impl Into<PlaceExprRef<'e>>) -> u64 { fn hash_place_expr<'e>(place_expr: impl Into<PlaceExprRef<'e>>) -> u64 {
let place_expr = place_expr.into(); let place_expr: PlaceExprRef = place_expr.into();
let mut hasher = FxHasher::default(); let mut hasher = FxHasher::default();
place_expr.root_name.as_str().hash(&mut hasher);
for segment in place_expr.sub_segments { // Special case for simple names (e.g. "foo"). Only hash the name so
match segment { // that a lookup by name can find it (see `place_by_name`).
PlaceExprSubSegment::Member(name) => name.hash(&mut hasher), if place_expr.sub_segments.is_empty() {
PlaceExprSubSegment::IntSubscript(int) => int.hash(&mut hasher), place_expr.root_name.as_str().hash(&mut hasher);
PlaceExprSubSegment::StringSubscript(string) => string.hash(&mut hasher), } else {
} place_expr.hash(&mut hasher);
} }
hasher.finish() hasher.finish()
} }
@ -755,21 +750,19 @@ pub(super) struct PlaceTableBuilder {
impl PlaceTableBuilder { impl PlaceTableBuilder {
pub(super) fn add_symbol(&mut self, name: Name) -> (ScopedPlaceId, bool) { pub(super) fn add_symbol(&mut self, name: Name) -> (ScopedPlaceId, bool) {
let hash = PlaceTable::hash_name(&name); let hash = PlaceTable::hash_name(&name);
let entry = self let entry = self.table.place_set.entry(
.table hash,
.place_set |id| self.table.places[*id].as_name() == Some(&name),
.raw_entry_mut() |id| PlaceTable::hash_place_expr(&self.table.places[*id].expr),
.from_hash(hash, |id| self.table.places[*id].as_name() == Some(&name)); );
match entry { match entry {
RawEntryMut::Occupied(entry) => (*entry.key(), false), Entry::Occupied(entry) => (*entry.get(), false),
RawEntryMut::Vacant(entry) => { Entry::Vacant(entry) => {
let symbol = PlaceExprWithFlags::name(name); let symbol = PlaceExprWithFlags::name(name);
let id = self.table.places.push(symbol); let id = self.table.places.push(symbol);
entry.insert_with_hasher(hash, id, (), |id| { entry.insert(id);
PlaceTable::hash_place_expr(&self.table.places[*id].expr)
});
let new_id = self.associated_place_ids.push(vec![]); let new_id = self.associated_place_ids.push(vec![]);
debug_assert_eq!(new_id, id); debug_assert_eq!(new_id, id);
(id, true) (id, true)
@ -779,19 +772,17 @@ impl PlaceTableBuilder {
pub(super) fn add_place(&mut self, place_expr: PlaceExprWithFlags) -> (ScopedPlaceId, bool) { pub(super) fn add_place(&mut self, place_expr: PlaceExprWithFlags) -> (ScopedPlaceId, bool) {
let hash = PlaceTable::hash_place_expr(&place_expr.expr); let hash = PlaceTable::hash_place_expr(&place_expr.expr);
let entry = self let entry = self.table.place_set.entry(
.table hash,
.place_set |id| self.table.places[*id].expr == place_expr.expr,
.raw_entry_mut() |id| PlaceTable::hash_place_expr(&self.table.places[*id].expr),
.from_hash(hash, |id| self.table.places[*id].expr == place_expr.expr); );
match entry { match entry {
RawEntryMut::Occupied(entry) => (*entry.key(), false), Entry::Occupied(entry) => (*entry.get(), false),
RawEntryMut::Vacant(entry) => { Entry::Vacant(entry) => {
let id = self.table.places.push(place_expr); let id = self.table.places.push(place_expr);
entry.insert_with_hasher(hash, id, (), |id| { entry.insert(id);
PlaceTable::hash_place_expr(&self.table.places[*id].expr)
});
let new_id = self.associated_place_ids.push(vec![]); let new_id = self.associated_place_ids.push(vec![]);
debug_assert_eq!(new_id, id); debug_assert_eq!(new_id, id);
for root in self.table.places[id].expr.root_exprs() { for root in self.table.places[id].expr.root_exprs() {