Fix rare multithreaded related hang (#21038)

This commit is contained in:
Micha Reiser 2025-10-23 09:25:16 +02:00 committed by GitHub
parent 6c18f18450
commit 76a55314e4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 42 additions and 420 deletions

6
Cargo.lock generated
View file

@ -3563,7 +3563,7 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "salsa"
version = "0.24.0"
source = "git+https://github.com/salsa-rs/salsa.git?rev=ef9f9329be6923acd050c8dddd172e3bc93e8051#ef9f9329be6923acd050c8dddd172e3bc93e8051"
source = "git+https://github.com/salsa-rs/salsa.git?rev=d38145c29574758de7ffbe8a13cd4584c3b09161#d38145c29574758de7ffbe8a13cd4584c3b09161"
dependencies = [
"boxcar",
"compact_str",
@ -3587,12 +3587,12 @@ dependencies = [
[[package]]
name = "salsa-macro-rules"
version = "0.24.0"
source = "git+https://github.com/salsa-rs/salsa.git?rev=ef9f9329be6923acd050c8dddd172e3bc93e8051#ef9f9329be6923acd050c8dddd172e3bc93e8051"
source = "git+https://github.com/salsa-rs/salsa.git?rev=d38145c29574758de7ffbe8a13cd4584c3b09161#d38145c29574758de7ffbe8a13cd4584c3b09161"
[[package]]
name = "salsa-macros"
version = "0.24.0"
source = "git+https://github.com/salsa-rs/salsa.git?rev=ef9f9329be6923acd050c8dddd172e3bc93e8051#ef9f9329be6923acd050c8dddd172e3bc93e8051"
source = "git+https://github.com/salsa-rs/salsa.git?rev=d38145c29574758de7ffbe8a13cd4584c3b09161#d38145c29574758de7ffbe8a13cd4584c3b09161"
dependencies = [
"proc-macro2",
"quote",

View file

@ -146,7 +146,7 @@ regex-automata = { version = "0.4.9" }
rustc-hash = { version = "2.0.0" }
rustc-stable-hash = { version = "0.1.2" }
# When updating salsa, make sure to also update the revision in `fuzz/Cargo.toml`
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "ef9f9329be6923acd050c8dddd172e3bc93e8051", default-features = false, features = [
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "d38145c29574758de7ffbe8a13cd4584c3b09161", default-features = false, features = [
"compact_str",
"macros",
"salsa_unstable",

View file

@ -10,23 +10,13 @@ use crate::semantic_index::{SemanticIndex, semantic_index};
use crate::types::{Truthiness, Type, TypeContext, infer_expression_types};
use crate::{Db, ModuleName, resolve_module};
#[allow(clippy::ref_option)]
fn dunder_all_names_cycle_recover(
_db: &dyn Db,
_value: &Option<FxHashSet<Name>>,
_count: u32,
_file: File,
) -> salsa::CycleRecoveryAction<Option<FxHashSet<Name>>> {
salsa::CycleRecoveryAction::Iterate
}
fn dunder_all_names_cycle_initial(_db: &dyn Db, _file: File) -> Option<FxHashSet<Name>> {
None
}
/// Returns a set of names in the `__all__` variable for `file`, [`None`] if it is not defined or
/// if it contains invalid elements.
#[salsa::tracked(returns(as_ref), cycle_fn=dunder_all_names_cycle_recover, cycle_initial=dunder_all_names_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
#[salsa::tracked(returns(as_ref), cycle_initial=dunder_all_names_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
pub(crate) fn dunder_all_names(db: &dyn Db, file: File) -> Option<FxHashSet<Name>> {
let _span = tracing::trace_span!("dunder_all_names", file=?file.path(db)).entered();

View file

@ -695,18 +695,6 @@ impl<'db> From<Place<'db>> for PlaceAndQualifiers<'db> {
}
}
fn place_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &PlaceAndQualifiers<'db>,
_count: u32,
_scope: ScopeId<'db>,
_place_id: ScopedPlaceId,
_requires_explicit_reexport: RequiresExplicitReExport,
_considered_definitions: ConsideredDefinitions,
) -> salsa::CycleRecoveryAction<PlaceAndQualifiers<'db>> {
salsa::CycleRecoveryAction::Iterate
}
fn place_cycle_initial<'db>(
_db: &'db dyn Db,
_scope: ScopeId<'db>,
@ -717,7 +705,7 @@ fn place_cycle_initial<'db>(
Place::bound(Type::Never).into()
}
#[salsa::tracked(cycle_fn=place_cycle_recover, cycle_initial=place_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
#[salsa::tracked(cycle_initial=place_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
pub(crate) fn place_by_id<'db>(
db: &'db dyn Db,
scope: ScopeId<'db>,
@ -1511,7 +1499,6 @@ mod implicit_globals {
#[salsa::tracked(
returns(deref),
cycle_initial=module_type_symbols_initial,
cycle_fn=module_type_symbols_cycle_recover,
heap_size=ruff_memory_usage::heap_size
)]
fn module_type_symbols<'db>(db: &'db dyn Db) -> smallvec::SmallVec<[ast::name::Name; 8]> {
@ -1545,14 +1532,6 @@ mod implicit_globals {
smallvec::SmallVec::default()
}
fn module_type_symbols_cycle_recover(
_db: &dyn Db,
_value: &smallvec::SmallVec<[ast::name::Name; 8]>,
_count: u32,
) -> salsa::CycleRecoveryAction<smallvec::SmallVec<[ast::name::Name; 8]>> {
salsa::CycleRecoveryAction::Iterate
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -30,20 +30,11 @@ use rustc_hash::FxHashMap;
use crate::{Db, module_name::ModuleName, resolve_module};
fn exports_cycle_recover(
_db: &dyn Db,
_value: &[Name],
_count: u32,
_file: File,
) -> salsa::CycleRecoveryAction<Box<[Name]>> {
salsa::CycleRecoveryAction::Iterate
}
fn exports_cycle_initial(_db: &dyn Db, _file: File) -> Box<[Name]> {
Box::default()
}
#[salsa::tracked(returns(deref), cycle_fn=exports_cycle_recover, cycle_initial=exports_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
#[salsa::tracked(returns(deref), cycle_initial=exports_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
pub(super) fn exported_names(db: &dyn Db, file: File) -> Box<[Name]> {
let module = parsed_module(db, file).load(db);
let mut finder = ExportFinder::new(db, file);

View file

@ -369,17 +369,6 @@ impl Default for MemberLookupPolicy {
}
}
fn member_lookup_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &PlaceAndQualifiers<'db>,
_count: u32,
_self: Type<'db>,
_name: Name,
_policy: MemberLookupPolicy,
) -> salsa::CycleRecoveryAction<PlaceAndQualifiers<'db>> {
salsa::CycleRecoveryAction::Iterate
}
fn member_lookup_cycle_initial<'db>(
_db: &'db dyn Db,
_self: Type<'db>,
@ -389,17 +378,6 @@ fn member_lookup_cycle_initial<'db>(
Place::bound(Type::Never).into()
}
fn class_lookup_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &PlaceAndQualifiers<'db>,
_count: u32,
_self: Type<'db>,
_name: Name,
_policy: MemberLookupPolicy,
) -> salsa::CycleRecoveryAction<PlaceAndQualifiers<'db>> {
salsa::CycleRecoveryAction::Iterate
}
fn class_lookup_cycle_initial<'db>(
_db: &'db dyn Db,
_self: Type<'db>,
@ -409,21 +387,6 @@ fn class_lookup_cycle_initial<'db>(
Place::bound(Type::Never).into()
}
#[allow(clippy::trivially_copy_pass_by_ref)]
fn variance_cycle_recover<'db, T>(
_db: &'db dyn Db,
_value: &TypeVarVariance,
count: u32,
_self: T,
_typevar: BoundTypeVarInstance<'db>,
) -> salsa::CycleRecoveryAction<TypeVarVariance> {
assert!(
count <= 2,
"Should only be able to cycle at most twice: there are only three levels in the lattice, each cycle should move us one"
);
salsa::CycleRecoveryAction::Iterate
}
fn variance_cycle_initial<'db, T>(
_db: &'db dyn Db,
_self: T,
@ -1582,7 +1545,7 @@ impl<'db> Type<'db> {
/// Return `true` if it would be redundant to add `self` to a union that already contains `other`.
///
/// See [`TypeRelation::Redundancy`] for more details.
#[salsa::tracked(cycle_fn=is_redundant_with_cycle_recover, cycle_initial=is_redundant_with_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
#[salsa::tracked(cycle_initial=is_redundant_with_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
pub(crate) fn is_redundant_with(self, db: &'db dyn Db, other: Type<'db>) -> bool {
self.has_relation_to(db, other, InferableTypeVars::None, TypeRelation::Redundancy)
.is_always_satisfied()
@ -3628,7 +3591,7 @@ impl<'db> Type<'db> {
self.class_member_with_policy(db, name, MemberLookupPolicy::default())
}
#[salsa::tracked(cycle_fn=class_lookup_cycle_recover, cycle_initial=class_lookup_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
#[salsa::tracked(cycle_initial=class_lookup_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
fn class_member_with_policy(
self,
db: &'db dyn Db,
@ -4093,7 +4056,7 @@ impl<'db> Type<'db> {
/// Similar to [`Type::member`], but allows the caller to specify what policy should be used
/// when looking up attributes. See [`MemberLookupPolicy`] for more information.
#[salsa::tracked(cycle_fn=member_lookup_cycle_recover, cycle_initial=member_lookup_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
#[salsa::tracked(cycle_initial=member_lookup_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
fn member_lookup_with_policy(
self,
db: &'db dyn Db,
@ -6616,7 +6579,7 @@ impl<'db> Type<'db> {
/// Note that this does not specialize generic classes, functions, or type aliases! That is a
/// different operation that is performed explicitly (via a subscript operation), or implicitly
/// via a call to the generic object.
#[salsa::tracked(heap_size=ruff_memory_usage::heap_size, cycle_fn=apply_specialization_cycle_recover, cycle_initial=apply_specialization_cycle_initial)]
#[salsa::tracked(heap_size=ruff_memory_usage::heap_size, cycle_initial=apply_specialization_cycle_initial)]
pub(crate) fn apply_specialization(
self,
db: &'db dyn Db,
@ -7336,16 +7299,6 @@ impl<'db> VarianceInferable<'db> for Type<'db> {
}
#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_redundant_with_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &bool,
_count: u32,
_subtype: Type<'db>,
_supertype: Type<'db>,
) -> salsa::CycleRecoveryAction<bool> {
salsa::CycleRecoveryAction::Iterate
}
fn is_redundant_with_cycle_initial<'db>(
_db: &'db dyn Db,
_subtype: Type<'db>,
@ -7354,16 +7307,6 @@ fn is_redundant_with_cycle_initial<'db>(
true
}
fn apply_specialization_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &Type<'db>,
_count: u32,
_self: Type<'db>,
_specialization: Specialization<'db>,
) -> salsa::CycleRecoveryAction<Type<'db>> {
salsa::CycleRecoveryAction::Iterate
}
fn apply_specialization_cycle_initial<'db>(
_db: &'db dyn Db,
_self: Type<'db>,
@ -8340,7 +8283,6 @@ impl<'db> TypeVarInstance<'db> {
}
#[salsa::tracked(
cycle_fn=lazy_bound_or_constraints_cycle_recover,
cycle_initial=lazy_bound_or_constraints_cycle_initial,
heap_size=ruff_memory_usage::heap_size
)]
@ -8365,7 +8307,6 @@ impl<'db> TypeVarInstance<'db> {
}
#[salsa::tracked(
cycle_fn=lazy_bound_or_constraints_cycle_recover,
cycle_initial=lazy_bound_or_constraints_cycle_initial,
heap_size=ruff_memory_usage::heap_size
)]
@ -8443,16 +8384,6 @@ impl<'db> TypeVarInstance<'db> {
}
}
#[allow(clippy::ref_option)]
fn lazy_bound_or_constraints_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &Option<TypeVarBoundOrConstraints<'db>>,
_count: u32,
_self: TypeVarInstance<'db>,
) -> salsa::CycleRecoveryAction<Option<TypeVarBoundOrConstraints<'db>>> {
salsa::CycleRecoveryAction::Iterate
}
fn lazy_bound_or_constraints_cycle_initial<'db>(
_db: &'db dyn Db,
_self: TypeVarInstance<'db>,
@ -9975,16 +9906,6 @@ fn walk_bound_method_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
visitor.visit_type(db, method.self_instance(db));
}
#[allow(clippy::trivially_copy_pass_by_ref)]
fn into_callable_type_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &CallableType<'db>,
_count: u32,
_self: BoundMethodType<'db>,
) -> salsa::CycleRecoveryAction<CallableType<'db>> {
salsa::CycleRecoveryAction::Iterate
}
fn into_callable_type_cycle_initial<'db>(
db: &'db dyn Db,
_self: BoundMethodType<'db>,
@ -10013,7 +9934,7 @@ impl<'db> BoundMethodType<'db> {
Self::new(db, self.function(db), f(self.self_instance(db)))
}
#[salsa::tracked(cycle_fn=into_callable_type_cycle_recover, cycle_initial=into_callable_type_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
#[salsa::tracked(cycle_initial=into_callable_type_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
pub(crate) fn into_callable_type(self, db: &'db dyn Db) -> CallableType<'db> {
let function = self.function(db);
let self_instance = self.typing_self_type(db);
@ -10777,7 +10698,7 @@ impl<'db> PEP695TypeAliasType<'db> {
}
/// The RHS type of a PEP-695 style type alias with specialization applied.
#[salsa::tracked(cycle_fn=value_type_cycle_recover, cycle_initial=value_type_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
#[salsa::tracked(cycle_initial=value_type_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
pub(crate) fn value_type(self, db: &'db dyn Db) -> Type<'db> {
let value_type = self.raw_value_type(db);
@ -10840,7 +10761,7 @@ impl<'db> PEP695TypeAliasType<'db> {
self.specialization(db).is_some()
}
#[salsa::tracked(cycle_fn=generic_context_cycle_recover, cycle_initial=generic_context_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
#[salsa::tracked(cycle_initial=generic_context_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
pub(crate) fn generic_context(self, db: &'db dyn Db) -> Option<GenericContext<'db>> {
let scope = self.rhs_scope(db);
let file = scope.file(db);
@ -10863,16 +10784,6 @@ impl<'db> PEP695TypeAliasType<'db> {
}
}
#[allow(clippy::ref_option, clippy::trivially_copy_pass_by_ref)]
fn generic_context_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &Option<GenericContext<'db>>,
_count: u32,
_self: PEP695TypeAliasType<'db>,
) -> salsa::CycleRecoveryAction<Option<GenericContext<'db>>> {
salsa::CycleRecoveryAction::Iterate
}
fn generic_context_cycle_initial<'db>(
_db: &'db dyn Db,
_self: PEP695TypeAliasType<'db>,
@ -10880,15 +10791,6 @@ fn generic_context_cycle_initial<'db>(
None
}
fn value_type_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &Type<'db>,
_count: u32,
_self: PEP695TypeAliasType<'db>,
) -> salsa::CycleRecoveryAction<Type<'db>> {
salsa::CycleRecoveryAction::Iterate
}
fn value_type_cycle_initial<'db>(_db: &'db dyn Db, _self: PEP695TypeAliasType<'db>) -> Type<'db> {
Type::Never
}

