Skip to content

Commit 5f426b9

Browse files
authored
[ty] Remove ScopedExpressionId (#19019)
## Summary The motivation of `ScopedExpressionId` was that we have an expression identifier that's local to a scope and, therefore, unlikely to change if a user makes changes in another scope. A local identifier like this has the advantage that query results may remain unchanged even if other parts of the file change, which in turn allows Salsa to short-circuit dependent queries. However, I noticed that we aren't using `ScopedExpressionId` in a place where it's important that the identifier is local. It's main use is inside `infer` which we always run for the entire file. The one exception to this is `Unpack` but unpack runs as part of `infer`. Edit: The above isn't entirely correct. We used ScopedExpressionId in TypeInference which is a query result. Now using ExpressionNodeKey does mean that a change to the AST invalidates most if not all TypeInference results of a single file. Salsa then has to run all dependent queries to see if they're affected by this change even if the change was local to another scope. If this locality proves to be important I suggest that we create two queries on top of TypeInference: one that returns the expression map which is mainly used in the linter and type inference and a second that returns all remaining fields. This should give us a similar optimization at a much lower cost I also considered remove `ScopedUseId` but I believe that one is still useful because using `ExpressionNodeKey` for it instead would mean that all `UseDefMap` change when a single AST node changes. Whether this is important is something difficult to assess. I'm simply not familiar enough with the `UseDefMap`. If the locality doesn't matter for the `UseDefMap`, then a similar change could be made and `bindings_by_use` could be changed to an `FxHashMap<UseId, Bindings>` where `UseId` is a thin wrapper around `NodeKey`. Closes astral-sh/ty#721
1 parent 37ba185 commit 5f426b9

File tree

10 files changed

+89
-256
lines changed

10 files changed

+89
-256
lines changed

crates/ty_python_semantic/src/dunder_all.rs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@ use ruff_python_ast::name::Name;
66
use ruff_python_ast::statement_visitor::{StatementVisitor, walk_stmt};
77
use ruff_python_ast::{self as ast};
88

9-
use crate::semantic_index::ast_ids::HasScopedExpressionId;
10-
use crate::semantic_index::place::ScopeId;
11-
use crate::semantic_index::{SemanticIndex, global_scope, semantic_index};
9+
use crate::semantic_index::{SemanticIndex, semantic_index};
1210
use crate::types::{Truthiness, Type, infer_expression_types};
1311
use crate::{Db, ModuleName, resolve_module};
1412

@@ -44,11 +42,6 @@ struct DunderAllNamesCollector<'db> {
4442
db: &'db dyn Db,
4543
file: File,
4644

47-
/// The scope in which the `__all__` names are being collected from.
48-
///
49-
/// This is always going to be the global scope of the module.
50-
scope: ScopeId<'db>,
51-
5245
/// The semantic index for the module.
5346
index: &'db SemanticIndex<'db>,
5447

@@ -68,7 +61,6 @@ impl<'db> DunderAllNamesCollector<'db> {
6861
Self {
6962
db,
7063
file,
71-
scope: global_scope(db, file),
7264
index,
7365
origin: None,
7466
invalid: false,
@@ -190,8 +182,7 @@ impl<'db> DunderAllNamesCollector<'db> {
190182
///
191183
/// This function panics if `expr` was not marked as a standalone expression during semantic indexing.
192184
fn standalone_expression_type(&self, expr: &ast::Expr) -> Type<'db> {
193-
infer_expression_types(self.db, self.index.expression(expr))
194-
.expression_type(expr.scoped_expression_id(self.db, self.scope))
185+
infer_expression_types(self.db, self.index.expression(expr)).expression_type(expr)
195186
}
196187

197188
/// Evaluate the given expression and return its truthiness.

crates/ty_python_semantic/src/semantic_index/ast_ids.rs

Lines changed: 6 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,11 @@ use crate::semantic_index::semantic_index;
2626
/// ```
2727
#[derive(Debug, salsa::Update, get_size2::GetSize)]
2828
pub(crate) struct AstIds {
29-
/// Maps expressions to their expression id.
30-
expressions_map: FxHashMap<ExpressionNodeKey, ScopedExpressionId>,
3129
/// Maps expressions which "use" a place (that is, [`ast::ExprName`], [`ast::ExprAttribute`] or [`ast::ExprSubscript`]) to a use id.
3230
uses_map: FxHashMap<ExpressionNodeKey, ScopedUseId>,
3331
}
3432

3533
impl AstIds {
36-
fn expression_id(&self, key: impl Into<ExpressionNodeKey>) -> ScopedExpressionId {
37-
let key = &key.into();
38-
*self.expressions_map.get(key).unwrap_or_else(|| {
39-
panic!("Could not find expression ID for {key:?}");
40-
})
41-
}
42-
4334
fn use_id(&self, key: impl Into<ExpressionNodeKey>) -> ScopedUseId {
4435
self.uses_map[&key.into()]
4536
}
@@ -94,90 +85,12 @@ impl HasScopedUseId for ast::ExprRef<'_> {
9485
}
9586
}
9687

97-
/// Uniquely identifies an [`ast::Expr`] in a [`crate::semantic_index::place::FileScopeId`].
98-
#[newtype_index]
99-
#[derive(salsa::Update, get_size2::GetSize)]
100-
pub struct ScopedExpressionId;
101-
102-
pub trait HasScopedExpressionId {
103-
/// Returns the ID that uniquely identifies the node in `scope`.
104-
fn scoped_expression_id(&self, db: &dyn Db, scope: ScopeId) -> ScopedExpressionId;
105-
}
106-
107-
impl<T: HasScopedExpressionId> HasScopedExpressionId for Box<T> {
108-
fn scoped_expression_id(&self, db: &dyn Db, scope: ScopeId) -> ScopedExpressionId {
109-
self.as_ref().scoped_expression_id(db, scope)
110-
}
111-
}
112-
113-
macro_rules! impl_has_scoped_expression_id {
114-
($ty: ty) => {
115-
impl HasScopedExpressionId for $ty {
116-
fn scoped_expression_id(&self, db: &dyn Db, scope: ScopeId) -> ScopedExpressionId {
117-
let expression_ref = ExprRef::from(self);
118-
expression_ref.scoped_expression_id(db, scope)
119-
}
120-
}
121-
};
122-
}
123-
124-
impl_has_scoped_expression_id!(ast::ExprBoolOp);
125-
impl_has_scoped_expression_id!(ast::ExprName);
126-
impl_has_scoped_expression_id!(ast::ExprBinOp);
127-
impl_has_scoped_expression_id!(ast::ExprUnaryOp);
128-
impl_has_scoped_expression_id!(ast::ExprLambda);
129-
impl_has_scoped_expression_id!(ast::ExprIf);
130-
impl_has_scoped_expression_id!(ast::ExprDict);
131-
impl_has_scoped_expression_id!(ast::ExprSet);
132-
impl_has_scoped_expression_id!(ast::ExprListComp);
133-
impl_has_scoped_expression_id!(ast::ExprSetComp);
134-
impl_has_scoped_expression_id!(ast::ExprDictComp);
135-
impl_has_scoped_expression_id!(ast::ExprGenerator);
136-
impl_has_scoped_expression_id!(ast::ExprAwait);
137-
impl_has_scoped_expression_id!(ast::ExprYield);
138-
impl_has_scoped_expression_id!(ast::ExprYieldFrom);
139-
impl_has_scoped_expression_id!(ast::ExprCompare);
140-
impl_has_scoped_expression_id!(ast::ExprCall);
141-
impl_has_scoped_expression_id!(ast::ExprFString);
142-
impl_has_scoped_expression_id!(ast::ExprStringLiteral);
143-
impl_has_scoped_expression_id!(ast::ExprBytesLiteral);
144-
impl_has_scoped_expression_id!(ast::ExprNumberLiteral);
145-
impl_has_scoped_expression_id!(ast::ExprBooleanLiteral);
146-
impl_has_scoped_expression_id!(ast::ExprNoneLiteral);
147-
impl_has_scoped_expression_id!(ast::ExprEllipsisLiteral);
148-
impl_has_scoped_expression_id!(ast::ExprAttribute);
149-
impl_has_scoped_expression_id!(ast::ExprSubscript);
150-
impl_has_scoped_expression_id!(ast::ExprStarred);
151-
impl_has_scoped_expression_id!(ast::ExprNamed);
152-
impl_has_scoped_expression_id!(ast::ExprList);
153-
impl_has_scoped_expression_id!(ast::ExprTuple);
154-
impl_has_scoped_expression_id!(ast::ExprSlice);
155-
impl_has_scoped_expression_id!(ast::ExprIpyEscapeCommand);
156-
impl_has_scoped_expression_id!(ast::Expr);
157-
158-
impl HasScopedExpressionId for ast::ExprRef<'_> {
159-
fn scoped_expression_id(&self, db: &dyn Db, scope: ScopeId) -> ScopedExpressionId {
160-
let ast_ids = ast_ids(db, scope);
161-
ast_ids.expression_id(*self)
162-
}
163-
}
164-
16588
#[derive(Debug, Default)]
16689
pub(super) struct AstIdsBuilder {
167-
expressions_map: FxHashMap<ExpressionNodeKey, ScopedExpressionId>,
16890
uses_map: FxHashMap<ExpressionNodeKey, ScopedUseId>,
16991
}
17092

17193
impl AstIdsBuilder {
172-
/// Adds `expr` to the expression ids map and returns its id.
173-
pub(super) fn record_expression(&mut self, expr: &ast::Expr) -> ScopedExpressionId {
174-
let expression_id = self.expressions_map.len().into();
175-
176-
self.expressions_map.insert(expr.into(), expression_id);
177-
178-
expression_id
179-
}
180-
18194
/// Adds `expr` to the use ids map and returns its id.
18295
pub(super) fn record_use(&mut self, expr: impl Into<ExpressionNodeKey>) -> ScopedUseId {
18396
let use_id = self.uses_map.len().into();
@@ -188,11 +101,9 @@ impl AstIdsBuilder {
188101
}
189102

190103
pub(super) fn finish(mut self) -> AstIds {
191-
self.expressions_map.shrink_to_fit();
192104
self.uses_map.shrink_to_fit();
193105

194106
AstIds {
195-
expressions_map: self.expressions_map,
196107
uses_map: self.uses_map,
197108
}
198109
}
@@ -219,6 +130,12 @@ pub(crate) mod node_key {
219130
}
220131
}
221132

133+
impl From<&ast::ExprCall> for ExpressionNodeKey {
134+
fn from(value: &ast::ExprCall) -> Self {
135+
Self(NodeKey::from_node(value))
136+
}
137+
}
138+
222139
impl From<&ast::Identifier> for ExpressionNodeKey {
223140
fn from(value: &ast::Identifier) -> Self {
224141
Self(NodeKey::from_node(value))

crates/ty_python_semantic/src/semantic_index/builder.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1918,7 +1918,6 @@ impl<'ast> Visitor<'ast> for SemanticIndexBuilder<'_, 'ast> {
19181918

19191919
self.scopes_by_expression
19201920
.insert(expr.into(), self.current_scope());
1921-
self.current_ast_ids().record_expression(expr);
19221921

19231922
let node_key = NodeKey::from_node(expr);
19241923

crates/ty_python_semantic/src/semantic_model.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use ruff_source_file::LineIndex;
77
use crate::Db;
88
use crate::module_name::ModuleName;
99
use crate::module_resolver::{KnownModule, Module, resolve_module};
10-
use crate::semantic_index::ast_ids::HasScopedExpressionId;
1110
use crate::semantic_index::place::FileScopeId;
1211
use crate::semantic_index::semantic_index;
1312
use crate::types::ide_support::all_declarations_and_bindings;
@@ -159,8 +158,7 @@ impl HasType for ast::ExprRef<'_> {
159158
let file_scope = index.expression_scope_id(*self);
160159
let scope = file_scope.to_scope_id(model.db, model.file);
161160

162-
let expression_id = self.scoped_expression_id(model.db, scope);
163-
infer_scope_types(model.db, scope).expression_type(expression_id)
161+
infer_scope_types(model.db, scope).expression_type(*self)
164162
}
165163
}
166164

crates/ty_python_semantic/src/types.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ pub(crate) use self::subclass_of::{SubclassOfInner, SubclassOfType};
3333
use crate::module_name::ModuleName;
3434
use crate::module_resolver::{KnownModule, resolve_module};
3535
use crate::place::{Boundness, Place, PlaceAndQualifiers, imported_symbol};
36-
use crate::semantic_index::ast_ids::HasScopedExpressionId;
3736
use crate::semantic_index::definition::Definition;
3837
use crate::semantic_index::place::{ScopeId, ScopedPlaceId};
3938
use crate::semantic_index::{imported_modules, place_table, semantic_index};
@@ -143,18 +142,17 @@ fn definition_expression_type<'db>(
143142
let index = semantic_index(db, file);
144143
let file_scope = index.expression_scope_id(expression);
145144
let scope = file_scope.to_scope_id(db, file);
146-
let expr_id = expression.scoped_expression_id(db, scope);
147145
if scope == definition.scope(db) {
148146
// expression is in the definition scope
149147
let inference = infer_definition_types(db, definition);
150-
if let Some(ty) = inference.try_expression_type(expr_id) {
148+
if let Some(ty) = inference.try_expression_type(expression) {
151149
ty
152150
} else {
153-
infer_deferred_types(db, definition).expression_type(expr_id)
151+
infer_deferred_types(db, definition).expression_type(expression)
154152
}
155153
} else {
156154
// expression is in a type-params sub-scope
157-
infer_scope_types(db, scope).expression_type(expr_id)
155+
infer_scope_types(db, scope).expression_type(expression)
158156
}
159157
}
160158

crates/ty_python_semantic/src/types/class.rs

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ use crate::{
3232
known_module_symbol, place_from_bindings, place_from_declarations,
3333
},
3434
semantic_index::{
35-
ast_ids::HasScopedExpressionId,
3635
attribute_assignments,
3736
definition::{DefinitionKind, TargetKind},
3837
place::ScopeId,
@@ -1861,10 +1860,8 @@ impl<'db> ClassLiteral<'db> {
18611860
// [.., self.name, ..] = <value>
18621861

18631862
let unpacked = infer_unpack_types(db, unpack);
1864-
let target_ast_id = assign
1865-
.target(&module)
1866-
.scoped_expression_id(db, method_scope);
1867-
let inferred_ty = unpacked.expression_type(target_ast_id);
1863+
1864+
let inferred_ty = unpacked.expression_type(assign.target(&module));
18681865

18691866
union_of_inferred_types = union_of_inferred_types.add(inferred_ty);
18701867
}
@@ -1890,10 +1887,8 @@ impl<'db> ClassLiteral<'db> {
18901887
// for .., self.name, .. in <iterable>:
18911888

18921889
let unpacked = infer_unpack_types(db, unpack);
1893-
let target_ast_id = for_stmt
1894-
.target(&module)
1895-
.scoped_expression_id(db, method_scope);
1896-
let inferred_ty = unpacked.expression_type(target_ast_id);
1890+
let inferred_ty =
1891+
unpacked.expression_type(for_stmt.target(&module));
18971892

18981893
union_of_inferred_types = union_of_inferred_types.add(inferred_ty);
18991894
}
@@ -1921,10 +1916,8 @@ impl<'db> ClassLiteral<'db> {
19211916
// with <context_manager> as .., self.name, ..:
19221917

19231918
let unpacked = infer_unpack_types(db, unpack);
1924-
let target_ast_id = with_item
1925-
.target(&module)
1926-
.scoped_expression_id(db, method_scope);
1927-
let inferred_ty = unpacked.expression_type(target_ast_id);
1919+
let inferred_ty =
1920+
unpacked.expression_type(with_item.target(&module));
19281921

19291922
union_of_inferred_types = union_of_inferred_types.add(inferred_ty);
19301923
}
@@ -1951,10 +1944,9 @@ impl<'db> ClassLiteral<'db> {
19511944
// [... for .., self.name, .. in <iterable>]
19521945

19531946
let unpacked = infer_unpack_types(db, unpack);
1954-
let target_ast_id = comprehension
1955-
.target(&module)
1956-
.scoped_expression_id(db, unpack.target_scope(db));
1957-
let inferred_ty = unpacked.expression_type(target_ast_id);
1947+
1948+
let inferred_ty =
1949+
unpacked.expression_type(comprehension.target(&module));
19581950

19591951
union_of_inferred_types = union_of_inferred_types.add(inferred_ty);
19601952
}

0 commit comments

Comments
 (0)