[ty] Add Salsa caching to ClassLiteral::fields by AlexWaygood · Pull Request #21512 · astral-sh/ruff (original) (raw)

Should we rebase this on top of Jack's PR to see the real performance benefits? It seems more questionable to merge with the now very limited performance improvement

Hmm, I'm not totally sure I understand the standard being applied here -- I feel like we've merged lots of performance improvements in the past that showed <3% improvements on our benchmarks, especially if they didn't have any measurable memory-usage regressions :-)

As I say, I've also verified locally that this yields a huge speedup when I cherry pick it onto #21467, which currently can't be merged due to the performance regression on that branch.

But sure, I'm happy if @oconnor663 wants to just cherry-pick f407537 onto #21467.

How much does it change performance if we were to cache own_fields only? I'm asking because that would require slightly less memory

I tried this out locally (applied to Jack's PR). It has very competitive performance to this version. But I'm inclined to go with this version, since there's no memory regression reported on this PR, and since caching ClassLiteral::own_fields rather than ClassLiteral::fields makes our APIs a fair bit less ergonomic IMO. You end up always having to assign the result of class.fields(db) before iterating over it, or the borrow checker complains in several places that temporary values are being dropped while borrowed, because the return type of ClassLiteral::fields() needs to be FxIndexMap<&'db Name, &'db Field<'db>> rather than &'db FxIndexMap<Name, Field<'db>>

Details

diff --git a/crates/ty_python_semantic/src/types/class.rs b/crates/ty_python_semantic/src/types/class.rs index abc54f5626..54cfd75f9c 100644 --- a/crates/ty_python_semantic/src/types/class.rs +++ b/crates/ty_python_semantic/src/types/class.rs @@ -2593,8 +2593,9 @@ impl<'db> ClassLiteral<'db> { ))) } (CodeGeneratorKind::TypedDict, "get") => {

@@ -2824,17 +2825,19 @@ impl<'db> ClassLiteral<'db> { /// Returns a list of all annotated attributes defined in this class, or any of its superclasses. /// /// See [ClassLiteral::own_fields] for more details.

@@ -2873,11 +2876,12 @@ impl<'db> ClassLiteral<'db> { /// y: str = "a" /// ``` /// we return a map {"x": (int, None), "y": (str, Some(Literal["a"]))}.

diff --git a/crates/ty_python_semantic/src/types/diagnostic.rs b/crates/ty_python_semantic/src/types/diagnostic.rs index 66316f1537..5b3cc2b62e 100644 --- a/crates/ty_python_semantic/src/types/diagnostic.rs +++ b/crates/ty_python_semantic/src/types/diagnostic.rs @@ -3135,7 +3135,7 @@ pub(crate) fn report_invalid_key_on_typed_dict<'db>( typed_dict_ty: Type<'db>, full_object_ty: Option<Type<'db>>, key_ty: Type<'db>,

) { let db = context.db(); if let Some(builder) = context.report_lint(&INVALID_KEY, key_node) { @@ -3197,8 +3197,8 @@ pub(crate) fn report_invalid_key_on_typed_dict<'db>( pub(super) fn report_namedtuple_field_without_default_after_field_with_default<'db>( context: &InferContext<'db, '_>, class: ClassLiteral<'db>,

) { let db = context.db(); let module = context.module(); diff --git a/crates/ty_python_semantic/src/types/infer/builder.rs b/crates/ty_python_semantic/src/types/infer/builder.rs index caa925ad74..778fe849b2 100644 --- a/crates/ty_python_semantic/src/types/infer/builder.rs +++ b/crates/ty_python_semantic/src/types/infer/builder.rs @@ -4,6 +4,7 @@ use itertools::{Either, Itertools}; use ruff_db::diagnostic::{Annotation, DiagnosticId, Severity}; use ruff_db::files::File; use ruff_db::parsed::ParsedModuleRef; +use ruff_python_ast:📛:Name; use ruff_python_ast::visitor::{Visitor, walk_expr}; use ruff_python_ast::{ self as ast, AnyNodeRef, ExprContext, HasNodeIndex, NodeIndex, PythonVersion, @@ -613,12 +614,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { ) { field_with_default_encountered = Some((field_name, field.single_declaration));

@@ -919,9 +919,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { CodeGeneratorKind::from_class(self.db(), class, None) { let specialization = None;

@@ -7269,7 +7269,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { }

         let value_ty = if let Some(Type::StringLiteral(key)) = key_ty

@@ -7954,7 +7954,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { Type::TypedDict(typed_dict_ty), None, key_ty,

@@ -11285,7 +11285,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { value_ty, None, slice_ty,

diff --git a/crates/ty_python_semantic/src/types/typed_dict.rs b/crates/ty_python_semantic/src/types/typed_dict.rs index 1b33d7111f..521329f2c9 100644 --- a/crates/ty_python_semantic/src/types/typed_dict.rs +++ b/crates/ty_python_semantic/src/types/typed_dict.rs @@ -58,7 +58,7 @@ impl<'db> TypedDictType<'db> { self.defining_class }

@@ -327,7 +327,7 @@ pub(super) fn validate_typed_dict_key_assignment<'db, 'ast>( Type::TypedDict(typed_dict), full_object_ty, Type::string_literal(db, key),