View file

@ -69,15 +69,6 @@ use ruff_python_ast::{self as ast, PythonVersion};
use ruff_text_size::{Ranged, TextRange};
use rustc_hash::FxHashSet;
fn explicit_bases_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &[Type<'db>],
_count: u32,
_self: ClassLiteral<'db>,
) -> salsa::CycleRecoveryAction<Box<[Type<'db>]>> {
salsa::CycleRecoveryAction::Iterate
}
fn explicit_bases_cycle_initial<'db>(
_db: &'db dyn Db,
_self: ClassLiteral<'db>,
@ -85,16 +76,6 @@ fn explicit_bases_cycle_initial<'db>(
Box::default()
}
#[expect(clippy::ref_option, clippy::trivially_copy_pass_by_ref)]
fn inheritance_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &Option<InheritanceCycle>,
_count: u32,
_self: ClassLiteral<'db>,
) -> salsa::CycleRecoveryAction<Option<InheritanceCycle>> {
salsa::CycleRecoveryAction::Iterate
}
fn inheritance_cycle_initial<'db>(
_db: &'db dyn Db,
_self: ClassLiteral<'db>,
@ -102,17 +83,6 @@ fn inheritance_cycle_initial<'db>(
None
}
fn implicit_attribute_recover<'db>(
_db: &'db dyn Db,
_value: &Member<'db>,
_count: u32,
_class_body_scope: ScopeId<'db>,
_name: String,
_target_method_decorator: MethodDecorator,
) -> salsa::CycleRecoveryAction<Member<'db>> {
salsa::CycleRecoveryAction::Iterate
}
fn implicit_attribute_initial<'db>(
_db: &'db dyn Db,
_class_body_scope: ScopeId<'db>,
@ -122,16 +92,6 @@ fn implicit_attribute_initial<'db>(
Member::unbound()
}
fn try_mro_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &Result<Mro<'db>, MroError<'db>>,
_count: u32,
_self: ClassLiteral<'db>,
_specialization: Option<Specialization<'db>>,
) -> salsa::CycleRecoveryAction<Result<Mro<'db>, MroError<'db>>> {
salsa::CycleRecoveryAction::Iterate
}
fn try_mro_cycle_initial<'db>(
db: &'db dyn Db,
self_: ClassLiteral<'db>,
@ -143,32 +103,11 @@ fn try_mro_cycle_initial<'db>(
))
}
#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_typed_dict_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &bool,
_count: u32,
_self: ClassLiteral<'db>,
) -> salsa::CycleRecoveryAction<bool> {
salsa::CycleRecoveryAction::Iterate
}
#[allow(clippy::unnecessary_wraps)]
fn is_typed_dict_cycle_initial<'db>(_db: &'db dyn Db, _self: ClassLiteral<'db>) -> bool {
false
}
fn try_metaclass_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &Result<(Type<'db>, Option<DataclassTransformerParams>), MetaclassError<'db>>,
_count: u32,
_self: ClassLiteral<'db>,
) -> salsa::CycleRecoveryAction<
Result<(Type<'db>, Option<DataclassTransformerParams<'db>>), MetaclassError<'db>>,
> {
salsa::CycleRecoveryAction::Iterate
}
#[allow(clippy::unnecessary_wraps)]
fn try_metaclass_cycle_initial<'db>(
_db: &'db dyn Db,
@ -196,9 +135,7 @@ impl<'db> CodeGeneratorKind<'db> {
class: ClassLiteral<'db>,
specialization: Option<Specialization<'db>>,
) -> Option<Self> {
#[salsa::tracked(
cycle_fn=code_generator_of_class_recover,
cycle_initial=code_generator_of_class_initial,
#[salsa::tracked(cycle_initial=code_generator_of_class_initial,
heap_size=ruff_memory_usage::heap_size
)]
fn code_generator_of_class<'db>(
@ -238,17 +175,6 @@ impl<'db> CodeGeneratorKind<'db> {
None
}
#[expect(clippy::ref_option)]
fn code_generator_of_class_recover<'db>(
_db: &'db dyn Db,
_value: &Option<CodeGeneratorKind<'db>>,
_count: u32,
_class: ClassLiteral<'db>,
_specialization: Option<Specialization<'db>>,
) -> salsa::CycleRecoveryAction<Option<CodeGeneratorKind<'db>>> {
salsa::CycleRecoveryAction::Iterate
}
code_generator_of_class(db, class, specialization)
}
@ -1106,7 +1032,7 @@ impl<'db> ClassType<'db> {
/// Return a callable type (or union of callable types) that represents the callable
/// constructor signature of this class.
#[salsa::tracked(cycle_fn=into_callable_cycle_recover, cycle_initial=into_callable_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
#[salsa::tracked(cycle_initial=into_callable_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
pub(super) fn into_callable(self, db: &'db dyn Db) -> Type<'db> {
let self_ty = Type::from(self);
let metaclass_dunder_call_function_symbol = self_ty
@ -1268,16 +1194,6 @@ impl<'db> ClassType<'db> {
}
}
#[allow(clippy::trivially_copy_pass_by_ref)]
fn into_callable_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &Type<'db>,
_count: u32,
_self: ClassType<'db>,
) -> salsa::CycleRecoveryAction<Type<'db>> {
salsa::CycleRecoveryAction::Iterate
}
fn into_callable_cycle_initial<'db>(_db: &'db dyn Db, _self: ClassType<'db>) -> Type<'db> {
Type::Never
}
@ -1427,17 +1343,6 @@ pub struct ClassLiteral<'db> {
// The Salsa heap is tracked separately.
impl get_size2::GetSize for ClassLiteral<'_> {}
#[expect(clippy::ref_option)]
#[allow(clippy::trivially_copy_pass_by_ref)]
fn generic_context_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &Option<GenericContext<'db>>,
_count: u32,
_self: ClassLiteral<'db>,
) -> salsa::CycleRecoveryAction<Option<GenericContext<'db>>> {
salsa::CycleRecoveryAction::Iterate
}
fn generic_context_cycle_initial<'db>(
_db: &'db dyn Db,
_self: ClassLiteral<'db>,
@ -1478,9 +1383,7 @@ impl<'db> ClassLiteral<'db> {
self.pep695_generic_context(db).is_some()
}
#[salsa::tracked(
cycle_fn=generic_context_cycle_recover,
cycle_initial=generic_context_cycle_initial,
#[salsa::tracked(cycle_initial=generic_context_cycle_initial,
heap_size=ruff_memory_usage::heap_size,
)]
pub(crate) fn pep695_generic_context(self, db: &'db dyn Db) -> Option<GenericContext<'db>> {
@ -1505,9 +1408,7 @@ impl<'db> ClassLiteral<'db> {
})
}
#[salsa::tracked(
cycle_fn=generic_context_cycle_recover,
cycle_initial=generic_context_cycle_initial,
#[salsa::tracked(cycle_initial=generic_context_cycle_initial,
heap_size=ruff_memory_usage::heap_size,
)]
pub(crate) fn inherited_legacy_generic_context(
@ -1691,7 +1592,7 @@ impl<'db> ClassLiteral<'db> {
///
/// Were this not a salsa query, then the calling query
/// would depend on the class's AST and rerun for every change in that file.
#[salsa::tracked(returns(deref), cycle_fn=explicit_bases_cycle_recover, cycle_initial=explicit_bases_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
#[salsa::tracked(returns(deref), cycle_initial=explicit_bases_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
pub(super) fn explicit_bases(self, db: &'db dyn Db) -> Box<[Type<'db>]> {
tracing::trace!("ClassLiteral::explicit_bases_query: {}", self.name(db));
@ -1827,7 +1728,7 @@ impl<'db> ClassLiteral<'db> {
/// attribute on a class at runtime.
///
/// [method resolution order]: https://docs.python.org/3/glossary.html#term-method-resolution-order
#[salsa::tracked(returns(as_ref), cycle_fn=try_mro_cycle_recover, cycle_initial=try_mro_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
#[salsa::tracked(returns(as_ref), cycle_initial=try_mro_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
pub(super) fn try_mro(
self,
db: &'db dyn Db,
@ -1877,9 +1778,7 @@ impl<'db> ClassLiteral<'db> {
/// Return `true` if this class constitutes a typed dict specification (inherits from
/// `typing.TypedDict`, either directly or indirectly).
#[salsa::tracked(
cycle_fn=is_typed_dict_cycle_recover,
cycle_initial=is_typed_dict_cycle_initial,
#[salsa::tracked(cycle_initial=is_typed_dict_cycle_initial,
heap_size=ruff_memory_usage::heap_size
)]
pub(super) fn is_typed_dict(self, db: &'db dyn Db) -> bool {
@ -1940,9 +1839,7 @@ impl<'db> ClassLiteral<'db> {
}
/// Return the metaclass of this class, or an error if the metaclass cannot be inferred.
#[salsa::tracked(
cycle_fn=try_metaclass_cycle_recover,
cycle_initial=try_metaclass_cycle_initial,
#[salsa::tracked(cycle_initial=try_metaclass_cycle_initial,
heap_size=ruff_memory_usage::heap_size,
)]
pub(super) fn try_metaclass(
@ -3124,9 +3021,7 @@ impl<'db> ClassLiteral<'db> {
)
}
#[salsa::tracked(
cycle_fn=implicit_attribute_recover,
cycle_initial=implicit_attribute_initial,
#[salsa::tracked(cycle_initial=implicit_attribute_initial,
heap_size=ruff_memory_usage::heap_size,
)]
fn implicit_attribute_inner(
@ -3562,7 +3457,7 @@ impl<'db> ClassLiteral<'db> {
///
/// A class definition like this will fail at runtime,
/// but we must be resilient to it or we could panic.
#[salsa::tracked(cycle_fn=inheritance_cycle_recover, cycle_initial=inheritance_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
#[salsa::tracked(cycle_initial=inheritance_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
pub(super) fn inheritance_cycle(self, db: &'db dyn Db) -> Option<InheritanceCycle> {
/// Return `true` if the class is cyclically defined.
///
@ -3654,7 +3549,7 @@ impl<'db> From<ClassLiteral<'db>> for ClassType<'db> {
#[salsa::tracked]
impl<'db> VarianceInferable<'db> for ClassLiteral<'db> {
#[salsa::tracked(cycle_fn=crate::types::variance_cycle_recover, cycle_initial=crate::types::variance_cycle_initial)]
#[salsa::tracked(cycle_initial=crate::types::variance_cycle_initial)]
fn variance_of(self, db: &'db dyn Db, typevar: BoundTypeVarInstance<'db>) -> TypeVarVariance {
let typevar_in_generic_context = self
.generic_context(db)

View file

@ -36,16 +36,6 @@ impl EnumMetadata<'_> {
}
}
#[allow(clippy::ref_option, clippy::trivially_copy_pass_by_ref)]
fn enum_metadata_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &Option<EnumMetadata<'db>>,
_count: u32,
_class: ClassLiteral<'db>,
) -> salsa::CycleRecoveryAction<Option<EnumMetadata<'db>>> {
salsa::CycleRecoveryAction::Iterate
}
#[allow(clippy::unnecessary_wraps)]
fn enum_metadata_cycle_initial<'db>(
_db: &'db dyn Db,
@ -56,7 +46,7 @@ fn enum_metadata_cycle_initial<'db>(
/// List all members of an enum.
#[allow(clippy::ref_option, clippy::unnecessary_wraps)]
#[salsa::tracked(returns(as_ref), cycle_fn=enum_metadata_cycle_recover, cycle_initial=enum_metadata_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
#[salsa::tracked(returns(as_ref), cycle_initial=enum_metadata_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
pub(crate) fn enum_metadata<'db>(
db: &'db dyn Db,
class: ClassLiteral<'db>,

View file

@ -898,7 +898,7 @@ impl<'db> FunctionType<'db> {
///
/// Were this not a salsa query, then the calling query
/// would depend on the function's AST and rerun for every change in that file.
#[salsa::tracked(returns(ref), cycle_fn=signature_cycle_recover, cycle_initial=signature_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
#[salsa::tracked(returns(ref), cycle_initial=signature_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
pub(crate) fn signature(self, db: &'db dyn Db) -> CallableSignature<'db> {
self.updated_signature(db)
.cloned()
@ -915,9 +915,7 @@ impl<'db> FunctionType<'db> {
/// Were this not a salsa query, then the calling query
/// would depend on the function's AST and rerun for every change in that file.
#[salsa::tracked(
returns(ref),
cycle_fn=last_definition_signature_cycle_recover,
cycle_initial=last_definition_signature_cycle_initial,
returns(ref), cycle_initial=last_definition_signature_cycle_initial,
heap_size=ruff_memory_usage::heap_size,
)]
pub(crate) fn last_definition_signature(self, db: &'db dyn Db) -> Signature<'db> {
@ -928,9 +926,7 @@ impl<'db> FunctionType<'db> {
/// Typed externally-visible "raw" signature of the last overload or implementation of this function.
#[salsa::tracked(
returns(ref),
cycle_fn=last_definition_signature_cycle_recover,
cycle_initial=last_definition_signature_cycle_initial,
returns(ref), cycle_initial=last_definition_signature_cycle_initial,
heap_size=ruff_memory_usage::heap_size,
)]
pub(crate) fn last_definition_raw_signature(self, db: &'db dyn Db) -> Signature<'db> {
@ -1194,15 +1190,6 @@ fn is_mode_with_nontrivial_return_type<'db>(db: &'db dyn Db, mode: Type<'db>) ->
})
}
fn signature_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &CallableSignature<'db>,
_count: u32,
_function: FunctionType<'db>,
) -> salsa::CycleRecoveryAction<CallableSignature<'db>> {
salsa::CycleRecoveryAction::Iterate
}
fn signature_cycle_initial<'db>(
_db: &'db dyn Db,
_function: FunctionType<'db>,
@ -1210,15 +1197,6 @@ fn signature_cycle_initial<'db>(
CallableSignature::single(Signature::bottom())
}
fn last_definition_signature_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &Signature<'db>,
_count: u32,
_function: FunctionType<'db>,
) -> salsa::CycleRecoveryAction<Signature<'db>> {
salsa::CycleRecoveryAction::Iterate
}
fn last_definition_signature_cycle_initial<'db>(
_db: &'db dyn Db,
_function: FunctionType<'db>,

