Skip to content

perf: deduplicate fast_container_type and fast_dict_type items before joining #19409

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
45 changes: 36 additions & 9 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -5068,11 +5068,16 @@ def fast_container_type(
Limitations:
- no active type context
- no star expressions
- the joined type of all entries must be an Instance or Tuple type
- not after deferral
- either exactly one distinct type inside,
or the joined type of all entries must be an Instance or Tuple type
"""
ctx = self.type_context[-1]
if ctx:
return None
if self.chk.current_node_deferred:
# Guarantees that all items will be Any, we'll reject it anyway.
return None
rt = self.resolved_type.get(e, None)
if rt is not None:
return rt if isinstance(rt, Instance) else None
Expand All @@ -5082,15 +5087,27 @@ def fast_container_type(
# fallback to slow path
self.resolved_type[e] = NoneType()
return None
values.append(self.accept(item))
vt = join.join_type_list(values)
if not allow_fast_container_literal(vt):

typ = self.accept(item)
if typ not in values:
values.append(typ)

vt = self._first_or_join_fast_item(values)
if vt is None:
self.resolved_type[e] = NoneType()
return None
ct = self.chk.named_generic_type(container_fullname, [vt])
self.resolved_type[e] = ct
return ct

def _first_or_join_fast_item(self, items: list[Type]) -> Type | None:
if len(items) == 1 and not self.chk.current_node_deferred:
return items[0]
typ = join.join_type_list(items)
if not allow_fast_container_literal(typ):
return None
return typ

def check_lst_expr(self, e: ListExpr | SetExpr | TupleExpr, fullname: str, tag: str) -> Type:
# fast path
t = self.fast_container_type(e, fullname)
Expand Down Expand Up @@ -5277,13 +5294,23 @@ def fast_dict_type(self, e: DictExpr) -> Type | None:
self.resolved_type[e] = NoneType()
return None
else:
keys.append(self.accept(key))
values.append(self.accept(value))
kt = join.join_type_list(keys)
vt = join.join_type_list(values)
if not (allow_fast_container_literal(kt) and allow_fast_container_literal(vt)):
key_t = self.accept(key)
if key_t not in keys:
keys.append(key_t)
value_t = self.accept(value)
if value_t not in values:
values.append(value_t)

kt = self._first_or_join_fast_item(keys)
if kt is None:
self.resolved_type[e] = NoneType()
return None

vt = self._first_or_join_fast_item(values)
if vt is None:
self.resolved_type[e] = NoneType()
return None

if stargs and (stargs[0] != kt or stargs[1] != vt):
self.resolved_type[e] = NoneType()
return None
Expand Down
10 changes: 5 additions & 5 deletions test-data/unit/check-generics.test
Original file line number Diff line number Diff line change
Expand Up @@ -2929,8 +2929,8 @@ def mix(fs: List[Callable[[S], T]]) -> Callable[[S], List[T]]:
def id(__x: U) -> U:
...
fs = [id, id, id]
reveal_type(mix(fs)) # N: Revealed type is "def [S] (S`7) -> builtins.list[S`7]"
reveal_type(mix([id, id, id])) # N: Revealed type is "def [S] (S`9) -> builtins.list[S`9]"
reveal_type(mix(fs)) # N: Revealed type is "def [S] (S`2) -> builtins.list[S`2]"
reveal_type(mix([id, id, id])) # N: Revealed type is "def [S] (S`4) -> builtins.list[S`4]"
[builtins fixtures/list.pyi]

[case testInferenceAgainstGenericCurry]
Expand Down Expand Up @@ -3118,11 +3118,11 @@ def dec4_bound(f: Callable[[I], List[T]]) -> Callable[[I], T]:
reveal_type(dec1(lambda x: x)) # N: Revealed type is "def [T] (T`3) -> builtins.list[T`3]"
reveal_type(dec2(lambda x: x)) # N: Revealed type is "def [S] (S`5) -> builtins.list[S`5]"
reveal_type(dec3(lambda x: x[0])) # N: Revealed type is "def [S] (S`8) -> S`8"
reveal_type(dec4(lambda x: [x])) # N: Revealed type is "def [S] (S`12) -> S`12"
reveal_type(dec4(lambda x: [x])) # N: Revealed type is "def [S] (S`11) -> S`11"
reveal_type(dec1(lambda x: 1)) # N: Revealed type is "def (builtins.int) -> builtins.list[builtins.int]"
reveal_type(dec5(lambda x: x)) # N: Revealed type is "def (builtins.int) -> builtins.list[builtins.int]"
reveal_type(dec3(lambda x: x)) # N: Revealed type is "def [S] (S`20) -> builtins.list[S`20]"
reveal_type(dec4(lambda x: x)) # N: Revealed type is "def [T] (builtins.list[T`24]) -> T`24"
reveal_type(dec3(lambda x: x)) # N: Revealed type is "def [S] (S`19) -> builtins.list[S`19]"
reveal_type(dec4(lambda x: x)) # N: Revealed type is "def [T] (builtins.list[T`23]) -> T`23"
dec4_bound(lambda x: x) # E: Value of type variable "I" of "dec4_bound" cannot be "list[T]"
[builtins fixtures/list.pyi]

Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/check-redefine2.test
Original file line number Diff line number Diff line change
Expand Up @@ -1073,7 +1073,7 @@ def f() -> None:
while int():
x = [x]

reveal_type(x) # N: Revealed type is "Union[Any, builtins.list[Any], builtins.list[Union[Any, builtins.list[Any]]], builtins.list[Union[Any, builtins.list[Any], builtins.list[Union[Any, builtins.list[Any]]]]], builtins.list[Union[Any, builtins.list[Any], builtins.list[Union[Any, builtins.list[Any]]], builtins.list[Union[Any, builtins.list[Any], builtins.list[Union[Any, builtins.list[Any]]]]]]]]"
reveal_type(x) # N: Revealed type is "Union[Any, builtins.list[Any]]"

[case testNewRedefinePartialNoneEmptyList]
# flags: --allow-redefinition-new --local-partial-types
Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/check-selftype.test
Original file line number Diff line number Diff line change
Expand Up @@ -2018,7 +2018,7 @@ class Ben(Object):
}
@classmethod
def doit(cls) -> Foo:
reveal_type(cls.MY_MAP) # N: Revealed type is "builtins.dict[builtins.str, def [Self <: __main__.Foo] (self: Self`4) -> Self`4]"
reveal_type(cls.MY_MAP) # N: Revealed type is "builtins.dict[builtins.str, def [Self <: __main__.Foo] (self: Self`1) -> Self`1]"
foo_method = cls.MY_MAP["foo"]
return foo_method(Foo())
[builtins fixtures/isinstancelist.pyi]
Expand Down