@@ -5069,31 +5069,56 @@ def fast_container_type(
5069
5069
module-level constant definitions.
5070
5070
5071
5071
Limitations:
5072
+
5072
5073
- no active type context
5074
+ - at least one item
5073
5075
- no star expressions
5074
- - the joined type of all entries must be an Instance or Tuple type
5076
+ - not after deferral
5077
+ - either exactly one distinct type inside,
5078
+ or the joined type of all entries is an Instance or Tuple type,
5075
5079
"""
5076
5080
ctx = self .type_context [- 1 ]
5077
- if ctx :
5081
+ if ctx or not e .items :
5082
+ return None
5083
+ if self .chk .current_node_deferred :
5084
+ # Guarantees that all items will be Any, we'll reject it anyway.
5078
5085
return None
5079
5086
rt = self .resolved_type .get (e , None )
5080
5087
if rt is not None :
5081
5088
return rt if isinstance (rt , Instance ) else None
5082
5089
values : list [Type ] = []
5090
+ # Preserve join order while avoiding O(n) lookups at every iteration
5091
+ values_set : set [Type ] = set ()
5083
5092
for item in e .items :
5084
5093
if isinstance (item , StarExpr ):
5085
5094
# fallback to slow path
5086
5095
self .resolved_type [e ] = NoneType ()
5087
5096
return None
5088
- values .append (self .accept (item ))
5089
- vt = join .join_type_list (values )
5090
- if not allow_fast_container_literal (vt ):
5097
+
5098
+ typ = self .accept (item )
5099
+ if typ not in values_set :
5100
+ values .append (typ )
5101
+ values_set .add (typ )
5102
+
5103
+ vt = self ._first_or_join_fast_item (values )
5104
+ if vt is None :
5091
5105
self .resolved_type [e ] = NoneType ()
5092
5106
return None
5093
5107
ct = self .chk .named_generic_type (container_fullname , [vt ])
5094
5108
self .resolved_type [e ] = ct
5095
5109
return ct
5096
5110
5111
+ def _first_or_join_fast_item (self , items : list [Type ]) -> Type | None :
5112
+ if len (items ) == 1 and not self .chk .current_node_deferred :
5113
+ return items [0 ]
5114
+ typ = join .join_type_list (items )
5115
+ if not allow_fast_container_literal (typ ):
5116
+ # TODO: This is overly strict, many other types can be joined safely here.
5117
+ # However, our join implementation isn't bug-free, and some joins may produce
5118
+ # undesired `Any`s or even more surprising results.
5119
+ return None
5120
+ return typ
5121
+
5097
5122
def check_lst_expr (self , e : ListExpr | SetExpr | TupleExpr , fullname : str , tag : str ) -> Type :
5098
5123
# fast path
5099
5124
t = self .fast_container_type (e , fullname )
@@ -5254,18 +5279,30 @@ def fast_dict_type(self, e: DictExpr) -> Type | None:
5254
5279
module-level constant definitions.
5255
5280
5256
5281
Limitations:
5282
+
5257
5283
- no active type context
5284
+ - at least one item
5258
5285
- only supported star expressions are other dict instances
5259
- - the joined types of all keys and values must be Instance or Tuple types
5286
+ - either exactly one distinct type (keys and values separately) inside,
5287
+ or the joined type of all entries is an Instance or Tuple type
5260
5288
"""
5261
5289
ctx = self .type_context [- 1 ]
5262
- if ctx :
5290
+ if ctx or not e . items :
5263
5291
return None
5292
+
5293
+ if self .chk .current_node_deferred :
5294
+ # Guarantees that all items will be Any, we'll reject it anyway.
5295
+ return None
5296
+
5264
5297
rt = self .resolved_type .get (e , None )
5265
5298
if rt is not None :
5266
5299
return rt if isinstance (rt , Instance ) else None
5300
+
5267
5301
keys : list [Type ] = []
5268
5302
values : list [Type ] = []
5303
+ # Preserve join order while avoiding O(n) lookups at every iteration
5304
+ keys_set : set [Type ] = set ()
5305
+ values_set : set [Type ] = set ()
5269
5306
stargs : tuple [Type , Type ] | None = None
5270
5307
for key , value in e .items :
5271
5308
if key is None :
@@ -5280,13 +5317,25 @@ def fast_dict_type(self, e: DictExpr) -> Type | None:
5280
5317
self .resolved_type [e ] = NoneType ()
5281
5318
return None
5282
5319
else :
5283
- keys .append (self .accept (key ))
5284
- values .append (self .accept (value ))
5285
- kt = join .join_type_list (keys )
5286
- vt = join .join_type_list (values )
5287
- if not (allow_fast_container_literal (kt ) and allow_fast_container_literal (vt )):
5320
+ key_t = self .accept (key )
5321
+ if key_t not in keys_set :
5322
+ keys .append (key_t )
5323
+ keys_set .add (key_t )
5324
+ value_t = self .accept (value )
5325
+ if value_t not in values_set :
5326
+ values .append (value_t )
5327
+ values_set .add (value_t )
5328
+
5329
+ kt = self ._first_or_join_fast_item (keys )
5330
+ if kt is None :
5288
5331
self .resolved_type [e ] = NoneType ()
5289
5332
return None
5333
+
5334
+ vt = self ._first_or_join_fast_item (values )
5335
+ if vt is None :
5336
+ self .resolved_type [e ] = NoneType ()
5337
+ return None
5338
+
5290
5339
if stargs and (stargs [0 ] != kt or stargs [1 ] != vt ):
5291
5340
self .resolved_type [e ] = NoneType ()
5292
5341
return None
0 commit comments