View file

@ -323,7 +323,6 @@ impl<'db> GenericContext<'db> {
#[salsa::tracked(
returns(ref),
cycle_fn=inferable_typevars_cycle_recover,
cycle_initial=inferable_typevars_cycle_initial,
heap_size=ruff_memory_usage::heap_size,
)]
@ -626,15 +625,6 @@ impl<'db> GenericContext<'db> {
}
}
fn inferable_typevars_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &FxHashSet<BoundTypeVarIdentity<'db>>,
_count: u32,
_self: GenericContext<'db>,
) -> salsa::CycleRecoveryAction<FxHashSet<BoundTypeVarIdentity<'db>>> {
salsa::CycleRecoveryAction::Iterate
}
fn inferable_typevars_cycle_initial<'db>(
_db: &'db dyn Db,
_self: GenericContext<'db>,

View file

@ -67,7 +67,7 @@ const ITERATIONS_BEFORE_FALLBACK: u32 = 10;
/// Infer all types for a [`ScopeId`], including all definitions and expressions in that scope.
/// Use when checking a scope, or needing to provide a type for an arbitrary expression in the
/// scope.
#[salsa::tracked(returns(ref), cycle_fn=scope_cycle_recover, cycle_initial=scope_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
#[salsa::tracked(returns(ref), cycle_initial=scope_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
pub(crate) fn infer_scope_types<'db>(db: &'db dyn Db, scope: ScopeId<'db>) -> ScopeInference<'db> {
let file = scope.file(db);
let _span = tracing::trace_span!("infer_scope_types", scope=?scope.as_id(), ?file).entered();
@ -81,15 +81,6 @@ pub(crate) fn infer_scope_types<'db>(db: &'db dyn Db, scope: ScopeId<'db>) -> Sc
TypeInferenceBuilder::new(db, InferenceRegion::Scope(scope), index, &module).finish_scope()
}
fn scope_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &ScopeInference<'db>,
_count: u32,
_scope: ScopeId<'db>,
) -> salsa::CycleRecoveryAction<ScopeInference<'db>> {
salsa::CycleRecoveryAction::Iterate
}
fn scope_cycle_initial<'db>(_db: &'db dyn Db, scope: ScopeId<'db>) -> ScopeInference<'db> {
ScopeInference::cycle_initial(scope)
}
@ -118,6 +109,8 @@ pub(crate) fn infer_definition_types<'db>(
fn definition_cycle_recover<'db>(
db: &'db dyn Db,
_id: salsa::Id,
_last_provisional_value: &DefinitionInference<'db>,
_value: &DefinitionInference<'db>,
count: u32,
definition: Definition<'db>,
@ -142,7 +135,7 @@ fn definition_cycle_initial<'db>(
///
/// Deferred expressions are type expressions (annotations, base classes, aliases...) in a stub
/// file, or in a file with `from __future__ import annotations`, or stringified annotations.
#[salsa::tracked(returns(ref), cycle_fn=deferred_cycle_recover, cycle_initial=deferred_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
#[salsa::tracked(returns(ref), cycle_initial=deferred_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
pub(crate) fn infer_deferred_types<'db>(
db: &'db dyn Db,
definition: Definition<'db>,
@ -163,15 +156,6 @@ pub(crate) fn infer_deferred_types<'db>(
.finish_definition()
}
fn deferred_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &DefinitionInference<'db>,
_count: u32,
_definition: Definition<'db>,
) -> salsa::CycleRecoveryAction<DefinitionInference<'db>> {
salsa::CycleRecoveryAction::Iterate
}
fn deferred_cycle_initial<'db>(
db: &'db dyn Db,
definition: Definition<'db>,
@ -239,6 +223,8 @@ pub(crate) fn infer_isolated_expression<'db>(
fn expression_cycle_recover<'db>(
db: &'db dyn Db,
_id: salsa::Id,
_last_provisional_value: &ExpressionInference<'db>,
_value: &ExpressionInference<'db>,
count: u32,
input: InferExpression<'db>,
@ -289,7 +275,7 @@ pub(crate) fn infer_expression_type<'db>(
infer_expression_type_impl(db, InferExpression::new(db, expression, tcx))
}
#[salsa::tracked(cycle_fn=single_expression_cycle_recover, cycle_initial=single_expression_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
#[salsa::tracked(cycle_initial=single_expression_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
fn infer_expression_type_impl<'db>(db: &'db dyn Db, input: InferExpression<'db>) -> Type<'db> {
let file = input.expression(db).file(db);
let module = parsed_module(db, file).load(db);
@ -299,15 +285,6 @@ fn infer_expression_type_impl<'db>(db: &'db dyn Db, input: InferExpression<'db>)
inference.expression_type(input.expression(db).node_ref(db, &module))
}
fn single_expression_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &Type<'db>,
_count: u32,
_input: InferExpression<'db>,
) -> salsa::CycleRecoveryAction<Type<'db>> {
salsa::CycleRecoveryAction::Iterate
}
fn single_expression_cycle_initial<'db>(
_db: &'db dyn Db,
_input: InferExpression<'db>,
@ -402,7 +379,7 @@ impl<'db> TypeContext<'db> {
///
/// Returns [`Truthiness::Ambiguous`] in case any non-definitely bound places
/// were encountered while inferring the type of the expression.
#[salsa::tracked(cycle_fn=static_expression_truthiness_cycle_recover, cycle_initial=static_expression_truthiness_cycle_initial, heap_size=get_size2::GetSize::get_heap_size)]
#[salsa::tracked(cycle_initial=static_expression_truthiness_cycle_initial, heap_size=get_size2::GetSize::get_heap_size)]
pub(crate) fn static_expression_truthiness<'db>(
db: &'db dyn Db,
expression: Expression<'db>,
@ -420,16 +397,6 @@ pub(crate) fn static_expression_truthiness<'db>(
inference.expression_type(node).bool(db)
}
#[expect(clippy::trivially_copy_pass_by_ref)]
fn static_expression_truthiness_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &Truthiness,
_count: u32,
_expression: Expression<'db>,
) -> salsa::CycleRecoveryAction<Truthiness> {
salsa::CycleRecoveryAction::Iterate
}
fn static_expression_truthiness_cycle_initial<'db>(
_db: &'db dyn Db,
_expression: Expression<'db>,
@ -443,7 +410,7 @@ fn static_expression_truthiness_cycle_initial<'db>(
/// involved in an unpacking operation. It returns a result-like object that can be used to get the
/// type of the variables involved in this unpacking along with any violations that are detected
/// during this unpacking.
#[salsa::tracked(returns(ref), cycle_fn=unpack_cycle_recover, cycle_initial=unpack_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
#[salsa::tracked(returns(ref), cycle_initial=unpack_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
pub(super) fn infer_unpack_types<'db>(db: &'db dyn Db, unpack: Unpack<'db>) -> UnpackResult<'db> {
let file = unpack.file(db);
let module = parsed_module(db, file).load(db);
@ -455,15 +422,6 @@ pub(super) fn infer_unpack_types<'db>(db: &'db dyn Db, unpack: Unpack<'db>) -> U
unpacker.finish()
}
fn unpack_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &UnpackResult<'db>,
_count: u32,
_unpack: Unpack<'db>,
) -> salsa::CycleRecoveryAction<UnpackResult<'db>> {
salsa::CycleRecoveryAction::Iterate
}
fn unpack_cycle_initial<'db>(_db: &'db dyn Db, _unpack: Unpack<'db>) -> UnpackResult<'db> {
UnpackResult::cycle_initial(Type::Never)
}

