Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit dc03478

Browse files
authored
[mypyc] Enable native integers outside tests (#14606)
I think that native integers work well enough to enable them outside tests. Require a more recent `mypy_extensions` that includes native int types, including `i64` and `i32`. Add definitions of `i64` and `i32` to the bundled stubs for `mypy_extensions`. Fork the stubs, since the definitions only make sense for mypy/mypyc. They require custom type checking logic. Other tools can treat these as aliases to `int`, which was implemented here: python/typeshed#9675 Also fix serialization of native int TypeInfos. Since we patch `builtins.int` when we process `mypy_extensions`, the patched information may not be serialized. We'll also need to perform similar patching during deserialization. Here is the performance impact to some benchmarks when using `i64` instead of `int` types (these assume some additional tweaks that should be ready soon): * richards: 33% faster * hexiom: 18% faster * deltablue: 2.6% faster Perhaps more importantly, native integers help with upcoming low-level features, such as packed arrays. Closes mypyc/mypyc#837. Remaining work can be tracked in separate issues.
1 parent 6787e51 commit dc03478

16 files changed

Lines changed: 154 additions & 43 deletions

File tree

misc/sync-typeshed.py

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,13 @@ def check_state() -> None:
3535
def update_typeshed(typeshed_dir: str, commit: str | None) -> str:
3636
"""Update contents of local typeshed copy.
3737
38+
We maintain our own separate mypy_extensions stubs, since it's
39+
treated specially by mypy and we make assumptions about what's there.
40+
We don't sync mypy_extensions stubs here -- this is done manually.
41+
3842
Return the normalized typeshed commit hash.
3943
"""
4044
assert os.path.isdir(os.path.join(typeshed_dir, "stdlib"))
41-
assert os.path.isdir(os.path.join(typeshed_dir, "stubs"))
4245
if commit:
4346
subprocess.run(["git", "checkout", commit], check=True, cwd=typeshed_dir)
4447
commit = git_head_commit(typeshed_dir)
@@ -48,15 +51,6 @@ def update_typeshed(typeshed_dir: str, commit: str | None) -> str:
4851
shutil.rmtree(stdlib_dir)
4952
# Copy new stdlib stubs.
5053
shutil.copytree(os.path.join(typeshed_dir, "stdlib"), stdlib_dir)
51-
# Copy mypy_extensions stubs. We don't want to use a stub package, since it's
52-
# treated specially by mypy and we make assumptions about what's there.
53-
stubs_dir = os.path.join("mypy", "typeshed", "stubs")
54-
shutil.rmtree(stubs_dir)
55-
os.makedirs(stubs_dir)
56-
shutil.copytree(
57-
os.path.join(typeshed_dir, "stubs", "mypy-extensions"),
58-
os.path.join(stubs_dir, "mypy-extensions"),
59-
)
6054
shutil.copy(os.path.join(typeshed_dir, "LICENSE"), os.path.join("mypy", "typeshed"))
6155
return commit
6256

mypy-requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# NOTE: this needs to be kept in sync with the "requires" list in pyproject.toml
22
typing_extensions>=3.10
3-
mypy_extensions>=0.4.3
3+
mypy_extensions>=1.0.0
44
typed_ast>=1.4.0,<2; python_version<'3.8'
55
tomli>=1.1.0; python_version<'3.11'

mypy/checkexpr.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3354,7 +3354,10 @@ def lookup_definer(typ: Instance, attr_name: str) -> str | None:
33543354
is_subtype(right_type, left_type)
33553355
and isinstance(left_type, Instance)
33563356
and isinstance(right_type, Instance)
3357-
and left_type.type.alt_promote is not right_type.type
3357+
and not (
3358+
left_type.type.alt_promote is not None
3359+
and left_type.type.alt_promote.type is right_type.type
3360+
)
33583361
and lookup_definer(left_type, op_name) != lookup_definer(right_type, rev_op_name)
33593362
):
33603363
# When we do "A() + B()" where B is a subclass of A, we'll actually try calling

