@@ -913,37 +913,24 @@ def check_overlapping_overloads(self, defn: OverloadedFuncDef) -> None:
913913
914914 is_descriptor_get = defn .info and defn .name == "__get__"
915915
916- # Pre-extract callable types and literal fingerprints for each overload item.
917- item_sigs : list [CallableType | None ] = []
918- item_literal_fingerprints : list [LiteralFingerprint ] = []
919- for item in defn .items :
920- assert isinstance (item , Decorator )
921- sig = self .extract_callable_type (item .var .type , item )
922- item_sigs .append (sig )
923- item_literal_fingerprints .append (
924- build_literal_fingerprint (sig ) if sig is not None else {}
925- )
926-
916+ # Pre-extract callable types and literal fingerprints for each overload
917+ # item, skipping items whose signature could not be extracted.
918+ # Each entry is (original 0-based index, Decorator, sig, fingerprint).
919+ prepared_items : list [tuple [int , Decorator , CallableType , LiteralFingerprint ]] = []
927920 for i , item in enumerate (defn .items ):
928921 assert isinstance (item , Decorator )
929- sig1 = item_sigs [i ]
930- if sig1 is None :
931- continue
932-
933- for j , item2 in enumerate (defn .items [i + 1 :], i + 1 ):
934- assert isinstance (item2 , Decorator )
935- sig2 = item_sigs [j ]
936- if sig2 is None :
937- continue
922+ sig = self .extract_callable_type (item .var .type , item )
923+ if sig is not None :
924+ prepared_items .append ((i , item , sig , build_literal_fingerprint (sig )))
938925
926+ for prepared_items_i , (i , item , sig1 , literals_fingerprint1 ) in enumerate (prepared_items ):
927+ for j , item2 , sig2 , literals_fingerprint2 in prepared_items [prepared_items_i + 1 :]:
939928 if not are_argument_counts_overlapping (sig1 , sig2 ):
940929 continue
941930
942931 # If there is any argument position where both overloads
943932 # carry a LiteralType with different values they are disjoint.
944- if literal_args_are_disjoint (
945- item_literal_fingerprints [i ], item_literal_fingerprints [j ]
946- ):
933+ if literal_args_are_disjoint (literals_fingerprint1 , literals_fingerprint2 ):
947934 continue
948935
949936 if overload_can_never_match (sig1 , sig2 ):
@@ -8978,36 +8965,22 @@ def detach_callable(typ: CallableType, class_type_vars: list[TypeVarLikeType]) -
89788965 return typ .copy_modified (variables = list (typ .variables ) + class_type_vars )
89798966
89808967
8981- # Fingerprint type for literal-disjointness checks: maps argument index to
8968+ # Fingerprint type for literal-disjointedness checks: maps argument index to
89828969# the set of (Python type of value, value) pairs present at that position.
89838970# Using type(value) as part of the key means Literal[1] (int) and
89848971# Literal[True] (bool) are kept distinct even though 1 == True in Python.
89858972# A union such as Literal["a", "b"] or Literal["a"] | Literal["b"] produces
8986- # a frozenset of two entries; a plain Literal["a"] produces a singleton set.
8973+ # a frozenset of two entries; a plain Literal["a"] produces a length 1 set.
89878974LiteralFingerprint = dict [int , frozenset [tuple [type , LiteralValue ]]]
89888975
89898976
8990- def literal_args_are_disjoint (fp1 : LiteralFingerprint , fp2 : LiteralFingerprint ) -> bool :
8991- """Return True if two overloads are provably disjoint via a Literal argument.
8992-
8993- If there is any argument position where both carry only LiteralType values
8994- and those value sets are disjoint, no single call can match both overloads
8995- and the pairwise overlap check can be skipped entirely.
8996- """
8997- for idx , vals1 in fp1 .items ():
8998- vals2 = fp2 .get (idx )
8999- if vals2 is not None and vals1 .isdisjoint (vals2 ):
9000- return True
9001- return False
9002-
9003-
90048977def build_literal_fingerprint (sig : CallableType ) -> LiteralFingerprint :
90058978 """Build a LiteralFingerprint for one overload signature.
90068979
90078980 Each argument position that carries only LiteralType values (including
90088981 unions such as ``Literal["a", "b"]``) is recorded as a frozenset of
90098982 ``(type(value), value)`` pairs. Positions with any non-literal type are
9010- omitted so the disjointness check is conservative.
8983+ omitted so the disjointedness check is conservative.
90118984 """
90128985 fingerprint : LiteralFingerprint = {}
90138986 for idx , arg_type in enumerate (sig .arg_types ):
@@ -9019,7 +8992,7 @@ def build_literal_fingerprint(sig: CallableType) -> LiteralFingerprint:
90198992 # represented as a UnionType of LiteralTypes. Collect all the
90208993 # literal values; if any member is not a LiteralType the whole
90218994 # position is skipped (a non-literal in the union makes it too
9022- # broad to prove disjointness ).
8995+ # broad to prove disjointedness ).
90238996 vals : set [tuple [type , LiteralValue ]] = set ()
90248997 for member in proper .items :
90258998 m = get_proper_type (member )
@@ -9033,6 +9006,20 @@ def build_literal_fingerprint(sig: CallableType) -> LiteralFingerprint:
90339006 return fingerprint
90349007
90359008
9009+ def literal_args_are_disjoint (fp1 : LiteralFingerprint , fp2 : LiteralFingerprint ) -> bool :
9010+ """Return True if two overloads are provably disjoint via a Literal argument.
9011+
9012+ If there is any argument position where both carry only LiteralType values
9013+ and those value sets are disjoint, no single call can match both overloads
9014+ and the pairwise overlap check can be skipped entirely.
9015+ """
9016+ for idx , vals1 in fp1 .items ():
9017+ vals2 = fp2 .get (idx )
9018+ if vals2 is not None and vals1 .isdisjoint (vals2 ):
9019+ return True
9020+ return False
9021+
9022+
90369023def overload_can_never_match (signature : CallableType , other : CallableType ) -> bool :
90379024 """Check if the 'other' method can never be matched due to 'signature'.
90389025
0 commit comments