From 36d4b02fa97a7b19572f524980b7f0fbd6d5d8aa Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama <45118249+mtshiba@users.noreply.github.com> Date: Sun, 19 Oct 2025 19:13:10 +0900 Subject: [PATCH] [ty] fix non-deterministic overload function inference (#20966) --- crates/ty_python_semantic/src/types/infer/builder.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/ty_python_semantic/src/types/infer/builder.rs b/crates/ty_python_semantic/src/types/infer/builder.rs index 44ed5841f8..9c6826e71e 100644 --- a/crates/ty_python_semantic/src/types/infer/builder.rs +++ b/crates/ty_python_semantic/src/types/infer/builder.rs @@ -106,7 +106,7 @@ use crate::types::{ }; use crate::types::{ClassBase, add_inferred_python_version_hint_to_diagnostic}; use crate::unpack::{EvaluationMode, UnpackPosition}; -use crate::{Db, FxOrderSet, Program}; +use crate::{Db, FxIndexSet, FxOrderSet, Program}; mod annotation_expression; mod type_expression; @@ -257,8 +257,10 @@ pub(super) struct TypeInferenceBuilder<'db, 'ast> { /// return x /// ``` /// + /// To keep the calculation deterministic, we use an `FxIndexSet` whose order is determined by the sequence of insertion calls. + /// /// [`check_overloaded_functions`]: TypeInferenceBuilder::check_overloaded_functions - called_functions: FxHashSet>, + called_functions: FxIndexSet>, /// Whether we are in a context that binds unbound typevars. typevar_binding_context: Option>, @@ -312,7 +314,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { region, scope, return_types_and_ranges: vec![], - called_functions: FxHashSet::default(), + called_functions: FxIndexSet::default(), deferred_state: DeferredExpressionState::None, multi_inference_state: MultiInferenceState::Panic, expressions: FxHashMap::default(), @@ -949,7 +951,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { // Collect all the unique overloaded function places in this scope. This requires a set // because an overloaded function uses the same place for each of the overloads and the // implementation. - let overloaded_function_places: FxHashSet<_> = self + let overloaded_function_places: FxIndexSet<_> = self .declarations .iter() .filter_map(|(definition, ty)| { @@ -971,7 +973,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { .index .use_def_map(self.scope().file_scope_id(self.db())); - let mut public_functions = FxHashSet::default(); + let mut public_functions = FxIndexSet::default(); for place in overloaded_function_places { if let Place::Defined(Type::FunctionLiteral(function), _, Definedness::AlwaysDefined) =