mypy/fixup.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@ def visit_type_info(self, info: TypeInfo) -> None:
8787
info.declared_metaclass.accept(self.type_fixer)
8888
if info.metaclass_type:
8989
info.metaclass_type.accept(self.type_fixer)
90+
if info.alt_promote:
91+
info.alt_promote.accept(self.type_fixer)
92+
instance = Instance(info, [])
93+
# Hack: We may also need to add a backwards promotion (from int to native int),
94+
# since it might not be serialized.
95+
if instance not in info.alt_promote.type._promote:
96+
info.alt_promote.type._promote.append(instance)
9097
if info._mro_refs:
9198
info.mro = [
9299
lookup_fully_qualified_typeinfo(

mypy/meet.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ def narrow_declared_type(declared: Type, narrowed: Type) -> Type:
167167
if (
168168
isinstance(narrowed, Instance)
169169
and narrowed.type.alt_promote
170-
and narrowed.type.alt_promote is declared.type
170+
and narrowed.type.alt_promote.type is declared.type
171171
):
172172
# Special case: 'int' can't be narrowed down to a native int type such as
173173
# i64, since they have different runtime representations.
@@ -715,10 +715,10 @@ def visit_instance(self, t: Instance) -> ProperType:
715715
return NoneType()
716716
else:
717717
alt_promote = t.type.alt_promote
718-
if alt_promote and alt_promote is self.s.type:
718+
if alt_promote and alt_promote.type is self.s.type:
719719
return t
720720
alt_promote = self.s.type.alt_promote
721-
if alt_promote and alt_promote is t.type:
721+
if alt_promote and alt_promote.type is t.type:
722722
return self.s
723723
if is_subtype(t, self.s):
724724
return t

mypy/nodes.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2932,7 +2932,7 @@ class is generic then it will be a type constructor of higher kind.
29322932
# This results in some unintuitive results, such as that even
29332933
# though i64 is compatible with int and int is compatible with
29342934
# float, i64 is *not* compatible with float.
2935-
alt_promote: TypeInfo | None
2935+
alt_promote: mypy.types.Instance | None
29362936

29372937
# Representation of a Tuple[...] base class, if the class has any
29382938
# (e.g., for named tuples). If this is not None, the actual Type
@@ -3230,6 +3230,7 @@ def serialize(self) -> JsonDict:
32303230
"bases": [b.serialize() for b in self.bases],
32313231
"mro": [c.fullname for c in self.mro],
32323232
"_promote": [p.serialize() for p in self._promote],
3233+
"alt_promote": None if self.alt_promote is None else self.alt_promote.serialize(),
32333234
"declared_metaclass": (
32343235
None if self.declared_metaclass is None else self.declared_metaclass.serialize()
32353236
),
@@ -3266,6 +3267,11 @@ def deserialize(cls, data: JsonDict) -> TypeInfo:
32663267
assert isinstance(t, mypy.types.ProperType)
32673268
_promote.append(t)
32683269
ti._promote = _promote
3270+
ti.alt_promote = (
3271+
None
3272+
if data["alt_promote"] is None
3273+
else mypy.types.Instance.deserialize(data["alt_promote"])
3274+
)
32693275
ti.declared_metaclass = (
32703276
None
32713277
if data["declared_metaclass"] is None

mypy/semanal_classprop.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,6 @@ def add_type_promotion(
181181
int_sym = builtin_names["int"]
182182
assert isinstance(int_sym.node, TypeInfo)
183183
int_sym.node._promote.append(Instance(defn.info, []))
184-
defn.info.alt_promote = int_sym.node
184+
defn.info.alt_promote = Instance(int_sym.node, [])
185185
if promote_targets:
186186
defn.info._promote.extend(promote_targets)

mypy/subtypes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ def visit_instance(self, left: Instance) -> bool:
455455
# Special case: Low-level integer types are compatible with 'int'. We can't
456456
# use promotions, since 'int' is already promoted to low-level integer types,
457457
# and we can't have circular promotions.
458-
if left.type.alt_promote is right.type:
458+
if left.type.alt_promote and left.type.alt_promote.type is right.type:
459459
return True
460460
rname = right.type.fullname
461461
# Always try a nominal check if possible,
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
version = "0.4.*"
1+
version = "1.0.*"
22

33
[tool.stubtest]
44
ignore_missing_stub = false

mypy/typeshed/stubs/mypy-extensions/mypy_extensions.pyi

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
# These stubs are forked from typeshed, since we use some definitions that only make
2+
# sense in the context of mypy/mypyc (in particular, native int types such as i64).
3+
14
import abc
25
import sys
36
from _collections_abc import dict_items, dict_keys, dict_values
47
from _typeshed import IdentityFunction, Self
58
from collections.abc import Mapping
6-
from typing import Any, ClassVar, Generic, TypeVar, overload, type_check_only
7-
from typing_extensions import Never
9+
from typing import Any, ClassVar, Generic, SupportsInt, TypeVar, overload, type_check_only
10+
from typing_extensions import Never, SupportsIndex
11+
from _typeshed import ReadableBuffer, SupportsTrunc
812

913
_T = TypeVar("_T")
1014
_U = TypeVar("_U")
@@ -68,3 +72,77 @@ def trait(cls: _T) -> _T: ...
6872
def mypyc_attr(*attrs: str, **kwattrs: object) -> IdentityFunction: ...
6973

7074
class FlexibleAlias(Generic[_T, _U]): ...
75+
76+
# Native int types such as i64 are magical and support implicit
77+
# coercions to/from int using special logic in mypy. We generally only
78+
# include operations here for which we have specialized primitives.
79+
80+
class i64:
81+
@overload
82+
def __new__(cls, __x: str | ReadableBuffer | SupportsInt | SupportsIndex | SupportsTrunc = ...) -> i64: ...
83+
@overload
84+
def __new__(cls, __x: str | bytes | bytearray, base: SupportsIndex) -> i64: ...
85+
86+
def __add__(self, x: i64) -> i64: ...
87+
def __radd__(self, x: i64) -> i64: ...
88+
def __sub__(self, x: i64) -> i64: ...
89+
def __rsub__(self, x: i64) -> i64: ...
90+
def __mul__(self, x: i64) -> i64: ...
91+
def __rmul__(self, x: i64) -> i64: ...
92+
def __floordiv__(self, x: i64) -> i64: ...
93+
def __rfloordiv__(self, x: i64) -> i64: ...
94+
def __mod__(self, x: i64) -> i64: ...
95+
def __rmod__(self, x: i64) -> i64: ...
96+
def __and__(self, x: i64) -> i64: ...
97+
def __rand__(self, x: i64) -> i64: ...
98+
def __or__(self, x: i64) -> i64: ...
99+
def __ror__(self, x: i64) -> i64: ...
100+
def __xor__(self, x: i64) -> i64: ...
101+
def __rxor__(self, x: i64) -> i64: ...
102+
def __lshift__(self, x: i64) -> i64: ...
103+
def __rlshift__(self, x: i64) -> i64: ...
104+
def __rshift__(self, x: i64) -> i64: ...
105+
def __rrshift__(self, x: i64) -> i64: ...
106+
def __neg__(self) -> i64: ...
107+
def __invert__(self) -> i64: ...
108+
def __pos__(self) -> i64: ...
109+
def __lt__(self, x: i64) -> bool: ...
110+
def __le__(self, x: i64) -> bool: ...
111+
def __ge__(self, x: i64) -> bool: ...
112+
def __gt__(self, x: i64) -> bool: ...
113+
def __index__(self) -> int: ...
114+
115+
class i32:
116+
@overload
117+
def __new__(cls, __x: str | ReadableBuffer | SupportsInt | SupportsIndex | SupportsTrunc = ...) -> i32: ...
118+
@overload
119+
def __new__(cls, __x: str | bytes | bytearray, base: SupportsIndex) -> i32: ...
120+
121+
def __add__(self, x: i32) -> i32: ...
122+
def __radd__(self, x: i32) -> i32: ...
123+
def __sub__(self, x: i32) -> i32: ...
124+
def __rsub__(self, x: i32) -> i32: ...
125+
def __mul__(self, x: i32) -> i32: ...
126+
def __rmul__(self, x: i32) -> i32: ...
127+
def __floordiv__(self, x: i32) -> i32: ...
128+
def __rfloordiv__(self, x: i32) -> i32: ...
129+
def __mod__(self, x: i32) -> i32: ...
130+
def __rmod__(self, x: i32) -> i32: ...
131+
def __and__(self, x: i32) -> i32: ...
132+
def __rand__(self, x: i32) -> i32: ...
133+
def __or__(self, x: i32) -> i32: ...
134+
def __ror__(self, x: i32) -> i32: ...
135+
def __xor__(self, x: i32) -> i32: ...
136+
def __rxor__(self, x: i32) -> i32: ...
137+
def __lshift__(self, x: i32) -> i32: ...
138+
def __rlshift__(self, x: i32) -> i32: ...
139+
def __rshift__(self, x: i32) -> i32: ...
140+
def __rrshift__(self, x: i32) -> i32: ...
141+
def __neg__(self) -> i32: ...
142+
def __invert__(self) -> i32: ...
143+
def __pos__(self) -> i32: ...
144+
def __lt__(self, x: i32) -> bool: ...
145+
def __le__(self, x: i32) -> bool: ...
146+
def __ge__(self, x: i32) -> bool: ...
147+
def __gt__(self, x: i32) -> bool: ...
148+
def __index__(self) -> int: ...

0 commit comments

Comments
 (0)