View file

@ -648,7 +648,7 @@ impl<'db> ProtocolInstanceType<'db> {
/// Such a protocol is therefore an equivalent type to `object`, which would in fact be
/// normalised to `object`.
pub(super) fn is_equivalent_to_object(self, db: &'db dyn Db) -> bool {
#[salsa::tracked(cycle_fn=recover, cycle_initial=initial, heap_size=ruff_memory_usage::heap_size)]
#[salsa::tracked(cycle_initial=initial, heap_size=ruff_memory_usage::heap_size)]
fn inner<'db>(db: &'db dyn Db, protocol: ProtocolInstanceType<'db>, _: ()) -> bool {
Type::object()
.satisfies_protocol(
@ -662,17 +662,6 @@ impl<'db> ProtocolInstanceType<'db> {
.is_always_satisfied()
}
#[expect(clippy::trivially_copy_pass_by_ref)]
fn recover<'db>(
_db: &'db dyn Db,
_result: &bool,
_count: u32,
_value: ProtocolInstanceType<'db>,
_: (),
) -> salsa::CycleRecoveryAction<bool> {
salsa::CycleRecoveryAction::Iterate
}
fn initial<'db>(_db: &'db dyn Db, _value: ProtocolInstanceType<'db>, _: ()) -> bool {
true
}

View file

@ -83,7 +83,6 @@ fn all_narrowing_constraints_for_pattern<'db>(
#[salsa::tracked(
returns(as_ref),
cycle_fn=constraints_for_expression_cycle_recover,
cycle_initial=constraints_for_expression_cycle_initial,
heap_size=ruff_memory_usage::heap_size,
)]
@ -98,7 +97,6 @@ fn all_narrowing_constraints_for_expression<'db>(
#[salsa::tracked(
returns(as_ref),
cycle_fn=negative_constraints_for_expression_cycle_recover,
cycle_initial=negative_constraints_for_expression_cycle_initial,
heap_size=ruff_memory_usage::heap_size,
)]
@ -120,16 +118,6 @@ fn all_negative_narrowing_constraints_for_pattern<'db>(
NarrowingConstraintsBuilder::new(db, &module, PredicateNode::Pattern(pattern), false).finish()
}
#[expect(clippy::ref_option)]
fn constraints_for_expression_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &Option<NarrowingConstraints<'db>>,
_count: u32,
_expression: Expression<'db>,
) -> salsa::CycleRecoveryAction<Option<NarrowingConstraints<'db>>> {
salsa::CycleRecoveryAction::Iterate
}
fn constraints_for_expression_cycle_initial<'db>(
_db: &'db dyn Db,
_expression: Expression<'db>,
@ -137,16 +125,6 @@ fn constraints_for_expression_cycle_initial<'db>(
None
}
#[expect(clippy::ref_option)]
fn negative_constraints_for_expression_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &Option<NarrowingConstraints<'db>>,
_count: u32,
_expression: Expression<'db>,
) -> salsa::CycleRecoveryAction<Option<NarrowingConstraints<'db>>> {
salsa::CycleRecoveryAction::Iterate
}
fn negative_constraints_for_expression_cycle_initial<'db>(
_db: &'db dyn Db,
_expression: Expression<'db>,

View file

@ -766,7 +766,7 @@ impl BoundOnClass {
}
/// Inner Salsa query for [`ProtocolClassLiteral::interface`].
#[salsa::tracked(cycle_fn=proto_interface_cycle_recover, cycle_initial=proto_interface_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
#[salsa::tracked(cycle_initial=proto_interface_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
fn cached_protocol_interface<'db>(
db: &'db dyn Db,
class: ClassType<'db>,
@ -864,15 +864,6 @@ fn cached_protocol_interface<'db>(
// If we use `expect(clippy::trivially_copy_pass_by_ref)` here,
// the lint expectation is unfulfilled on WASM
#[allow(clippy::trivially_copy_pass_by_ref)]
fn proto_interface_cycle_recover<'db>(
_db: &dyn Db,
_value: &ProtocolInterface<'db>,
_count: u32,
_class: ClassType<'db>,
) -> salsa::CycleRecoveryAction<ProtocolInterface<'db>> {
salsa::CycleRecoveryAction::Iterate
}
fn proto_interface_cycle_initial<'db>(
db: &'db dyn Db,
_class: ClassType<'db>,

View file

@ -201,7 +201,7 @@ impl<'db> TupleType<'db> {
// N.B. If this method is not Salsa-tracked, we take 10 minutes to check
// `static-frame` as part of a mypy_primer run! This is because it's called
// from `NominalInstanceType::class()`, which is a very hot method.
#[salsa::tracked(cycle_fn=to_class_type_cycle_recover, cycle_initial=to_class_type_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
#[salsa::tracked(cycle_initial=to_class_type_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
pub(crate) fn to_class_type(self, db: &'db dyn Db) -> ClassType<'db> {
let tuple_class = KnownClass::Tuple
.try_to_class_literal(db)
@ -290,15 +290,6 @@ impl<'db> TupleType<'db> {
}
}
fn to_class_type_cycle_recover<'db>(
_db: &'db dyn Db,
_value: &ClassType<'db>,
_count: u32,
_self: TupleType<'db>,
) -> salsa::CycleRecoveryAction<ClassType<'db>> {
salsa::CycleRecoveryAction::Iterate
}
fn to_class_type_cycle_initial<'db>(db: &'db dyn Db, self_: TupleType<'db>) -> ClassType<'db> {
let tuple_class = KnownClass::Tuple
.try_to_class_literal(db)

View file

@ -30,7 +30,7 @@ ty_python_semantic = { path = "../crates/ty_python_semantic" }
ty_vendored = { path = "../crates/ty_vendored" }
libfuzzer-sys = { git = "https://github.com/rust-fuzz/libfuzzer", default-features = false }
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "ef9f9329be6923acd050c8dddd172e3bc93e8051", default-features = false, features = [
salsa = { git = "https://github.com/salsa-rs/salsa.git", rev = "d38145c29574758de7ffbe8a13cd4584c3b09161", default-features = false, features = [
"compact_str",
"macros",
"salsa_unstable",