From 6e1aea6c7609a40fcf7f273bc9382b29fcfa6396 Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Wed, 27 May 2020 11:17:29 -0700 Subject: [PATCH 001/138] [mypyc] Add a primitive for formatting an int as a str (#8870) This is partially just shameless cheesing on an example kmod used in a blog post (http://blog.kevmod.com/2020/05/python-performance-its-not-just-the-interpreter/comment-page-1/#comment-55896), but it is generally useful. Probably it will be more useful as part of more general string formatting optimizations, but it is a start. This gives a 3x speedup on the example in the blog post. I had been hoping to just allocate a unicode string and call sprintf into it but that turned out to be quite a bit slower than boxing the int and calling PyObject_Str on it! So I just implemented it. --- mypyc/lib-rt/CPy.h | 56 ++++++++++++++++++++++++++++ mypyc/primitives/int_ops.py | 16 ++++++++ mypyc/test-data/irbuild-classes.test | 8 ++-- mypyc/test-data/run.test | 9 ++++- 4 files changed, 83 insertions(+), 6 deletions(-) diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index f534e8386cbc..e01b234ea77b 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -634,6 +634,62 @@ static CPyTagged CPyTagged_Id(PyObject *o) { return CPyTagged_FromSsize_t((Py_ssize_t)o); } + +#define MAX_INT_CHARS 22 +#define _PyUnicode_LENGTH(op) \ + (((PyASCIIObject *)(op))->length) + +// using snprintf or PyUnicode_FromFormat was way slower than +// boxing the int and calling PyObject_Str on it, so we implement our own +static int fmt_ssize_t(char *out, Py_ssize_t n) { + bool neg = n < 0; + if (neg) n = -n; + + // buf gets filled backward and then we copy it forward + char buf[MAX_INT_CHARS]; + int i = 0; + do { + buf[i] = (n % 10) + '0'; + n /= 10; + i++; + } while (n); + + + int len = i; + int j = 0; + if (neg) { + out[j++] = '-'; + len++; + } + + for (; j < len; j++, i--) { + out[j] = buf[i-1]; + } + out[j] = '\0'; + + return len; +} + +static PyObject *CPyTagged_ShortToStr(Py_ssize_t n) { + PyObject *obj = PyUnicode_New(MAX_INT_CHARS, 127); + if (!obj) return NULL; + int len = fmt_ssize_t((char *)PyUnicode_1BYTE_DATA(obj), n); + _PyUnicode_LENGTH(obj) = len; + return obj; +} + +static PyObject *CPyTagged_Str(CPyTagged n) { + if (CPyTagged_CheckShort(n)) { + return CPyTagged_ShortToStr(CPyTagged_ShortAsSsize_t(n)); + } else { + return PyObject_Str(CPyTagged_AsObject(n)); + } +} + +static PyObject *CPyBool_Str(bool b) { + return PyObject_Str(b ? Py_True : Py_False); +} + static PyObject *CPyList_GetItemUnsafe(PyObject *list, CPyTagged index) { Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); PyObject *result = PyList_GET_ITEM(list, n); diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index bd4fbc7b7957..6d162e937f76 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -54,6 +54,22 @@ emit=call_emit('CPyLong_FromStrWithBase'), priority=1) +# str(n) on ints +func_op(name='builtins.str', + arg_types=[int_rprimitive], + result_type=str_rprimitive, + error_kind=ERR_MAGIC, + emit=call_emit('CPyTagged_Str'), + priority=2) + +# We need a specialization for str on bools also since the int one is wrong... +func_op(name='builtins.str', + arg_types=[bool_rprimitive], + result_type=str_rprimitive, + error_kind=ERR_MAGIC, + emit=call_emit('CPyBool_Str'), + priority=3) + def int_binary_op(op: str, c_func_name: str, result_type: RType = int_rprimitive, diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index 2097f114c6a5..5a42307e7ba0 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -241,12 +241,10 @@ def use_c(x: C, y: object) -> int: def A.foo(self, x): self :: __main__.A x :: int - r0 :: object - r1 :: str + r0 :: str L0: - r0 = box(int, x) - r1 = str r0 :: object - return r1 + r0 = str x :: int + return r0 def B.foo(self, x): self :: __main__.B x :: object diff --git a/mypyc/test-data/run.test b/mypyc/test-data/run.test index a25d13f4141e..c2448caa9e6e 100644 --- a/mypyc/test-data/run.test +++ b/mypyc/test-data/run.test @@ -825,6 +825,8 @@ def g() -> str: return 'some\a \v \t \x7f " \n \0string 🐍' def tostr(x: int) -> str: return str(x) +def booltostr(x: bool) -> str: + return str(x) def concat(x: str, y: str) -> str: return x + y def eq(x: str) -> int: @@ -835,15 +837,20 @@ def eq(x: str) -> int: return 2 [file driver.py] -from native import f, g, tostr, concat, eq +from native import f, g, tostr, booltostr, concat, eq assert f() == 'some string' assert g() == 'some\a \v \t \x7f " \n \0string 🐍' assert tostr(57) == '57' assert concat('foo', 'bar') == 'foobar' +assert booltostr(True) == 'True' +assert booltostr(False) == 'False' assert eq('foo') == 0 assert eq('zar') == 1 assert eq('bar') == 2 +assert int(tostr(0)) == 0 +assert int(tostr(20)) == 20 + [case testFstring] var = 'mypyc' From 43c3c5c987aa999f134d1ec3c9edeadb3cb95c6a Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 27 May 2020 21:19:48 +0100 Subject: [PATCH 002/138] [mypyc] Add "# type:ignore" to doc configuration (#8885) This is for convenience to avoid bogus mypy errors. Also test if disabling Appveyor worked. --- mypyc/doc/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/doc/conf.py b/mypyc/doc/conf.py index ec6f5542605c..1014f4682fb6 100644 --- a/mypyc/doc/conf.py +++ b/mypyc/doc/conf.py @@ -27,7 +27,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [ +extensions = [ # type: ignore ] # Add any paths that contain templates here, relative to this directory. From 4dce036e958dd9f982b8c02418f7fc174d3aa315 Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Wed, 27 May 2020 13:20:11 -0700 Subject: [PATCH 003/138] Don't use OrderedDict on Python 3.6 and greater (#8904) OrderedDict is written in Python and maintains its own ordering on top of the ordering the dict provides in recent Pythons. This ordering can differ if certain methods are called, but we don't need that. Just always use dict when it is ordered. --- mypy/checkexpr.py | 2 +- mypy/errors.py | 3 ++- mypy/join.py | 2 +- mypy/meet.py | 2 +- mypy/messages.py | 2 +- mypy/nodes.py | 3 ++- mypy/options.py | 2 +- mypy/ordered_dict.py | 9 +++++++++ mypy/plugins/attrs.py | 2 +- mypy/semanal_typeddict.py | 2 +- mypy/type_visitor.py | 2 +- mypy/typeanal.py | 2 +- mypy/types.py | 2 +- mypyc/codegen/emit.py | 2 +- mypyc/codegen/emitclass.py | 2 +- mypyc/codegen/emitmodule.py | 2 +- mypyc/ir/class_ir.py | 2 +- mypyc/ir/ops.py | 2 +- mypyc/irbuild/builder.py | 2 +- mypyc/irbuild/main.py | 2 +- mypyc/irbuild/mapper.py | 2 +- mypyc/test/test_emitfunc.py | 2 +- mypyc/test/test_serialization.py | 2 +- 23 files changed, 33 insertions(+), 22 deletions(-) create mode 100644 mypy/ordered_dict.py diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 14b14224fb4a..1f0b6f94c4f1 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -1,6 +1,6 @@ """Expression type checker. This file is conceptually part of TypeChecker.""" -from collections import OrderedDict +from mypy.ordered_dict import OrderedDict from contextlib import contextmanager import itertools from typing import ( diff --git a/mypy/errors.py b/mypy/errors.py index 06651b764d62..12557573a655 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -1,7 +1,8 @@ import os.path import sys import traceback -from collections import OrderedDict, defaultdict +from mypy.ordered_dict import OrderedDict +from collections import defaultdict from typing import Tuple, List, TypeVar, Set, Dict, Optional, TextIO, Callable from typing_extensions import Final diff --git a/mypy/join.py b/mypy/join.py index 222b882349c7..736e10fd20f2 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -1,6 +1,6 @@ """Calculation of the least upper bound types (joins).""" -from collections import OrderedDict +from mypy.ordered_dict import OrderedDict from typing import List, Optional from mypy.types import ( diff --git a/mypy/meet.py b/mypy/meet.py index 548278c154da..3446783c9f0a 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -1,4 +1,4 @@ -from collections import OrderedDict +from mypy.ordered_dict import OrderedDict from typing import List, Optional, Tuple, Callable from mypy.join import ( diff --git a/mypy/messages.py b/mypy/messages.py index 2dd95e878572..ecd61c4e0eda 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -9,7 +9,7 @@ checker but we are moving away from this convention. """ -from collections import OrderedDict +from mypy.ordered_dict import OrderedDict import re import difflib from textwrap import dedent diff --git a/mypy/nodes.py b/mypy/nodes.py index dd3d0f390340..8ccb522323ba 100644 --- a/mypy/nodes.py +++ b/mypy/nodes.py @@ -2,7 +2,8 @@ import os from abc import abstractmethod -from collections import OrderedDict, defaultdict +from mypy.ordered_dict import OrderedDict +from collections import defaultdict from typing import ( Any, TypeVar, List, Tuple, cast, Set, Dict, Union, Optional, Callable, Sequence, Iterator ) diff --git a/mypy/options.py b/mypy/options.py index f3b095d9449c..54e106d2efe7 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -1,4 +1,4 @@ -from collections import OrderedDict +from mypy.ordered_dict import OrderedDict import re import pprint import sys diff --git a/mypy/ordered_dict.py b/mypy/ordered_dict.py new file mode 100644 index 000000000000..f1e78ac242f7 --- /dev/null +++ b/mypy/ordered_dict.py @@ -0,0 +1,9 @@ +# OrderedDict is kind of slow, so for most of our uses in Python 3.6 +# and later we'd rather just use dict + +import sys + +if sys.version_info < (3, 6): + from collections import OrderedDict as OrderedDict +else: + OrderedDict = dict diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 12675042aa57..bff78f5fa907 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -1,6 +1,6 @@ """Plugin for supporting the attrs library (http://www.attrs.org)""" -from collections import OrderedDict +from mypy.ordered_dict import OrderedDict from typing import Optional, Dict, List, cast, Tuple, Iterable from typing_extensions import Final diff --git a/mypy/semanal_typeddict.py b/mypy/semanal_typeddict.py index 6ff0b9bdc478..99a1e1395379 100644 --- a/mypy/semanal_typeddict.py +++ b/mypy/semanal_typeddict.py @@ -1,6 +1,6 @@ """Semantic analysis of TypedDict definitions.""" -from collections import OrderedDict +from mypy.ordered_dict import OrderedDict from typing import Optional, List, Set, Tuple from typing_extensions import Final diff --git a/mypy/type_visitor.py b/mypy/type_visitor.py index b993f0d8a151..905f46a92576 100644 --- a/mypy/type_visitor.py +++ b/mypy/type_visitor.py @@ -12,7 +12,7 @@ """ from abc import abstractmethod -from collections import OrderedDict +from mypy.ordered_dict import OrderedDict from typing import Generic, TypeVar, cast, Any, List, Callable, Iterable, Optional, Set from mypy_extensions import trait diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 183a9a792c91..5fcbaa0a2a94 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -3,7 +3,7 @@ import itertools from itertools import chain from contextlib import contextmanager -from collections import OrderedDict +from mypy.ordered_dict import OrderedDict from typing import Callable, List, Optional, Set, Tuple, Iterator, TypeVar, Iterable from typing_extensions import Final diff --git a/mypy/types.py b/mypy/types.py index c214f82c6776..3500f30e49c5 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -3,7 +3,7 @@ import copy import sys from abc import abstractmethod -from collections import OrderedDict +from mypy.ordered_dict import OrderedDict from typing import ( Any, TypeVar, Dict, List, Tuple, cast, Set, Optional, Union, Iterable, NamedTuple, diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index a0c19690e27a..037182674a11 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -1,6 +1,6 @@ """Utilities for emitting C code.""" -from collections import OrderedDict +from mypy.ordered_dict import OrderedDict from typing import List, Set, Dict, Optional, Callable, Union from mypyc.common import ( diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py index a702312fb6ea..7ec749341aff 100644 --- a/mypyc/codegen/emitclass.py +++ b/mypyc/codegen/emitclass.py @@ -2,7 +2,7 @@ from typing import Optional, List, Tuple, Dict, Callable, Mapping, Set -from collections import OrderedDict +from mypy.ordered_dict import OrderedDict from mypyc.common import PREFIX, NATIVE_PREFIX, REG_PREFIX from mypyc.codegen.emit import Emitter, HeaderDeclaration diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py index 347c285afbc9..79d2e3f9605b 100644 --- a/mypyc/codegen/emitmodule.py +++ b/mypyc/codegen/emitmodule.py @@ -5,7 +5,7 @@ import os import json -from collections import OrderedDict +from mypy.ordered_dict import OrderedDict from typing import List, Tuple, Dict, Iterable, Set, TypeVar, Optional from mypy.nodes import MypyFile diff --git a/mypyc/ir/class_ir.py b/mypyc/ir/class_ir.py index 4858c1ce18e6..aeb0f8410c56 100644 --- a/mypyc/ir/class_ir.py +++ b/mypyc/ir/class_ir.py @@ -1,7 +1,7 @@ """Intermediate representation of classes.""" from typing import List, Optional, Set, Tuple, Dict, NamedTuple -from collections import OrderedDict +from mypy.ordered_dict import OrderedDict from mypyc.common import JsonDict from mypyc.ir.ops import Value, DeserMaps diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 6f1db29480eb..6e3c1b0875dc 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -15,7 +15,7 @@ List, Sequence, Dict, Generic, TypeVar, Optional, Any, NamedTuple, Tuple, Callable, Union, Iterable, Set ) -from collections import OrderedDict +from mypy.ordered_dict import OrderedDict from typing_extensions import Final, Type, TYPE_CHECKING from mypy_extensions import trait diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 71bc272eccd0..be634be14150 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -13,7 +13,7 @@ from typing import Callable, Dict, List, Tuple, Optional, Union, Sequence, Set, Any from typing_extensions import overload -from collections import OrderedDict +from mypy.ordered_dict import OrderedDict from mypy.build import Graph from mypy.nodes import ( diff --git a/mypyc/irbuild/main.py b/mypyc/irbuild/main.py index 30e800c651f8..2fd8ea99d102 100644 --- a/mypyc/irbuild/main.py +++ b/mypyc/irbuild/main.py @@ -20,7 +20,7 @@ def f(x: int) -> int: below, mypyc.irbuild.builder, and mypyc.irbuild.visitor. """ -from collections import OrderedDict +from mypy.ordered_dict import OrderedDict from typing import List, Dict, Callable, Any, TypeVar, cast from mypy.nodes import MypyFile, Expression, ClassDef diff --git a/mypyc/irbuild/mapper.py b/mypyc/irbuild/mapper.py index dfa7a99753fb..c47e27bbf7b7 100644 --- a/mypyc/irbuild/mapper.py +++ b/mypyc/irbuild/mapper.py @@ -1,7 +1,7 @@ """Maintain a mapping from mypy concepts to IR/compiled concepts.""" from typing import Dict, Optional, Union -from collections import OrderedDict +from mypy.ordered_dict import OrderedDict from mypy.nodes import FuncDef, TypeInfo, SymbolNode, ARG_STAR, ARG_STAR2 from mypy.types import ( diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index 59dc5876b6d5..5bad7bc2a93b 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -1,6 +1,6 @@ import unittest -from collections import OrderedDict +from mypy.ordered_dict import OrderedDict from mypy.nodes import Var from mypy.test.helpers import assert_string_arrays_equal diff --git a/mypyc/test/test_serialization.py b/mypyc/test/test_serialization.py index ef0f7f3a4e3f..338be1aedb85 100644 --- a/mypyc/test/test_serialization.py +++ b/mypyc/test/test_serialization.py @@ -4,7 +4,7 @@ # contain its own tests so that pytest will rewrite the asserts... from typing import Any, Dict, Tuple -from collections import OrderedDict +from mypy.ordered_dict import OrderedDict from collections.abc import Iterable from mypyc.ir.ops import DeserMaps From 07c9f6fac1091ea566ad51284eea3739cdbd13ba Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Wed, 27 May 2020 17:10:52 -0700 Subject: [PATCH 004/138] [mypyc] Optimize two-argument super() for the simple case (#8903) --- mypyc/irbuild/expression.py | 22 +++++++++++++++++++++- mypyc/test-data/run-classes.test | 14 ++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 624219d6b62c..caa979713cf3 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -241,8 +241,28 @@ def translate_method_call(builder: IRBuilder, expr: CallExpr, callee: MemberExpr def translate_super_method_call(builder: IRBuilder, expr: CallExpr, callee: SuperExpr) -> Value: - if callee.info is None or callee.call.args: + if callee.info is None or (len(callee.call.args) != 0 and len(callee.call.args) != 2): return translate_call(builder, expr, callee) + + # We support two-argument super but only when it is super(CurrentClass, self) + # TODO: We could support it when it is a parent class in many cases? + if len(callee.call.args) == 2: + self_arg = callee.call.args[1] + if ( + not isinstance(self_arg, NameExpr) + or not isinstance(self_arg.node, Var) + or not self_arg.node.is_self + ): + return translate_call(builder, expr, callee) + + typ_arg = callee.call.args[0] + if ( + not isinstance(typ_arg, NameExpr) + or not isinstance(typ_arg.node, TypeInfo) + or callee.info is not typ_arg.node + ): + return translate_call(builder, expr, callee) + ir = builder.mapper.type_to_ir[callee.info] # Search for the method in the mro, skipping ourselves. for base in ir.mro[1:]: diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 6b67cb08ec7d..64680b03308e 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -709,15 +709,27 @@ from typing import List class A: def __init__(self, x: int) -> None: self.x = x + + def foo(self, x: int) -> int: + return x + class B(A): def __init__(self, x: int, y: int) -> None: super().__init__(x) self.y = y + + def foo(self, x: int) -> int: + return super().foo(x+1) + class C(B): def __init__(self, x: int, y: int) -> None: init = super(C, self).__init__ init(x, y+1) + def foo(self, x: int) -> int: + # should go to A, not B + return super(B, self).foo(x+1) + class X: def __init__(self, x: int) -> None: self.x = x @@ -753,6 +765,8 @@ assert c.x == 10 and c.y == 21 z = Z(10, 20) assert z.x == 10 and z.y == 20 +assert c.foo(10) == 11 + PrintList().v_list([1,2,3]) [out] yo! From e80585a56fa7a8211c7d7524511d6d5c79097361 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Thu, 28 May 2020 04:01:59 -0700 Subject: [PATCH 005/138] remove open() pythoneval test (#8908) python/typeshed#3371 causes this to produce a different error message. open() with no arguments doesn't seem like an important use case, so testing for it explicitly in pythoneval isn't all that useful. --- test-data/unit/pythoneval.test | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 6a76b3941a5d..a37cf2959d02 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -287,17 +287,14 @@ _program.py:3: note: Revealed type is 'typing.BinaryIO' _program.py:5: note: Revealed type is 'typing.IO[Any]' [case testOpenReturnTypeInferenceSpecialCases] -reveal_type(open()) reveal_type(open(mode='rb', file='x')) reveal_type(open(file='x', mode='rb')) mode = 'rb' reveal_type(open(mode=mode, file='r')) [out] -_testOpenReturnTypeInferenceSpecialCases.py:1: error: Too few arguments for "open" -_testOpenReturnTypeInferenceSpecialCases.py:1: note: Revealed type is 'typing.TextIO' +_testOpenReturnTypeInferenceSpecialCases.py:1: note: Revealed type is 'typing.BinaryIO' _testOpenReturnTypeInferenceSpecialCases.py:2: note: Revealed type is 'typing.BinaryIO' -_testOpenReturnTypeInferenceSpecialCases.py:3: note: Revealed type is 'typing.BinaryIO' -_testOpenReturnTypeInferenceSpecialCases.py:5: note: Revealed type is 'typing.IO[Any]' +_testOpenReturnTypeInferenceSpecialCases.py:4: note: Revealed type is 'typing.IO[Any]' [case testPathOpenReturnTypeInference] from pathlib import Path From c48624a6db632295748b28c007be46aaddd8a6ce Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Thu, 28 May 2020 09:55:33 -0700 Subject: [PATCH 006/138] Debugging code to log PyObject_GetAttr and python method calls (#8907) --- mypyc/lib-rt/CPy.h | 29 +++++++++++++++++++++++++++++ mypyc/primitives/generic_ops.py | 4 ++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index e01b234ea77b..537b90f72906 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -1220,6 +1220,35 @@ static void CPy_TypeError(const char *expected, PyObject *value) { } } + +#ifdef MYPYC_LOG_GETATTR +static void CPy_LogGetAttr(const char *method, PyObject *obj, PyObject *attr) { + PyObject *module = PyImport_ImportModule("getattr_hook"); + if (module) { + PyObject *res = PyObject_CallMethod(module, method, "OO", obj, attr); + Py_XDECREF(res); + Py_DECREF(module); + } + PyErr_Clear(); +} +#else +#define CPy_LogGetAttr(method, obj, attr) (void)0 +#endif + +// Intercept a method call and log it. This needs to be a macro +// because there is no API that accepts va_args for making a +// call. Worse, it needs to use the comma operator to return the right +// value. +#define CPyObject_CallMethodObjArgs(obj, attr, ...) \ + (CPy_LogGetAttr("log_method", (obj), (attr)), \ + PyObject_CallMethodObjArgs((obj), (attr), __VA_ARGS__)) + +// This one is a macro for consistency with the above, I guess. +#define CPyObject_GetAttr(obj, attr) \ + (CPy_LogGetAttr("log", (obj), (attr)), \ + PyObject_GetAttr((obj), (attr))) + + // These functions are basically exactly PyCode_NewEmpty and // _PyTraceback_Add which are available in all the versions we support. // We're continuing to use them because we'll probably optimize them later. diff --git a/mypyc/primitives/generic_ops.py b/mypyc/primitives/generic_ops.py index 1de71db3ee4a..3acf99cd99de 100644 --- a/mypyc/primitives/generic_ops.py +++ b/mypyc/primitives/generic_ops.py @@ -158,7 +158,7 @@ arg_types=[object_rprimitive, object_rprimitive], result_type=object_rprimitive, error_kind=ERR_MAGIC, - emit=call_emit('PyObject_GetAttr') + emit=call_emit('CPyObject_GetAttr') ) # getattr(obj, attr, default) @@ -224,7 +224,7 @@ is_var_arg=True, error_kind=ERR_MAGIC, format_str='{dest} = py_method_call({comma_args})', - emit=simple_emit('{dest} = PyObject_CallMethodObjArgs({comma_args}, NULL);')) + emit=simple_emit('{dest} = CPyObject_CallMethodObjArgs({comma_args}, NULL);')) # len(obj) func_op(name='builtins.len', From dae2ae8d717ac1c5718bcd2405c87ab4541cb512 Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Thu, 28 May 2020 09:56:39 -0700 Subject: [PATCH 007/138] Add scripts to misc/ for computing and applying diffs of mypy caches (#8906) This can (with some infrastructure) allow for much faster distribution of cache artifacts. --- misc/apply-cache-diff.py | 60 ++++++++++++++++ misc/diff-cache.py | 147 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 misc/apply-cache-diff.py create mode 100644 misc/diff-cache.py diff --git a/misc/apply-cache-diff.py b/misc/apply-cache-diff.py new file mode 100644 index 000000000000..543ece9981ab --- /dev/null +++ b/misc/apply-cache-diff.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +"""Script for applying a cache diff. + +With some infrastructure, this can allow for distributing small cache diffs to users in +many cases instead of full cache artifacts. +""" + +import argparse +import json +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from mypy.metastore import MetadataStore, FilesystemMetadataStore, SqliteMetadataStore + + +def make_cache(input_dir: str, sqlite: bool) -> MetadataStore: + if sqlite: + return SqliteMetadataStore(input_dir) + else: + return FilesystemMetadataStore(input_dir) + + +def apply_diff(cache_dir: str, diff_file: str, sqlite: bool = False) -> None: + cache = make_cache(cache_dir, sqlite) + with open(diff_file, "r") as f: + diff = json.load(f) + + old_deps = json.loads(cache.read("@deps.meta.json")) + + for file, data in diff.items(): + if data is None: + cache.remove(file) + else: + cache.write(file, data) + if file.endswith('.meta.json') and "@deps" not in file: + meta = json.loads(data) + old_deps["snapshot"][meta["id"]] = meta["hash"] + + cache.write("@deps.meta.json", json.dumps(old_deps)) + + cache.commit() + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument('--sqlite', action='store_true', default=False, + help='Use a sqlite cache') + parser.add_argument('cache_dir', + help="Directory for the cache") + parser.add_argument('diff', + help="Cache diff file") + args = parser.parse_args() + + apply_diff(args.cache_dir, args.diff, args.sqlite) + + +if __name__ == '__main__': + main() diff --git a/misc/diff-cache.py b/misc/diff-cache.py new file mode 100644 index 000000000000..11811cc3ae55 --- /dev/null +++ b/misc/diff-cache.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python3 +"""Produce a diff between mypy caches. + +With some infrastructure, this can allow for distributing small cache diffs to users in +many cases instead of full cache artifacts. +""" + +import argparse +import json +import os +import sys + +from collections import defaultdict +from typing import Any, Dict, Optional, Set + +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from mypy.metastore import FilesystemMetadataStore, MetadataStore, SqliteMetadataStore + + +def make_cache(input_dir: str, sqlite: bool) -> MetadataStore: + if sqlite: + return SqliteMetadataStore(input_dir) + else: + return FilesystemMetadataStore(input_dir) + + +def merge_deps(all: Dict[str, Set[str]], new: Dict[str, Set[str]]) -> None: + for k, v in new.items(): + all.setdefault(k, set()).update(v) + + +def load(cache: MetadataStore, s: str) -> Any: + data = cache.read(s) + obj = json.loads(data) + if s.endswith(".meta.json"): + # For meta files, zero out the mtimes and sort the + # dependencies to avoid spurious conflicts + obj["mtime"] = 0 + obj["data_mtime"] = 0 + if "dependencies" in obj: + all_deps = obj["dependencies"] + obj["suppressed"] + num_deps = len(obj["dependencies"]) + thing = list(zip(all_deps, obj["dep_prios"], obj["dep_lines"])) + + def unzip(x: Any) -> Any: + return zip(*x) if x else ((), (), ()) + + obj["dependencies"], prios1, lines1 = unzip(sorted(thing[:num_deps])) + obj["suppressed"], prios2, lines2 = unzip(sorted(thing[num_deps:])) + obj["dep_prios"] = prios1 + prios2 + obj["dep_lines"] = lines1 + lines2 + if s.endswith(".deps.json"): + # For deps files, sort the deps to avoid spurious mismatches + for v in obj.values(): + v.sort() + return obj + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument( + "--verbose", action="store_true", default=False, help="Increase verbosity" + ) + parser.add_argument( + "--sqlite", action="store_true", default=False, help="Use a sqlite cache" + ) + parser.add_argument("input_dir1", help="Input directory for the cache") + parser.add_argument("input_dir2", help="Input directory for the cache") + parser.add_argument("output", help="Output file") + args = parser.parse_args() + + cache1 = make_cache(args.input_dir1, args.sqlite) + cache2 = make_cache(args.input_dir2, args.sqlite) + + type_misses: Dict[str, int] = defaultdict(int) + type_hits: Dict[str, int] = defaultdict(int) + + updates: Dict[str, Optional[str]] = {} + + deps1: Dict[str, Set[str]] = {} + deps2: Dict[str, Set[str]] = {} + + misses = hits = 0 + cache1_all = list(cache1.list_all()) + for s in cache1_all: + obj1 = load(cache1, s) + try: + obj2 = load(cache2, s) + except FileNotFoundError: + obj2 = None + + typ = s.split(".")[-2] + if obj1 != obj2: + misses += 1 + type_misses[typ] += 1 + + # Collect the dependencies instead of including them directly in the diff + # so we can produce a much smaller direct diff of them. + if ".deps." not in s: + if obj2 is not None: + updates[s] = json.dumps(obj2) + else: + updates[s] = None + elif obj2: + merge_deps(deps1, obj1) + merge_deps(deps2, obj2) + else: + hits += 1 + type_hits[typ] += 1 + + cache1_all_set = set(cache1_all) + for s in cache2.list_all(): + if s not in cache1_all_set: + updates[s] = cache2.read(s) + + # Compute what deps have been added and merge them all into the + # @root deps file. + new_deps = {k: deps1.get(k, set()) - deps2.get(k, set()) for k in deps2} + new_deps = {k: v for k, v in new_deps.items() if v} + try: + root_deps = load(cache1, "@root.deps.json") + except FileNotFoundError: + root_deps = {} + merge_deps(new_deps, root_deps) + + new_deps_json = {k: list(v) for k, v in new_deps.items() if v} + updates["@root.deps.json"] = json.dumps(new_deps_json) + + # Drop updates to deps.meta.json for size reasons. The diff + # applier will manually fix it up. + updates.pop("./@deps.meta.json", None) + updates.pop("@deps.meta.json", None) + + ### + + print("Generated incremental cache:", hits, "hits,", misses, "misses") + if args.verbose: + print("hits", type_hits) + print("misses", type_misses) + + with open(args.output, "w") as f: + json.dump(updates, f) + + +if __name__ == "__main__": + main() From 1d7a6ae9fa313a93293a48585dcfc4e9dc482467 Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Thu, 28 May 2020 12:10:20 -0700 Subject: [PATCH 008/138] Use variable name to determined NamedTuple class name (#6698) This lets us avoid inserting line numbers into the name when the variable name and argument to NamedTuple disagree. This is good, because the line numbers are ugly and when combined with bug #6548 causes problems when a namedtuple is reexported. --- mypy/semanal_namedtuple.py | 20 +++++++++++++------- test-data/unit/check-incremental.test | 2 +- test-data/unit/fine-grained.test | 23 +++++++++++++++++++++++ 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py index 6b02d5c50f0f..ce82cb84348b 100644 --- a/mypy/semanal_namedtuple.py +++ b/mypy/semanal_namedtuple.py @@ -179,14 +179,20 @@ def check_namedtuple(self, info = self.build_namedtuple_typeinfo(name, [], [], {}, node.line) self.store_namedtuple_info(info, name, call, is_typed) return True, info - name = cast(Union[StrExpr, BytesExpr, UnicodeExpr], call.args[0]).value - if name != var_name or is_func_scope: - # There are three special cases where need to give it a unique name derived + + # We use the variable name as the class name if it exists. If + # it doesn't, we use the name passed as an argument. We prefer + # the variable name because it should be unique inside a + # module, and so we don't need to disambiguate it with a line + # number. + if var_name: + name = var_name + else: + name = cast(Union[StrExpr, BytesExpr, UnicodeExpr], call.args[0]).value + + if var_name is None or is_func_scope: + # There are two special cases where need to give it a unique name derived # from the line number: - # * There is a name mismatch with l.h.s., therefore we need to disambiguate - # situations like: - # A = NamedTuple('Same', [('x', int)]) - # B = NamedTuple('Same', [('y', str)]) # * This is a base class expression, since it often matches the class name: # class NT(NamedTuple('NT', [...])): # ... diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index f08d710f55ea..da06cb8eb9c9 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -5057,7 +5057,7 @@ NT = NamedTuple('BadName', [('x', int)]) [builtins fixtures/tuple.pyi] [out] [out2] -tmp/a.py:3: note: Revealed type is 'Tuple[builtins.int, fallback=b.BadName@2]' +tmp/a.py:3: note: Revealed type is 'Tuple[builtins.int, fallback=b.NT]' [case testNewAnalyzerIncrementalBrokenNamedTupleNested] diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index e098bc760f37..5761f6cb337c 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -9622,3 +9622,26 @@ class C: [out] == main:5: error: Unsupported left operand type for + ("str") + +[case testReexportNamedTupleChange] +from m import M + +def f(x: M) -> None: ... + +f(M(0)) + +[file m.py] +from n import M + +[file n.py] +from typing import NamedTuple +M = NamedTuple('_N', [('x', int)]) + +[file n.py.2] +# change the line numbers +from typing import NamedTuple +M = NamedTuple('_N', [('x', int)]) + +[builtins fixtures/tuple.pyi] +[out] +== From 5bf0fc486f8da639558472270131557ef7f0619f Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Thu, 28 May 2020 13:56:53 -0700 Subject: [PATCH 009/138] [mypyc] Refactor of load_final_static, make it report NameError (#8915) Pull out handling of of the error checks into ll_builder, and report the right exception. This is groundwork for fixing some potential segfaults involving uninitialized classes. --- mypyc/ir/ops.py | 1 + mypyc/irbuild/builder.py | 18 +++++------------- mypyc/irbuild/ll_builder.py | 22 ++++++++++++++++++++-- mypyc/test-data/fixtures/ir.py | 2 ++ mypyc/test-data/irbuild-basic.test | 6 +++--- mypyc/test-data/run.test | 2 +- 6 files changed, 32 insertions(+), 19 deletions(-) diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 6e3c1b0875dc..a0f83f10cc21 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -1113,6 +1113,7 @@ class RaiseStandardError(RegisterOp): STOP_ITERATION = 'StopIteration' # type: Final UNBOUND_LOCAL_ERROR = 'UnboundLocalError' # type: Final RUNTIME_ERROR = 'RuntimeError' # type: Final + NAME_ERROR = 'NameError' # type: Final def __init__(self, class_name: str, value: Optional[Union[str, Value]], line: int) -> None: super().__init__(line) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index be634be14150..beed68ca635a 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -34,7 +34,7 @@ BasicBlock, AssignmentTarget, AssignmentTargetRegister, AssignmentTargetIndex, AssignmentTargetAttr, AssignmentTargetTuple, Environment, LoadInt, Value, Register, Op, Assign, Branch, Unreachable, TupleGet, GetAttr, SetAttr, LoadStatic, - InitStatic, PrimitiveOp, OpDescription, NAMESPACE_MODULE, RaiseStandardError + InitStatic, PrimitiveOp, OpDescription, NAMESPACE_MODULE, RaiseStandardError, ) from mypyc.ir.rtypes import ( RType, RTuple, RInstance, int_rprimitive, dict_rprimitive, @@ -315,20 +315,12 @@ def init_final_static(self, lvalue: Lvalue, rvalue_reg: Value, def load_final_static(self, fullname: str, typ: RType, line: int, error_name: Optional[str] = None) -> Value: - if error_name is None: - error_name = fullname - ok_block, error_block = BasicBlock(), BasicBlock() split_name = split_target(self.graph, fullname) assert split_name is not None - value = self.add(LoadStatic(typ, split_name[1], split_name[0], line=line)) - self.add(Branch(value, error_block, ok_block, Branch.IS_ERROR, rare=True)) - self.activate_block(error_block) - self.add(RaiseStandardError(RaiseStandardError.VALUE_ERROR, - 'value for final name "{}" was not set'.format(error_name), - line)) - self.add(Unreachable()) - self.activate_block(ok_block) - return value + module, name = split_name + return self.builder.load_static_checked( + typ, name, module, line=line, + error_msg='value for final name "{}" was not set'.format(error_name)) def load_final_literal_value(self, val: Union[int, str, bytes, float, bool], line: int) -> Value: diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 91a2cde1dc68..607bf4b264ef 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -19,8 +19,9 @@ from mypyc.ir.ops import ( BasicBlock, Environment, Op, LoadInt, Value, Register, Assign, Branch, Goto, Call, Box, Unbox, Cast, GetAttr, - LoadStatic, MethodCall, PrimitiveOp, OpDescription, RegisterOp, - NAMESPACE_TYPE, NAMESPACE_MODULE, LoadErrorValue, CallC + LoadStatic, MethodCall, PrimitiveOp, OpDescription, RegisterOp, CallC, + RaiseStandardError, Unreachable, LoadErrorValue, + NAMESPACE_TYPE, NAMESPACE_MODULE, NAMESPACE_STATIC, ) from mypyc.ir.rtypes import ( RType, RUnion, RInstance, optional_value_type, int_rprimitive, float_rprimitive, @@ -457,6 +458,23 @@ def load_static_unicode(self, value: str) -> Value: static_symbol = self.literal_static_name(value) return self.add(LoadStatic(str_rprimitive, static_symbol, ann=value)) + def load_static_checked(self, typ: RType, identifier: str, module_name: Optional[str] = None, + namespace: str = NAMESPACE_STATIC, + line: int = -1, + error_msg: Optional[str] = None) -> Value: + if error_msg is None: + error_msg = "name '{}' is not defined".format(identifier) + ok_block, error_block = BasicBlock(), BasicBlock() + value = self.add(LoadStatic(typ, identifier, module_name, namespace, line=line)) + self.add(Branch(value, error_block, ok_block, Branch.IS_ERROR, rare=True)) + self.activate_block(error_block) + self.add(RaiseStandardError(RaiseStandardError.NAME_ERROR, + error_msg, + line)) + self.add(Unreachable()) + self.activate_block(ok_block) + return value + def load_module(self, name: str) -> Value: return self.add(LoadStatic(object_rprimitive, name, namespace=NAMESPACE_MODULE)) diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index 9218734b0e76..7faca81dff40 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -183,6 +183,8 @@ class TypeError(Exception): pass class AttributeError(Exception): pass +class NameError(Exception): pass + class LookupError(Exception): pass class KeyError(LookupError): pass diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 6392539519e6..024b4e8dba3b 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -3247,7 +3247,7 @@ L0: r0 = __main__.x :: static if is_error(r0) goto L1 else goto L2 L1: - raise ValueError('value for final name "x" was not set') + raise NameError('value for final name "x" was not set') unreachable L2: r2 = 0 @@ -3271,7 +3271,7 @@ L0: r0 = __main__.x :: static if is_error(r0) goto L1 else goto L2 L1: - raise ValueError('value for final name "x" was not set') + raise NameError('value for final name "x" was not set') unreachable L2: r2 = r0[0] @@ -3294,7 +3294,7 @@ L0: r0 = __main__.x :: static if is_error(r0) goto L1 else goto L2 L1: - raise ValueError('value for final name "x" was not set') + raise NameError('value for final name "x" was not set') unreachable L2: r2 = 1 diff --git a/mypyc/test-data/run.test b/mypyc/test-data/run.test index c2448caa9e6e..8c03352d22d6 100644 --- a/mypyc/test-data/run.test +++ b/mypyc/test-data/run.test @@ -4451,7 +4451,7 @@ def f() -> int: from native import f try: print(f()) -except ValueError as e: +except NameError as e: print(e.args[0]) [out] value for final name "x" was not set From 5cfb0561e624b79365ac5333cbde7ff500249740 Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Thu, 28 May 2020 13:58:58 -0700 Subject: [PATCH 010/138] [mypyc] Basic support for protocols (#8914) The idea is to represent a protocol type P as Union[P, object], so that we'll make fast calls if a class explictly inherits from the protocol and fall back to generic ops otherwise. --- mypyc/irbuild/mapper.py | 9 ++++++- mypyc/irbuild/util.py | 2 +- mypyc/lib-rt/CPy.h | 6 ++++- mypyc/test-data/run-classes.test | 42 ++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/mypyc/irbuild/mapper.py b/mypyc/irbuild/mapper.py index c47e27bbf7b7..364e650aa5dc 100644 --- a/mypyc/irbuild/mapper.py +++ b/mypyc/irbuild/mapper.py @@ -67,7 +67,14 @@ def type_to_rtype(self, typ: Optional[Type]) -> RType: elif typ.type.fullname == 'builtins.tuple': return tuple_rprimitive # Varying-length tuple elif typ.type in self.type_to_ir: - return RInstance(self.type_to_ir[typ.type]) + inst = RInstance(self.type_to_ir[typ.type]) + # Treat protocols as Union[protocol, object], so that we can do fast + # method calls in the cases where the protocol is explicitly inherited from + # and fall back to generic operations when it isn't. + if typ.type.is_protocol: + return RUnion([inst, object_rprimitive]) + else: + return inst else: return object_rprimitive elif isinstance(typ, TupleType): diff --git a/mypyc/irbuild/util.py b/mypyc/irbuild/util.py index 18d8306c869e..cc98903d8e30 100644 --- a/mypyc/irbuild/util.py +++ b/mypyc/irbuild/util.py @@ -19,7 +19,7 @@ def is_trait_decorator(d: Expression) -> bool: def is_trait(cdef: ClassDef) -> bool: - return any(is_trait_decorator(d) for d in cdef.decorators) + return any(is_trait_decorator(d) for d in cdef.decorators) or cdef.info.is_protocol def is_dataclass_decorator(d: Expression) -> bool: diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index 537b90f72906..e07e9a964579 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -82,7 +82,11 @@ static bool _CPy_IsSafeMetaClass(PyTypeObject *metaclass) { bool matches = false; if (PyUnicode_CompareWithASCIIString(module, "typing") == 0 && (strcmp(metaclass->tp_name, "TypingMeta") == 0 - || strcmp(metaclass->tp_name, "GenericMeta") == 0)) { + || strcmp(metaclass->tp_name, "GenericMeta") == 0 + || strcmp(metaclass->tp_name, "_ProtocolMeta") == 0)) { + matches = true; + } else if (PyUnicode_CompareWithASCIIString(module, "typing_extensions") == 0 && + strcmp(metaclass->tp_name, "_ProtocolMeta") == 0) { matches = true; } else if (PyUnicode_CompareWithASCIIString(module, "abc") == 0 && strcmp(metaclass->tp_name, "ABCMeta") == 0) { diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 64680b03308e..db910bacf055 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -958,6 +958,48 @@ assert b.z is None # N.B: this doesn't match cpython assert not hasattr(b, 'bogus') +[case testProtocol] +from typing_extensions import Protocol + +class Proto(Protocol): + def foo(self, x: int) -> None: + pass + + def bar(self, x: int) -> None: + pass + +class A: + def foo(self, x: int) -> None: + print("A:", x) + + def bar(self, *args: int, **kwargs: int) -> None: + print("A:", args, kwargs) + +class B(A, Proto): + def foo(self, x: int) -> None: + print("B:", x) + + def bar(self, *args: int, **kwargs: int) -> None: + print("B:", args, kwargs) + +def f(x: Proto) -> None: + x.foo(20) + x.bar(x=20) + +[file driver.py] +from native import A, B, f + +f(A()) +f(B()) + +# ... this exploits a bug in glue methods to distinguish whether we +# are making a direct call or a pycall... +[out] +A: 20 +A: () {'x': 20} +B: 20 +B: (20,) {} + [case testMethodOverrideDefault1] class A: def foo(self, x: int) -> None: From 273a86557e6e76acd52e6588230aa8e4ac5de532 Mon Sep 17 00:00:00 2001 From: Rune Tynan Date: Thu, 28 May 2020 20:25:57 -0400 Subject: [PATCH 011/138] Change test to platform check for windows-specific type checking (#8916) --- test-data/stdlib-samples/3.2/test/support.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-data/stdlib-samples/3.2/test/support.py b/test-data/stdlib-samples/3.2/test/support.py index a36ba28f2341..88ce10cd74a9 100644 --- a/test-data/stdlib-samples/3.2/test/support.py +++ b/test-data/stdlib-samples/3.2/test/support.py @@ -473,7 +473,7 @@ def fcmp(x, y): # fuzzy comparison function # encoded by the filesystem encoding (in strict mode). It can be None if we # cannot generate such filename. TESTFN_UNENCODABLE = None # type: Any -if os.name in ('nt', 'ce'): +if sys.platform == "win32": # skip win32s (0) or Windows 9x/ME (1) if sys.getwindowsversion().platform >= 2: # Different kinds of characters from various languages to minimize the From 8457e50ddbde8dea0aa481cb410cac80fb657912 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Mon, 1 Jun 2020 19:10:42 +0800 Subject: [PATCH 012/138] [mypyc] Support top level function ops via CallC (#8902) Relates to mypyc/mypyc#709 This PR supports top-level function ops via recently added CallC IR. To demonstrate the idea, it transform to_list op from PrimitiveOp to CallC. It also refines CallC with arguments coercing and support of steals. --- mypyc/ir/ops.py | 19 ++++++++++++--- mypyc/irbuild/builder.py | 12 ++++++++-- mypyc/irbuild/ll_builder.py | 33 +++++++++++++++++++------- mypyc/primitives/list_ops.py | 10 ++++---- mypyc/primitives/registry.py | 31 ++++++++++++++++++++---- mypyc/primitives/str_ops.py | 2 +- mypyc/test-data/irbuild-basic.test | 38 +++++++++++++++++++++++++++++- 7 files changed, 119 insertions(+), 26 deletions(-) diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index a0f83f10cc21..619d13ca898a 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -1145,13 +1145,19 @@ class CallC(RegisterOp): A call to a C function """ - error_kind = ERR_MAGIC - - def __init__(self, function_name: str, args: List[Value], ret_type: RType, line: int) -> None: + def __init__(self, + function_name: str, + args: List[Value], + ret_type: RType, + steals: StealsDescription, + error_kind: int, + line: int) -> None: + self.error_kind = error_kind super().__init__(line) self.function_name = function_name self.args = args self.type = ret_type + self.steals = steals def to_str(self, env: Environment) -> str: args_str = ', '.join(env.format('%r', arg) for arg in self.args) @@ -1160,6 +1166,13 @@ def to_str(self, env: Environment) -> str: def sources(self) -> List[Value]: return self.args + def stolen(self) -> List[Value]: + if isinstance(self.steals, list): + assert len(self.steals) == len(self.args) + return [arg for arg, steal in zip(self.args, self.steals) if steal] + else: + return [] if not self.steals else self.sources() + def accept(self, visitor: 'OpVisitor[T]') -> T: return visitor.visit_call_c(self) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index beed68ca635a..0d3f91e50ae8 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -43,7 +43,7 @@ ) from mypyc.ir.func_ir import FuncIR, INVALID_FUNC_DEF from mypyc.ir.class_ir import ClassIR, NonExtClassInfo -from mypyc.primitives.registry import func_ops +from mypyc.primitives.registry import func_ops, CFunctionDescription, c_function_ops from mypyc.primitives.list_ops import list_len_op, to_list, list_pop_last from mypyc.primitives.dict_ops import dict_get_item_op, dict_set_item_op from mypyc.primitives.generic_ops import py_setattr_op, iter_op, next_op @@ -229,6 +229,9 @@ def gen_method_call(self, def load_module(self, name: str) -> Value: return self.builder.load_module(name) + def call_c(self, desc: CFunctionDescription, args: List[Value], line: int) -> Value: + return self.builder.call_c(desc, args, line) + @property def environment(self) -> Environment: return self.builder.environment @@ -498,7 +501,7 @@ def process_iterator_tuple_assignment(self, # Assign the starred value and all values after it if target.star_idx is not None: post_star_vals = target.items[split_idx + 1:] - iter_list = self.primitive_op(to_list, [iterator], line) + iter_list = self.call_c(to_list, [iterator], line) iter_list_len = self.primitive_op(list_len_op, [iter_list], line) post_star_len = self.add(LoadInt(len(post_star_vals))) condition = self.binary_op(post_star_len, iter_list_len, '<=', line) @@ -715,6 +718,11 @@ def call_refexpr_with_args( # Handle data-driven special-cased primitive call ops. if callee.fullname is not None and expr.arg_kinds == [ARG_POS] * len(arg_values): + call_c_ops_candidates = c_function_ops.get(callee.fullname, []) + target = self.builder.matching_call_c(call_c_ops_candidates, arg_values, + expr.line, self.node_type(expr)) + if target: + return target ops = func_ops.get(callee.fullname, []) target = self.builder.matching_primitive_op( ops, arg_values, expr.line, self.node_type(expr) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 607bf4b264ef..b8720a58bb96 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -26,7 +26,6 @@ from mypyc.ir.rtypes import ( RType, RUnion, RInstance, optional_value_type, int_rprimitive, float_rprimitive, bool_rprimitive, list_rprimitive, str_rprimitive, is_none_rprimitive, object_rprimitive, - void_rtype ) from mypyc.ir.func_ir import FuncDecl, FuncSignature from mypyc.ir.class_ir import ClassIR, all_concrete_classes @@ -35,7 +34,7 @@ ) from mypyc.primitives.registry import ( binary_ops, unary_ops, method_ops, func_ops, - c_method_call_ops, CFunctionDescription + c_method_call_ops, CFunctionDescription, c_function_ops ) from mypyc.primitives.list_ops import ( list_extend_op, list_len_op, new_list_op @@ -592,6 +591,10 @@ def builtin_call(self, args: List[Value], fn_op: str, line: int) -> Value: + call_c_ops_candidates = c_function_ops.get(fn_op, []) + target = self.matching_call_c(call_c_ops_candidates, args, line) + if target: + return target ops = func_ops.get(fn_op, []) target = self.matching_primitive_op(ops, args, line) assert target, 'Unsupported builtin function: %s' % fn_op @@ -667,13 +670,25 @@ def add_bool_branch(self, value: Value, true: BasicBlock, false: BasicBlock) -> self.add(Branch(value, true, false, Branch.BOOL_EXPR)) def call_c(self, - function_name: str, + desc: CFunctionDescription, args: List[Value], line: int, - result_type: Optional[RType]) -> Value: + result_type: Optional[RType] = None) -> Value: # handle void function via singleton RVoid instance - ret_type = void_rtype if result_type is None else result_type - target = self.add(CallC(function_name, args, ret_type, line)) + coerced = [] + for i, arg in enumerate(args): + formal_type = desc.arg_types[i] + arg = self.coerce(arg, formal_type, line) + coerced.append(arg) + target = self.add(CallC(desc.c_function_name, coerced, desc.return_type, desc.steals, + desc.error_kind, line)) + if result_type and not is_runtime_subtype(target.type, result_type): + if is_none_rprimitive(result_type): + # Special case None return. The actual result may actually be a bool + # and so we can't just coerce it. + target = self.none() + else: + target = self.coerce(target, result_type, line) return target def matching_call_c(self, @@ -697,7 +712,7 @@ def matching_call_c(self, else: matching = desc if matching: - target = self.call_c(matching.c_function_name, args, line, result_type) + target = self.call_c(matching, args, line, result_type) return target return None @@ -786,8 +801,8 @@ def translate_special_method_call(self, """ ops = method_ops.get(name, []) call_c_ops_candidates = c_method_call_ops.get(name, []) - call_c_op = self.matching_call_c(call_c_ops_candidates, [base_reg] + args, line, - result_type=result_type) + call_c_op = self.matching_call_c(call_c_ops_candidates, [base_reg] + args, + line, result_type) if call_c_op is not None: return call_c_op return self.matching_primitive_op(ops, [base_reg] + args, line, result_type=result_type) diff --git a/mypyc/primitives/list_ops.py b/mypyc/primitives/list_ops.py index 9239faf962cf..7d177782dcc9 100644 --- a/mypyc/primitives/list_ops.py +++ b/mypyc/primitives/list_ops.py @@ -8,7 +8,7 @@ ) from mypyc.primitives.registry import ( name_ref_op, binary_op, func_op, method_op, custom_op, name_emit, - call_emit, call_negative_bool_emit, + call_emit, call_negative_bool_emit, c_function_op ) @@ -20,12 +20,13 @@ is_borrowed=True) # list(obj) -to_list = func_op( +to_list = c_function_op( name='builtins.list', arg_types=[object_rprimitive], - result_type=list_rprimitive, + return_type=list_rprimitive, + c_function_name='PySequence_List', error_kind=ERR_MAGIC, - emit=call_emit('PySequence_List')) +) def emit_new(emitter: EmitterInterface, args: List[str], dest: str) -> None: @@ -83,7 +84,6 @@ def emit_new(emitter: EmitterInterface, args: List[str], dest: str) -> None: error_kind=ERR_FALSE, emit=call_emit('CPyList_SetItem')) - # list.append(obj) list_append_op = method_op( name='append', diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index 9e0ba1061157..eee929db2e35 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -45,9 +45,10 @@ CFunctionDescription = NamedTuple( 'CFunctionDescription', [('name', str), ('arg_types', List[RType]), - ('result_type', Optional[RType]), + ('return_type', RType), ('c_function_name', str), ('error_kind', int), + ('steals', StealsDescription), ('priority', int)]) # Primitive binary ops (key is operator such as '+') @@ -65,8 +66,12 @@ # Primitive ops for reading module attributes (key is name such as 'builtins.None') name_ref_ops = {} # type: Dict[str, OpDescription] +# CallC op for method call(such as 'str.join') c_method_call_ops = {} # type: Dict[str, List[CFunctionDescription]] +# CallC op for top level function call(such as 'builtins.list') +c_function_ops = {} # type: Dict[str, List[CFunctionDescription]] + def simple_emit(template: str) -> EmitCallback: """Construct a simple PrimitiveOp emit callback function. @@ -323,14 +328,30 @@ def custom_op(arg_types: List[RType], def c_method_op(name: str, arg_types: List[RType], - result_type: Optional[RType], + return_type: RType, c_function_name: str, error_kind: int, - priority: int = 1) -> None: + steals: StealsDescription = False, + priority: int = 1) -> CFunctionDescription: ops = c_method_call_ops.setdefault(name, []) - desc = CFunctionDescription(name, arg_types, result_type, - c_function_name, error_kind, priority) + desc = CFunctionDescription(name, arg_types, return_type, + c_function_name, error_kind, steals, priority) + ops.append(desc) + return desc + + +def c_function_op(name: str, + arg_types: List[RType], + return_type: RType, + c_function_name: str, + error_kind: int, + steals: StealsDescription = False, + priority: int = 1) -> CFunctionDescription: + ops = c_function_ops.setdefault(name, []) + desc = CFunctionDescription(name, arg_types, return_type, + c_function_name, error_kind, steals, priority) ops.append(desc) + return desc # Import various modules that set up global state. diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py index fabc4ba19216..03bd386ef05f 100644 --- a/mypyc/primitives/str_ops.py +++ b/mypyc/primitives/str_ops.py @@ -37,7 +37,7 @@ c_method_op( name='join', arg_types=[str_rprimitive, object_rprimitive], - result_type=str_rprimitive, + return_type=str_rprimitive, c_function_name='PyUnicode_Join', error_kind=ERR_MAGIC ) diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 024b4e8dba3b..cfe10d177523 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -3383,7 +3383,7 @@ L0: r5 = None return r5 -[case testCallCWithStrJoin] +[case testCallCWithStrJoinMethod] from typing import List def f(x: str, y: List[str]) -> str: return x.join(y) @@ -3395,3 +3395,39 @@ def f(x, y): L0: r0 = PyUnicode_Join(x, y) return r0 + +[case testCallCWithToListFunction] +from typing import List, Iterable, Tuple, Dict +# generic object +def f(x: Iterable[int]) -> List[int]: + return list(x) + +# need coercing +def g(x: Tuple[int, int, int]) -> List[int]: + return list(x) + +# non-list object +def h(x: Dict[int, str]) -> List[int]: + return list(x) + +[out] +def f(x): + x :: object + r0 :: list +L0: + r0 = PySequence_List(x) + return r0 +def g(x): + x :: tuple[int, int, int] + r0 :: object + r1 :: list +L0: + r0 = box(tuple[int, int, int], x) + r1 = PySequence_List(r0) + return r1 +def h(x): + x :: dict + r0 :: list +L0: + r0 = PySequence_List(x) + return r0 From c1f1f9566feefa2ade91b880f731d0b0cf76ff09 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Mon, 1 Jun 2020 23:52:16 +0800 Subject: [PATCH 013/138] Support binary ops via CallC (#8929) related mypyc/mypyc#709, mypyc/mypyc#734 * support binary ops, implement str += * support list * int, int * list --- mypyc/irbuild/ll_builder.py | 7 ++++++- mypyc/primitives/list_ops.py | 26 ++++++++++++-------------- mypyc/primitives/registry.py | 17 +++++++++++++++++ mypyc/primitives/str_ops.py | 14 +++++++------- mypyc/test-data/irbuild-lists.test | 4 ++-- 5 files changed, 44 insertions(+), 24 deletions(-) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index b8720a58bb96..98ab82f0c569 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -34,7 +34,8 @@ ) from mypyc.primitives.registry import ( binary_ops, unary_ops, method_ops, func_ops, - c_method_call_ops, CFunctionDescription, c_function_ops + c_method_call_ops, CFunctionDescription, c_function_ops, + c_binary_ops ) from mypyc.primitives.list_ops import ( list_extend_op, list_len_op, new_list_op @@ -541,6 +542,10 @@ def binary_op(self, if value is not None: return value + call_c_ops_candidates = c_binary_ops.get(expr_op, []) + target = self.matching_call_c(call_c_ops_candidates, [lreg, rreg], line) + if target: + return target ops = binary_ops.get(expr_op, []) target = self.matching_primitive_op(ops, [lreg, rreg], line) assert target, 'Unsupported binary operation: %s' % expr_op diff --git a/mypyc/primitives/list_ops.py b/mypyc/primitives/list_ops.py index 7d177782dcc9..76254dd35292 100644 --- a/mypyc/primitives/list_ops.py +++ b/mypyc/primitives/list_ops.py @@ -7,8 +7,8 @@ int_rprimitive, short_int_rprimitive, list_rprimitive, object_rprimitive, bool_rprimitive ) from mypyc.primitives.registry import ( - name_ref_op, binary_op, func_op, method_op, custom_op, name_emit, - call_emit, call_negative_bool_emit, c_function_op + name_ref_op, func_op, method_op, custom_op, name_emit, + call_emit, call_negative_bool_emit, c_function_op, c_binary_op ) @@ -125,20 +125,18 @@ def emit_new(emitter: EmitterInterface, args: List[str], dest: str) -> None: emit=call_emit('CPyList_Count')) # list * int -binary_op(op='*', - arg_types=[list_rprimitive, int_rprimitive], - result_type=list_rprimitive, - error_kind=ERR_MAGIC, - format_str='{dest} = {args[0]} * {args[1]} :: list', - emit=call_emit("CPySequence_Multiply")) +c_binary_op(name='*', + arg_types=[list_rprimitive, int_rprimitive], + return_type=list_rprimitive, + c_function_name='CPySequence_Multiply', + error_kind=ERR_MAGIC) # int * list -binary_op(op='*', - arg_types=[int_rprimitive, list_rprimitive], - result_type=list_rprimitive, - error_kind=ERR_MAGIC, - format_str='{dest} = {args[0]} * {args[1]} :: list', - emit=call_emit("CPySequence_RMultiply")) +c_binary_op(name='*', + arg_types=[int_rprimitive, list_rprimitive], + return_type=list_rprimitive, + c_function_name='CPySequence_RMultiply', + error_kind=ERR_MAGIC) def emit_len(emitter: EmitterInterface, args: List[str], dest: str) -> None: diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index eee929db2e35..8ed180f48576 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -72,6 +72,9 @@ # CallC op for top level function call(such as 'builtins.list') c_function_ops = {} # type: Dict[str, List[CFunctionDescription]] +# CallC op for binary ops +c_binary_ops = {} # type: Dict[str, List[CFunctionDescription]] + def simple_emit(template: str) -> EmitCallback: """Construct a simple PrimitiveOp emit callback function. @@ -354,6 +357,20 @@ def c_function_op(name: str, return desc +def c_binary_op(name: str, + arg_types: List[RType], + return_type: RType, + c_function_name: str, + error_kind: int, + steals: StealsDescription = False, + priority: int = 1) -> CFunctionDescription: + ops = c_binary_ops.setdefault(name, []) + desc = CFunctionDescription(name, arg_types, return_type, + c_function_name, error_kind, steals, priority) + ops.append(desc) + return desc + + # Import various modules that set up global state. import mypyc.primitives.int_ops # noqa import mypyc.primitives.str_ops # noqa diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py index 03bd386ef05f..2e261131257b 100644 --- a/mypyc/primitives/str_ops.py +++ b/mypyc/primitives/str_ops.py @@ -8,7 +8,7 @@ ) from mypyc.primitives.registry import ( func_op, binary_op, simple_emit, name_ref_op, method_op, call_emit, name_emit, - c_method_op + c_method_op, c_binary_op ) @@ -68,12 +68,12 @@ # # PyUnicodeAppend makes an effort to reuse the LHS when the refcount # is 1. This is super dodgy but oh well, the interpreter does it. -binary_op(op='+=', - arg_types=[str_rprimitive, str_rprimitive], - steals=[True, False], - result_type=str_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyStr_Append')) +c_binary_op(name='+=', + arg_types=[str_rprimitive, str_rprimitive], + return_type=str_rprimitive, + c_function_name='CPyStr_Append', + error_kind=ERR_MAGIC, + steals=[True, False]) def emit_str_compare(comparison: str) -> Callable[[EmitterInterface, List[str], str], None]: diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test index cd12bfcdf10e..de760d71914b 100644 --- a/mypyc/test-data/irbuild-lists.test +++ b/mypyc/test-data/irbuild-lists.test @@ -121,13 +121,13 @@ def f(a): r7 :: None L0: r0 = 2 - r1 = a * r0 :: list + r1 = CPySequence_Multiply(a, r0) b = r1 r2 = 3 r3 = 4 r4 = box(short_int, r3) r5 = [r4] - r6 = r2 * r5 :: list + r6 = CPySequence_RMultiply(r2, r5) b = r6 r7 = None return r7 From 48b70bb2596899e26ad16275e3bf1a3aa882ee1f Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 2 Jun 2020 13:04:22 +0100 Subject: [PATCH 014/138] Update docs to reflect that following imports is supported in dmypy (#8930) Mark this as experimental, since this hasn't been tested a lot yet. --- docs/source/mypy_daemon.rst | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/docs/source/mypy_daemon.rst b/docs/source/mypy_daemon.rst index ff91e476dfd4..3fa6029bd679 100644 --- a/docs/source/mypy_daemon.rst +++ b/docs/source/mypy_daemon.rst @@ -40,28 +40,24 @@ Use ``dmypy run -- `` to typecheck a set of files You can use almost arbitrary mypy flags after ``--``. The daemon will always run on the current host. Example:: - dmypy run -- --follow-imports=error prog.py pkg1/ pkg2/ - -.. note:: - You'll need to use either the :option:`--follow-imports=error ` or the - :option:`--follow-imports=skip ` option with dmypy because the current - implementation can't follow imports. - See :ref:`follow-imports` for details on how these work. - You can also define these using a - :ref:`configuration file `. + dmypy run -- prog.py pkg/*.py ``dmypy run`` will automatically restart the daemon if the configuration or mypy version changes. -You need to provide all files or directories you want to type check -(other than stubs) as arguments. This is a result of the -:option:`--follow-imports ` restriction mentioned above. - The initial run will process all the code and may take a while to finish, but subsequent runs will be quick, especially if you've only -changed a few files. You can use :ref:`remote caching ` +changed a few files. (You can use :ref:`remote caching ` to speed up the initial run. The speedup can be significant if -you have a large codebase. +you have a large codebase.) + +.. note:: + + Mypy 0.780 added support for following imports in dmypy (enabled by + default). This functionality is still experimental. You can use + ``--follow-imports=skip`` or ``--follow-imports=error`` to fall + back to the stable functionality. See :ref:`follow-imports` for + details on how these work. Daemon client commands ********************** From 63f2fed5d0e10d4d875442e9bba2283c81dfdf4b Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Tue, 2 Jun 2020 16:42:52 +0100 Subject: [PATCH 015/138] Add docs for no_site_packages config option (#8932) --- docs/source/config_file.rst | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index 70f4877b810a..07c3884de621 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -230,10 +230,17 @@ section of the command line docs. This option may only be set in the global section (``[mypy]``). +``no_site_packages`` (bool, default False) + Disables using type information in installed packages (see :pep:`561`). + This will also disable searching for a usable Python executable. This acts + the same as :option:`--no-site-packages ` command + line flag. + ``no_silence_site_packages`` (bool, default False) - Enables reporting error messages generated within :pep:`561` compliant packages. - Those error messages are suppressed by default, since you are usually - not able to control errors in 3rd party code. + Enables reporting error messages generated within installed packages (see + :pep:`561` for more details on distributing type information). Those error + messages are suppressed by default, since you are usually not able to + control errors in 3rd party code. This option may only be set in the global section (``[mypy]``). From dfcff6851c46f04c0b0ace903f614e23c656cb55 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Wed, 3 Jun 2020 18:01:43 +0800 Subject: [PATCH 016/138] [mypyc] Support unary_ops, add int_neg_op as an example (#8933) Related mypyc/mypyc#709, mypyc/mypyc#734 Support unary ops and provide int_neg_op as an example. --- mypyc/irbuild/ll_builder.py | 6 +++++- mypyc/primitives/int_ops.py | 19 +++++++++---------- mypyc/primitives/registry.py | 16 ++++++++++++++++ mypyc/test-data/analysis.test | 2 +- mypyc/test-data/irbuild-basic.test | 4 ++-- mypyc/test/test_emitfunc.py | 5 +++-- 6 files changed, 36 insertions(+), 16 deletions(-) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 98ab82f0c569..7ef4a67372a8 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -35,7 +35,7 @@ from mypyc.primitives.registry import ( binary_ops, unary_ops, method_ops, func_ops, c_method_call_ops, CFunctionDescription, c_function_ops, - c_binary_ops + c_binary_ops, c_unary_ops ) from mypyc.primitives.list_ops import ( list_extend_op, list_len_op, new_list_op @@ -555,6 +555,10 @@ def unary_op(self, lreg: Value, expr_op: str, line: int) -> Value: + call_c_ops_candidates = c_unary_ops.get(expr_op, []) + target = self.matching_call_c(call_c_ops_candidates, [lreg], line) + if target: + return target ops = unary_ops.get(expr_op, []) target = self.matching_primitive_op(ops, [lreg], line) assert target, 'Unsupported unary operation: %s' % expr_op diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index 6d162e937f76..7e1e70100d4c 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -6,14 +6,14 @@ See also the documentation for mypyc.rtypes.int_rprimitive. """ -from mypyc.ir.ops import OpDescription, ERR_NEVER, ERR_MAGIC +from mypyc.ir.ops import ERR_NEVER, ERR_MAGIC from mypyc.ir.rtypes import ( int_rprimitive, bool_rprimitive, float_rprimitive, object_rprimitive, short_int_rprimitive, str_rprimitive, RType ) from mypyc.primitives.registry import ( - name_ref_op, binary_op, unary_op, func_op, custom_op, - simple_emit, call_emit, name_emit, + name_ref_op, binary_op, func_op, custom_op, + simple_emit, call_emit, name_emit, c_unary_op, CFunctionDescription ) # These int constructors produce object_rprimitives that then need to be unboxed @@ -131,13 +131,12 @@ def int_compare_op(op: str, c_func_name: str) -> None: emit=simple_emit('{dest} = {args[0]} + {args[1]};')) -def int_unary_op(op: str, c_func_name: str) -> OpDescription: - return unary_op(op=op, - arg_type=int_rprimitive, - result_type=int_rprimitive, - error_kind=ERR_NEVER, - format_str='{dest} = %s{args[0]} :: int' % op, - emit=call_emit(c_func_name)) +def int_unary_op(name: str, c_function_name: str) -> CFunctionDescription: + return c_unary_op(name=name, + arg_type=int_rprimitive, + return_type=int_rprimitive, + c_function_name=c_function_name, + error_kind=ERR_NEVER) int_neg_op = int_unary_op('-', 'CPyTagged_Negate') diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index 8ed180f48576..22c92b7b50ca 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -75,6 +75,9 @@ # CallC op for binary ops c_binary_ops = {} # type: Dict[str, List[CFunctionDescription]] +# CallC op for unary ops +c_unary_ops = {} # type: Dict[str, List[CFunctionDescription]] + def simple_emit(template: str) -> EmitCallback: """Construct a simple PrimitiveOp emit callback function. @@ -371,6 +374,19 @@ def c_binary_op(name: str, return desc +def c_unary_op(name: str, + arg_type: RType, + return_type: RType, + c_function_name: str, + error_kind: int, + steals: StealsDescription = False, + priority: int = 1) -> CFunctionDescription: + ops = c_unary_ops.setdefault(name, []) + desc = CFunctionDescription(name, [arg_type], return_type, + c_function_name, error_kind, steals, priority) + ops.append(desc) + return desc + # Import various modules that set up global state. import mypyc.primitives.int_ops # noqa import mypyc.primitives.str_ops # noqa diff --git a/mypyc/test-data/analysis.test b/mypyc/test-data/analysis.test index 03c3e45f39aa..22e7648f121d 100644 --- a/mypyc/test-data/analysis.test +++ b/mypyc/test-data/analysis.test @@ -539,7 +539,7 @@ L3: if r5 goto L4 else goto L5 :: bool L4: r6 = 1 - r7 = -r6 :: int + r7 = CPyTagged_Negate(r6) restore_exc_info r1 return r7 L5: diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index cfe10d177523..241a3aaddcd2 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -463,7 +463,7 @@ def f(n): r1 :: int L0: r0 = 1 - r1 = -r0 :: int + r1 = CPyTagged_Negate(r0) return r1 [case testConditionalExpr] @@ -1027,7 +1027,7 @@ L1: r2 = x goto L3 L2: - r3 = -x :: int + r3 = CPyTagged_Negate(x) r2 = r3 L3: return r2 diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index 5bad7bc2a93b..ab814e35587d 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -8,7 +8,7 @@ from mypyc.ir.ops import ( Environment, BasicBlock, Goto, Return, LoadInt, Assign, IncRef, DecRef, Branch, Call, Unbox, Box, TupleGet, GetAttr, PrimitiveOp, RegisterOp, - SetAttr, Op, Value + SetAttr, Op, Value, CallC ) from mypyc.ir.rtypes import ( RTuple, RInstance, int_rprimitive, bool_rprimitive, list_rprimitive, @@ -98,7 +98,8 @@ def test_int_sub(self) -> None: "cpy_r_r0 = CPyTagged_Subtract(cpy_r_m, cpy_r_k);") def test_int_neg(self) -> None: - self.assert_emit(PrimitiveOp([self.m], int_neg_op, 55), + self.assert_emit(CallC(int_neg_op.c_function_name, [self.m], int_neg_op.return_type, + int_neg_op.steals, int_neg_op.error_kind, 55), "cpy_r_r0 = CPyTagged_Negate(cpy_r_m);") def test_list_len(self) -> None: From d49e245d902dbd5758e78787f4708f03085b941d Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 3 Jun 2020 13:24:26 +0100 Subject: [PATCH 017/138] Bump version (#8935) Co-authored-by: Ivan Levkivskyi --- mypy/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/version.py b/mypy/version.py index 81a8cfca378b..bf89a897e083 100644 --- a/mypy/version.py +++ b/mypy/version.py @@ -5,7 +5,7 @@ # - Release versions have the form "0.NNN". # - Dev versions have the form "0.NNN+dev" (PLUS sign to conform to PEP 440). # - For 1.0 we'll switch back to 1.2.3 form. -__version__ = '0.770+dev' +__version__ = '0.790+dev' base_version = __version__ mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) From c796a56a73ca04c281e2c6a8c2274fccc07ce966 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 3 Jun 2020 17:59:25 +0100 Subject: [PATCH 018/138] Report note about binary operation on the same location as error (#8936) This way we also suppress the note when ignoring the error. --- mypy/checkexpr.py | 10 +++++++--- mypy/errors.py | 4 ++++ mypy/messages.py | 10 +++++++++- test-data/unit/check-errorcodes.test | 12 ++++++++++++ test-data/unit/check-isinstance.test | 4 ++-- test-data/unit/check-unions.test | 4 ++-- 6 files changed, 36 insertions(+), 8 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 1f0b6f94c4f1..fd4a0436d425 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2680,12 +2680,16 @@ def check_op(self, method: str, base_type: Type, if msg.is_errors(): self.msg.add_errors(msg) + # Point any notes to the same location as an existing message. + recent_context = msg.most_recent_context() if len(left_variants) >= 2 and len(right_variants) >= 2: - self.msg.warn_both_operands_are_from_unions(context) + self.msg.warn_both_operands_are_from_unions(recent_context) elif len(left_variants) >= 2: - self.msg.warn_operand_was_from_union("Left", base_type, context=right_expr) + self.msg.warn_operand_was_from_union( + "Left", base_type, context=recent_context) elif len(right_variants) >= 2: - self.msg.warn_operand_was_from_union("Right", right_type, context=right_expr) + self.msg.warn_operand_was_from_union( + "Right", right_type, context=recent_context) # See the comment in 'check_overload_call' for more details on why # we call 'combine_function_signature' instead of just unioning the inferred diff --git a/mypy/errors.py b/mypy/errors.py index 12557573a655..b3747658b6f3 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -405,6 +405,10 @@ def is_errors_for_file(self, file: str) -> bool: """Are there any errors for the given file?""" return file in self.error_info_map + def most_recent_error_location(self) -> Tuple[int, int]: + info = self.error_info_map[self.file][-1] + return info.line, info.column + def raise_error(self, use_stdout: bool = True) -> None: """Raise a CompileError with the generated messages. diff --git a/mypy/messages.py b/mypy/messages.py index ecd61c4e0eda..f59c15344d06 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -31,7 +31,7 @@ FuncDef, reverse_builtin_aliases, ARG_POS, ARG_OPT, ARG_NAMED, ARG_NAMED_OPT, ARG_STAR, ARG_STAR2, ReturnStmt, NameExpr, Var, CONTRAVARIANT, COVARIANT, SymbolNode, - CallExpr, SymbolTable + CallExpr, SymbolTable, TempNode ) from mypy.subtypes import ( is_subtype, find_member, get_member_flags, @@ -145,6 +145,14 @@ def enable_errors(self) -> None: def is_errors(self) -> bool: return self.errors.is_errors() + def most_recent_context(self) -> Context: + """Return a dummy context matching the most recent generated error in current file.""" + line, column = self.errors.most_recent_error_location() + node = TempNode(NoneType()) + node.line = line + node.column = column + return node + def report(self, msg: str, context: Optional[Context], diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 2c1e52b04ac7..1639be052458 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -736,3 +736,15 @@ class C: def __rsub__(self, x): pass x - C() # type: ignore[operator] + +[case testErrorCodeMultiLineBinaryOperatorOperand] +# flags: --strict-optional +from typing import Optional + +class C: pass + +def f() -> Optional[C]: + return None + +f( # type: ignore[operator] +) + C() diff --git a/test-data/unit/check-isinstance.test b/test-data/unit/check-isinstance.test index e41b88fff8a7..0bc8bbb5f430 100644 --- a/test-data/unit/check-isinstance.test +++ b/test-data/unit/check-isinstance.test @@ -974,8 +974,8 @@ def foo() -> Union[int, str, A]: pass def bar() -> None: x = foo() x + 1 # E: Unsupported left operand type for + ("A") \ - # E: Unsupported operand types for + ("str" and "int") \ - # N: Left operand is of type "Union[int, str, A]" + # N: Left operand is of type "Union[int, str, A]" \ + # E: Unsupported operand types for + ("str" and "int") if isinstance(x, A): x.a else: diff --git a/test-data/unit/check-unions.test b/test-data/unit/check-unions.test index 0476de86c27a..4fcc1007ae48 100644 --- a/test-data/unit/check-unions.test +++ b/test-data/unit/check-unions.test @@ -303,9 +303,9 @@ from typing import Union class A: pass def f(x: Union[int, str, A]): x + object() # E: Unsupported left operand type for + ("A") \ + # N: Left operand is of type "Union[int, str, A]" \ # E: Unsupported operand types for + ("int" and "object") \ - # E: Unsupported operand types for + ("str" and "object") \ - # N: Left operand is of type "Union[int, str, A]" + # E: Unsupported operand types for + ("str" and "object") [builtins fixtures/primitives.pyi] [case testNarrowingDownNamedTupleUnion] From 348a9d4774b229b96c30a27ed2ca93992da5bd4f Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Wed, 3 Jun 2020 14:29:26 -0700 Subject: [PATCH 019/138] Track tuples in memprofile (#8943) --- mypy/memprofile.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mypy/memprofile.py b/mypy/memprofile.py index 4dde1abe588c..9ed2c4afee06 100644 --- a/mypy/memprofile.py +++ b/mypy/memprofile.py @@ -40,11 +40,16 @@ def collect_memory_stats() -> Tuple[Dict[str, int], if isinstance(x, list): # Keep track of which node a list is associated with. inferred[id(x)] = '%s (list)' % n + if isinstance(x, tuple): + # Keep track of which node a list is associated with. + inferred[id(x)] = '%s (tuple)' % n for k in get_class_descriptors(type(obj)): x = getattr(obj, k, None) if isinstance(x, list): inferred[id(x)] = '%s (list)' % n + if isinstance(x, tuple): + inferred[id(x)] = '%s (tuple)' % n freqs = {} # type: Dict[str, int] memuse = {} # type: Dict[str, int] From 3adb2e952cf9e16d4d493440a1c7be3de4d214ef Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 4 Jun 2020 15:24:57 +0100 Subject: [PATCH 020/138] Report some additional serious errors in junit.xml (#8950) Previously junit.xml was not generated for invalid package name errors, among other things. --- mypy/main.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/mypy/main.py b/mypy/main.py index 42430967dccd..a1a6fc17166d 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -97,11 +97,7 @@ def flush_errors(new_messages: List[str], serious: bool) -> None: ", ".join("[mypy-%s]" % glob for glob in options.per_module_options.keys() if glob in options.unused_configs)), file=stderr) - if options.junit_xml: - t1 = time.time() - py_version = '{}_{}'.format(options.python_version[0], options.python_version[1]) - util.write_junit_xml(t1 - t0, serious, messages, options.junit_xml, - py_version, options.platform) + maybe_write_junit_xml(time.time() - t0, serious, messages, options) if MEM_PROFILE: from mypy.memprofile import print_memory_profile @@ -907,10 +903,10 @@ def set_strict_flags() -> None: for p in special_opts.packages: if os.sep in p or os.altsep and os.altsep in p: fail("Package name '{}' cannot have a slash in it.".format(p), - stderr) + stderr, options) p_targets = cache.find_modules_recursive(p) if not p_targets: - fail("Can't find package '{}'".format(p), stderr) + fail("Can't find package '{}'".format(p), stderr, options) targets.extend(p_targets) for m in special_opts.modules: targets.append(BuildSource(None, m, None)) @@ -926,7 +922,7 @@ def set_strict_flags() -> None: # which causes issues when using the same variable to catch # exceptions of different types. except InvalidSourceList as e2: - fail(str(e2), stderr) + fail(str(e2), stderr, options) return targets, options @@ -987,6 +983,15 @@ def process_cache_map(parser: argparse.ArgumentParser, options.cache_map[source] = (meta_file, data_file) -def fail(msg: str, stderr: TextIO) -> None: +def maybe_write_junit_xml(td: float, serious: bool, messages: List[str], options: Options) -> None: + if options.junit_xml: + py_version = '{}_{}'.format(options.python_version[0], options.python_version[1]) + util.write_junit_xml( + td, serious, messages, options.junit_xml, py_version, options.platform) + + +def fail(msg: str, stderr: TextIO, options: Options) -> None: + """Fail with a serious error.""" stderr.write('%s\n' % msg) + maybe_write_junit_xml(0.0, serious=True, messages=[msg], options=options) sys.exit(2) From d98ba8e3e58cec46de23b40475a1e7874d52b9a5 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Sat, 6 Jun 2020 01:32:16 +0800 Subject: [PATCH 021/138] [mypyc] Introduce low level integer type (#8955) closes mypyc/mypyc#735 This PR introduces a c_int_rprimitive RType which represents a low level, plain integer (corresponds to C's Py_ssize_t). It also allows LoadInt to select its rtype to generate tagged/plain integer code accordingly. --- mypyc/codegen/emitfunc.py | 7 +++++-- mypyc/ir/ops.py | 4 ++-- mypyc/ir/rtypes.py | 10 +++++++++- mypyc/test/test_emitfunc.py | 4 +++- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index 7566d43ac67d..063d36cceb93 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -13,7 +13,7 @@ BasicBlock, Value, MethodCall, PrimitiveOp, EmitterInterface, Unreachable, NAMESPACE_STATIC, NAMESPACE_TYPE, NAMESPACE_MODULE, RaiseStandardError, CallC ) -from mypyc.ir.rtypes import RType, RTuple +from mypyc.ir.rtypes import RType, RTuple, is_c_int_rprimitive from mypyc.ir.func_ir import FuncIR, FuncDecl, FUNC_STATICMETHOD, FUNC_CLASSMETHOD from mypyc.ir.class_ir import ClassIR @@ -179,7 +179,10 @@ def visit_assign(self, op: Assign) -> None: def visit_load_int(self, op: LoadInt) -> None: dest = self.reg(op) - self.emit_line('%s = %d;' % (dest, op.value * 2)) + if is_c_int_rprimitive(op.type): + self.emit_line('%s = %d;' % (dest, op.value)) + else: + self.emit_line('%s = %d;' % (dest, op.value * 2)) def visit_load_error_value(self, op: LoadErrorValue) -> None: if isinstance(op.type, RTuple): diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 619d13ca898a..430059d51832 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -783,10 +783,10 @@ class LoadInt(RegisterOp): error_kind = ERR_NEVER - def __init__(self, value: int, line: int = -1) -> None: + def __init__(self, value: int, line: int = -1, rtype: RType = short_int_rprimitive) -> None: super().__init__(line) self.value = value - self.type = short_int_rprimitive + self.type = rtype def sources(self) -> List[Value]: return [] diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index 6bd947b6df5d..9615139f6461 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -174,7 +174,7 @@ def __init__(self, self.is_unboxed = is_unboxed self._ctype = ctype self.is_refcounted = is_refcounted - if ctype == 'CPyTagged': + if ctype in ('CPyTagged', 'Py_ssize_t'): self.c_undefined = 'CPY_INT_TAG' elif ctype == 'PyObject *': # Boxed types use the null pointer as the error value. @@ -234,6 +234,10 @@ def __repr__(self) -> str: short_int_rprimitive = RPrimitive('short_int', is_unboxed=True, is_refcounted=False, ctype='CPyTagged') # type: Final +# low level integer (corresponds to C's 'int'). +c_int_rprimitive = RPrimitive('c_int', is_unboxed=True, is_refcounted=False, + ctype='Py_ssize_t') # type: Final + # Floats are represent as 'float' PyObject * values. (In the future # we'll likely switch to a more efficient, unboxed representation.) float_rprimitive = RPrimitive('builtins.float', is_unboxed=False, @@ -275,6 +279,10 @@ def is_short_int_rprimitive(rtype: RType) -> bool: return rtype is short_int_rprimitive +def is_c_int_rprimitive(rtype: RType) -> bool: + return rtype is c_int_rprimitive + + def is_float_rprimitive(rtype: RType) -> bool: return isinstance(rtype, RPrimitive) and rtype.name == 'builtins.float' diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index ab814e35587d..43ca79cdaadb 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -12,7 +12,7 @@ ) from mypyc.ir.rtypes import ( RTuple, RInstance, int_rprimitive, bool_rprimitive, list_rprimitive, - dict_rprimitive, object_rprimitive + dict_rprimitive, object_rprimitive, c_int_rprimitive ) from mypyc.ir.func_ir import FuncIR, FuncDecl, RuntimeArg, FuncSignature from mypyc.ir.class_ir import ClassIR @@ -70,6 +70,8 @@ def test_return(self) -> None: def test_load_int(self) -> None: self.assert_emit(LoadInt(5), "cpy_r_r0 = 10;") + self.assert_emit(LoadInt(5, -1, c_int_rprimitive), + "cpy_r_r00 = 5;") def test_tuple_get(self) -> None: self.assert_emit(TupleGet(self.t, 1, 0), 'cpy_r_r0 = cpy_r_t.f1;') From 8cc06b70829c885a6f9f2a9f989994c51693f23c Mon Sep 17 00:00:00 2001 From: "Michael R. Crusoe" <1330696+mr-c@users.noreply.github.com> Date: Sat, 6 Jun 2020 20:07:33 +0200 Subject: [PATCH 022/138] include mypyc sub directories in the sdist (#8949) --- MANIFEST.in | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MANIFEST.in b/MANIFEST.in index cb0e4f1ec243..611ffe249b5c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -5,6 +5,15 @@ recursive-include docs * recursive-include mypy/typeshed *.py *.pyi recursive-include mypy/xml *.xsd *.xslt *.css recursive-include mypyc/lib-rt *.c *.h *.tmpl +recursive-include mypyc/ir *.py +recursive-include mypyc/codegen *.py +recursive-include mypyc/irbuild *.py +recursive-include mypyc/primitives *.py +recursive-include mypyc/transform *.py +recursive-include mypyc/test *.py +recursive-include mypyc/test-data *.test +recursive-include mypyc/test-data/fixtures *.py *.pyi +recursive-include mypyc/doc *.rst *.py *.md Makefile *.bat include mypy_bootstrap.ini include mypy_self_check.ini include LICENSE From fe216a3434899deb7a09b41489e752e1f32e48ff Mon Sep 17 00:00:00 2001 From: Chetan Khanna <31681374+ChetanKhanna@users.noreply.github.com> Date: Mon, 8 Jun 2020 18:18:13 +0530 Subject: [PATCH 023/138] Update .gitignore to include _build/ folder from docs/souce (#8911) --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9f169c19f89a..3b454ed5bdbb 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ __pycache__ /build /env*/ docs/build/ +docs/source/_build *.iml /out/ .venv*/ From a67b4afecb2daf7b19e3839541069f406186e98a Mon Sep 17 00:00:00 2001 From: Jakub Stasiak Date: Mon, 8 Jun 2020 15:36:15 +0200 Subject: [PATCH 024/138] Make reveal_type work with call expressions returning None (#8924) I think it's unnecessarily strict to raise an error in case like this. Was: error: "foo" does not return a value --- mypy/checkexpr.py | 3 ++- test-data/unit/check-functions.test | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index fd4a0436d425..03a2ac4521fc 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3023,7 +3023,8 @@ def visit_reveal_expr(self, expr: RevealExpr) -> Type: """Type check a reveal_type expression.""" if expr.kind == REVEAL_TYPE: assert expr.expr is not None - revealed_type = self.accept(expr.expr, type_context=self.type_context[-1]) + revealed_type = self.accept(expr.expr, type_context=self.type_context[-1], + allow_none_return=True) if not self.chk.current_node_deferred: self.msg.reveal_type(revealed_type, expr.expr) if not self.chk.in_checked_function(): diff --git a/test-data/unit/check-functions.test b/test-data/unit/check-functions.test index cff1818fd8f5..c0092f1057c2 100644 --- a/test-data/unit/check-functions.test +++ b/test-data/unit/check-functions.test @@ -2572,3 +2572,9 @@ lambda a=nonsense: a # E: Name 'nonsense' is not defined lambda a=(1 + 'asdf'): a # E: Unsupported operand types for + ("int" and "str") def f(x: int = i): # E: Name 'i' is not defined i = 42 + +[case testRevealTypeOfCallExpressionReturningNoneWorks] +def foo() -> None: + pass + +reveal_type(foo()) # N: Revealed type is 'None' From 109e15dbdfe6d82c6b5f8dac209c19eaf0dd4ee6 Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Mon, 8 Jun 2020 08:45:31 -0700 Subject: [PATCH 025/138] Change some sequence components in types from lists to tuples (#8945) This cuts down on memory a bit by avoiding the separate allocation inside lists and by allowing all empty sequences to share the empty tuple. --- mypy/checkexpr.py | 4 +++- mypy/expandtype.py | 2 +- mypy/exprtotype.py | 2 +- mypy/messages.py | 2 +- mypy/semanal_shared.py | 2 +- mypy/suggestions.py | 2 +- mypy/typeanal.py | 9 +++++---- mypy/types.py | 12 ++++++------ test-data/unit/plugins/dyn_class.py | 2 +- 9 files changed, 20 insertions(+), 17 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 03a2ac4521fc..450993a90c4d 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3135,7 +3135,9 @@ class LongName(Generic[T]): ... # This type is invalid in most runtime contexts, give it an 'object' type. return self.named_type('builtins.object') - def apply_type_arguments_to_callable(self, tp: Type, args: List[Type], ctx: Context) -> Type: + def apply_type_arguments_to_callable( + self, tp: Type, args: Sequence[Type], ctx: Context + ) -> Type: """Apply type arguments to a generic callable type coming from a type object. This will first perform type arguments count checks, report the diff --git a/mypy/expandtype.py b/mypy/expandtype.py index cdcb9c77dec2..b805f3c0be83 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -20,7 +20,7 @@ def expand_type_by_instance(typ: Type, instance: Instance) -> Type: """Substitute type variables in type using values from an Instance. Type variables are considered to be bound by the class declaration.""" # TODO: use an overloaded signature? (ProperType stays proper after expansion.) - if instance.args == []: + if not instance.args: return typ else: variables = {} # type: Dict[TypeVarId, Type] diff --git a/mypy/exprtotype.py b/mypy/exprtotype.py index dac9063eb946..a3b762a1b64f 100644 --- a/mypy/exprtotype.py +++ b/mypy/exprtotype.py @@ -61,7 +61,7 @@ def expr_to_unanalyzed_type(expr: Expression, _parent: Optional[Expression] = No args = expr.index.items else: args = [expr.index] - base.args = [expr_to_unanalyzed_type(arg, expr) for arg in args] + base.args = tuple(expr_to_unanalyzed_type(arg, expr) for arg in args) if not base.args: base.empty_tuple_index = True return base diff --git a/mypy/messages.py b/mypy/messages.py index f59c15344d06..941c7adc3634 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -1595,7 +1595,7 @@ def format(typ: Type) -> str: base_str = itype.type.fullname else: base_str = itype.type.name - if itype.args == []: + if not itype.args: # No type arguments, just return the type name return base_str elif itype.type.fullname == 'builtins.tuple': diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index ced37c600f14..ba0972e8c302 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -221,4 +221,4 @@ def calculate_tuple_fallback(typ: TupleType) -> None: """ fallback = typ.partial_fallback assert fallback.type.fullname == 'builtins.tuple' - fallback.args[0] = join.join_type_list(list(typ.items)) + fallback.args = (join.join_type_list(list(typ.items)),) + fallback.args[1:] diff --git a/mypy/suggestions.py b/mypy/suggestions.py index ab9dd8260b0b..d8a927b39590 100644 --- a/mypy/suggestions.py +++ b/mypy/suggestions.py @@ -805,7 +805,7 @@ def visit_instance(self, t: Instance) -> str: if (mod, obj) == ('builtins', 'tuple'): mod, obj = 'typing', 'Tuple[' + t.args[0].accept(self) + ', ...]' - elif t.args != []: + elif t.args: obj += '[{}]'.format(self.list_str(t.args)) if mod_obj == ('builtins', 'unicode'): diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 5fcbaa0a2a94..f1a96eacd23e 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -5,7 +5,7 @@ from contextlib import contextmanager from mypy.ordered_dict import OrderedDict -from typing import Callable, List, Optional, Set, Tuple, Iterator, TypeVar, Iterable +from typing import Callable, List, Optional, Set, Tuple, Iterator, TypeVar, Iterable, Sequence from typing_extensions import Final from mypy_extensions import DefaultNamedArg @@ -324,7 +324,8 @@ def get_omitted_any(self, typ: Type, fullname: Optional[str] = None) -> AnyType: disallow_any = not self.is_typeshed_stub and self.options.disallow_any_generics return get_omitted_any(disallow_any, self.fail, self.note, typ, fullname) - def analyze_type_with_type_info(self, info: TypeInfo, args: List[Type], ctx: Context) -> Type: + def analyze_type_with_type_info( + self, info: TypeInfo, args: Sequence[Type], ctx: Context) -> Type: """Bind unbound type when were able to find target TypeInfo. This handles simple cases like 'int', 'modname.UserClass[str]', etc. @@ -951,7 +952,7 @@ def fix_instance(t: Instance, fail: MsgCallback, note: MsgCallback, else: fullname = t.type.fullname any_type = get_omitted_any(disallow_any, fail, note, t, fullname, unexpanded_type) - t.args = [any_type] * len(t.type.type_vars) + t.args = (any_type,) * len(t.type.type_vars) return # Invalid number of type parameters. n = len(t.type.type_vars) @@ -968,7 +969,7 @@ def fix_instance(t: Instance, fail: MsgCallback, note: MsgCallback, # Construct the correct number of type arguments, as # otherwise the type checker may crash as it expects # things to be right. - t.args = [AnyType(TypeOfAny.from_error) for _ in t.type.type_vars] + t.args = tuple(AnyType(TypeOfAny.from_error) for _ in t.type.type_vars) t.invalid = True diff --git a/mypy/types.py b/mypy/types.py index 3500f30e49c5..98943e374e48 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -397,7 +397,7 @@ class UnboundType(ProperType): def __init__(self, name: Optional[str], - args: Optional[List[Type]] = None, + args: Optional[Sequence[Type]] = None, line: int = -1, column: int = -1, optional: bool = False, @@ -410,7 +410,7 @@ def __init__(self, args = [] assert name is not None self.name = name - self.args = args + self.args = tuple(args) # Should this type be wrapped in an Optional? self.optional = optional # Special case for X[()] @@ -432,7 +432,7 @@ def __init__(self, self.original_str_fallback = original_str_fallback def copy_modified(self, - args: Bogus[Optional[List[Type]]] = _dummy, + args: Bogus[Optional[Sequence[Type]]] = _dummy, ) -> 'UnboundType': if args is _dummy: args = self.args @@ -731,12 +731,12 @@ class Instance(ProperType): __slots__ = ('type', 'args', 'erased', 'invalid', 'type_ref', 'last_known_value') - def __init__(self, typ: mypy.nodes.TypeInfo, args: List[Type], + def __init__(self, typ: mypy.nodes.TypeInfo, args: Sequence[Type], line: int = -1, column: int = -1, erased: bool = False, last_known_value: Optional['LiteralType'] = None) -> None: super().__init__(line, column) self.type = typ - self.args = args + self.args = tuple(args) self.type_ref = None # type: Optional[str] # True if result of type variable substitution @@ -2013,7 +2013,7 @@ def visit_instance(self, t: Instance) -> str: if t.erased: s += '*' - if t.args != []: + if t.args: s += '[{}]'.format(self.list_str(t.args)) if self.id_mapper: s += '<{}>'.format(self.id_mapper.id(t.type)) diff --git a/test-data/unit/plugins/dyn_class.py b/test-data/unit/plugins/dyn_class.py index 266284a21de3..56ef89e17869 100644 --- a/test-data/unit/plugins/dyn_class.py +++ b/test-data/unit/plugins/dyn_class.py @@ -39,7 +39,7 @@ def replace_col_hook(ctx): if new_sym: new_info = new_sym.node assert isinstance(new_info, TypeInfo) - node.type = Instance(new_info, node.type.args.copy(), + node.type = Instance(new_info, node.type.args, node.type.line, node.type.column) From 1ae08558ca003d2bf06cf936fe45034cbd0bf323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B0=AD=E4=B9=9D=E9=BC=8E?= <109224573@qq.com> Date: Mon, 8 Jun 2020 23:56:24 +0800 Subject: [PATCH 026/138] Readme: use https links (#8954) --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a9fbad192d6b..d9f2e7d5abcf 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ def fib(n: int) -> Iterator[int]: yield a a, b = b, a + b ``` -See [the documentation](http://mypy.readthedocs.io/en/stable/introduction.html) for more examples. +See [the documentation](https://mypy.readthedocs.io/en/stable/introduction.html) for more examples. For Python 2.7, the standard annotations are written as comments: ```python @@ -56,7 +56,7 @@ def is_palindrome(s): return s == s[::-1] ``` -See [the documentation for Python 2 support](http://mypy.readthedocs.io/en/latest/python2.html). +See [the documentation for Python 2 support](https://mypy.readthedocs.io/en/latest/python2.html). Mypy is in development; some features are missing and there are bugs. See 'Development status' below. @@ -73,7 +73,7 @@ In Ubuntu, Mint and Debian you can install Python 3 like this: For other Linux flavors, macOS and Windows, packages are available at - http://www.python.org/getit/ + https://www.python.org/getit/ Quick start @@ -125,7 +125,7 @@ Mypy can be integrated into popular IDEs: Mypy can also be integrated into [Flake8] using [flake8-mypy], or can be set up as a pre-commit hook using [pre-commit mirrors-mypy]. -[Flake8]: http://flake8.pycqa.org/ +[Flake8]: https://flake8.pycqa.org/ [flake8-mypy]: https://github.com/ambv/flake8-mypy [pre-commit mirrors-mypy]: https://github.com/pre-commit/mirrors-mypy @@ -218,7 +218,7 @@ see "Troubleshooting" above. Working with the git version of mypy ------------------------------------ -mypy contains a submodule, "typeshed". See http://github.com/python/typeshed. +mypy contains a submodule, "typeshed". See https://github.com/python/typeshed. This submodule contains types for the Python standard library. Due to the way git submodules work, you'll have to do @@ -256,7 +256,7 @@ future. Changelog --------- -Follow mypy's updates on the blog: http://mypy-lang.blogspot.com/ +Follow mypy's updates on the blog: https://mypy-lang.blogspot.com/ Issue tracker From 3b2e114274735a78031d01f3f812451213e11682 Mon Sep 17 00:00:00 2001 From: Chetan Khanna <31681374+ChetanKhanna@users.noreply.github.com> Date: Mon, 8 Jun 2020 22:46:33 +0530 Subject: [PATCH 027/138] Discuss unreachable code as a common issue (#8899) Documented unreachable code issues and --warn-unreachable usage --- docs/source/common_issues.rst | 52 +++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/docs/source/common_issues.rst b/docs/source/common_issues.rst index e0d6106eadba..704c9a23f76d 100644 --- a/docs/source/common_issues.rst +++ b/docs/source/common_issues.rst @@ -808,3 +808,55 @@ not necessary: class NarrowerArgument(A): def test(self, t: List[int]) -> Sequence[str]: # type: ignore[override] ... + +Unreachable code during typechecking +------------------------------------ + +Sometimes a part of the code can become unreachable, even if not immediately obvious. +It is important to note that in such cases, that part of the code will *not* be type-checked +by mypy anymore. Consider the following code snippet: + +.. code-block:: python + + class Foo: + bar:str = '' + + def bar() -> None: + foo: Foo = Foo() + return + x:int = 'abc' + +It is trivial to notice that any statement after ``return`` is unreachable and hence mypy will +not complain about the mis-typed code below it. For a more subtle example, consider: + +.. code-block:: python + + class Foo: + bar:str = '' + + def bar() -> None: + foo: Foo = Foo() + assert foo.bar is None + x:int = 'abc' + +Again, mypy will not throw any errors because the type of ``foo.bar`` says it's ``str`` and never ``None``. +Hence the ``assert`` statement will always fail and the statement below will never be executed. +Note that in Python, ``None`` is not a null-reference but an object of type ``NoneType``. This can +also be demonstrated by the following: + +.. code-block:: python + + class Foo: + bar: str = '' + + def bar() -> None: + foo: Foo = Foo() + if not foo.bar: + return + x:int = 'abc' + +Here mypy will go on to check the last line as well and report ``Incompatible types in assignment``. + +If we want to let mypy warn us of such unreachable code blocks, we can use the ``--warn-unreachable`` +option. With this mypy will throw ``Statement is unreachable`` error along with the line number from +where the unreachable block starts. From 793cf187f0e529f9d6623a512ae0149124ded542 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Tue, 9 Jun 2020 17:17:00 +0800 Subject: [PATCH 028/138] [mypyc] Support var arg in CallC, replace new_dict_op (#8948) Related: mypyc/mypyc#709, mypyc/mypyc#734 Summary: * introduce variable arguments in CallC * replace old new_dict_op (which relies on a specialized emit callback) with two CallC ops: dict_new_op and dict_build_op, which handles create an empty dict and a dict from k-v pairs. * fix related IR tests, especially separate the testDel case into three subcases which test list, dict and attributes respectively. --- mypyc/ir/ops.py | 4 +- mypyc/irbuild/classdef.py | 10 +- mypyc/irbuild/expression.py | 4 +- mypyc/irbuild/ll_builder.py | 44 +++++- mypyc/primitives/dict_ops.py | 33 +++-- mypyc/primitives/registry.py | 65 ++++++++- mypyc/test-data/irbuild-basic.test | 170 +++++++++++++----------- mypyc/test-data/irbuild-dict.test | 46 ++++--- mypyc/test-data/irbuild-statements.test | 128 ++++++++++-------- mypyc/test-data/refcount.test | 24 ++-- mypyc/test/test_emitfunc.py | 5 +- 11 files changed, 323 insertions(+), 210 deletions(-) diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 430059d51832..75d67d603940 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -1151,13 +1151,15 @@ def __init__(self, ret_type: RType, steals: StealsDescription, error_kind: int, - line: int) -> None: + line: int, + var_arg_idx: int = -1) -> None: self.error_kind = error_kind super().__init__(line) self.function_name = function_name self.args = args self.type = ret_type self.steals = steals + self.var_arg_idx = var_arg_idx # the position of the first variable argument in args def to_str(self, env: Environment) -> str: args_str = ', '.join(env.format('%r', arg) for arg in self.args) diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index f52e480caedc..7d9244b23f83 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -21,7 +21,7 @@ dataclass_sleight_of_hand, pytype_from_template_op, py_calc_meta_op, type_object_op, not_implemented_op, true_op ) -from mypyc.primitives.dict_ops import dict_set_item_op, new_dict_op +from mypyc.primitives.dict_ops import dict_set_item_op, dict_new_op from mypyc.primitives.tuple_ops import new_tuple_op from mypyc.common import SELF_NAME from mypyc.irbuild.util import ( @@ -73,7 +73,7 @@ def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None: # We populate __annotations__ for non-extension classes # because dataclasses uses it to determine which attributes to compute on. # TODO: Maybe generate more precise types for annotations - non_ext_anns = builder.primitive_op(new_dict_op, [], cdef.line) + non_ext_anns = builder.call_c(dict_new_op, [], cdef.line) non_ext = NonExtClassInfo(non_ext_dict, non_ext_bases, non_ext_anns, non_ext_metaclass) dataclass_non_ext = None type_obj = None @@ -258,7 +258,7 @@ def setup_non_ext_dict(builder: IRBuilder, builder.goto(exit_block) builder.activate_block(false_block) - builder.assign(non_ext_dict, builder.primitive_op(new_dict_op, [], cdef.line), cdef.line) + builder.assign(non_ext_dict, builder.call_c(dict_new_op, [], cdef.line), cdef.line) builder.goto(exit_block) builder.activate_block(exit_block) @@ -518,9 +518,9 @@ def dataclass_non_ext_info(builder: IRBuilder, cdef: ClassDef) -> Optional[NonEx """ if is_dataclass(cdef): return NonExtClassInfo( - builder.primitive_op(new_dict_op, [], cdef.line), + builder.call_c(dict_new_op, [], cdef.line), builder.add(TupleSet([], cdef.line)), - builder.primitive_op(new_dict_op, [], cdef.line), + builder.call_c(dict_new_op, [], cdef.line), builder.primitive_op(type_object_op, [], cdef.line), ) else: diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index caa979713cf3..25e086bea38d 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -25,7 +25,7 @@ from mypyc.primitives.misc_ops import new_slice_op, ellipsis_op, type_op from mypyc.primitives.list_ops import new_list_op, list_append_op, list_extend_op from mypyc.primitives.tuple_ops import list_tuple_op -from mypyc.primitives.dict_ops import new_dict_op, dict_set_item_op +from mypyc.primitives.dict_ops import dict_new_op, dict_set_item_op from mypyc.primitives.set_ops import new_set_op, set_add_op, set_update_op from mypyc.irbuild.specialize import specializers from mypyc.irbuild.builder import IRBuilder @@ -541,7 +541,7 @@ def gen_inner_stmts() -> None: def transform_dictionary_comprehension(builder: IRBuilder, o: DictionaryComprehension) -> Value: - d = builder.primitive_op(new_dict_op, [], o.line) + d = builder.call_c(dict_new_op, [], o.line) loop_params = list(zip(o.indices, o.sequences, o.condlists)) def gen_inner_stmts() -> None: diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 7ef4a67372a8..7a43d51a7230 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -26,6 +26,7 @@ from mypyc.ir.rtypes import ( RType, RUnion, RInstance, optional_value_type, int_rprimitive, float_rprimitive, bool_rprimitive, list_rprimitive, str_rprimitive, is_none_rprimitive, object_rprimitive, + c_int_rprimitive ) from mypyc.ir.func_ir import FuncDecl, FuncSignature from mypyc.ir.class_ir import ClassIR, all_concrete_classes @@ -41,7 +42,9 @@ list_extend_op, list_len_op, new_list_op ) from mypyc.primitives.tuple_ops import list_tuple_op, new_tuple_op -from mypyc.primitives.dict_ops import new_dict_op, dict_update_in_display_op +from mypyc.primitives.dict_ops import ( + dict_update_in_display_op, dict_new_op, dict_build_op +) from mypyc.primitives.generic_ops import ( py_getattr_op, py_call_op, py_call_with_kwargs_op, py_method_call_op ) @@ -566,12 +569,14 @@ def unary_op(self, def make_dict(self, key_value_pairs: Sequence[DictEntry], line: int) -> Value: result = None # type: Union[Value, None] - initial_items = [] # type: List[Value] + keys = [] # type: List[Value] + values = [] # type: List[Value] for key, value in key_value_pairs: if key is not None: # key:value if result is None: - initial_items.extend((key, value)) + keys.append(key) + values.append(value) continue self.translate_special_method_call( @@ -583,7 +588,7 @@ def make_dict(self, key_value_pairs: Sequence[DictEntry], line: int) -> Value: else: # **value if result is None: - result = self.primitive_op(new_dict_op, initial_items, line) + result = self._create_dict(keys, values, line) self.primitive_op( dict_update_in_display_op, @@ -592,7 +597,7 @@ def make_dict(self, key_value_pairs: Sequence[DictEntry], line: int) -> Value: ) if result is None: - result = self.primitive_op(new_dict_op, initial_items, line) + result = self._create_dict(keys, values, line) return result @@ -685,12 +690,22 @@ def call_c(self, result_type: Optional[RType] = None) -> Value: # handle void function via singleton RVoid instance coerced = [] - for i, arg in enumerate(args): + # coerce fixed number arguments + for i in range(min(len(args), len(desc.arg_types))): formal_type = desc.arg_types[i] + arg = args[i] arg = self.coerce(arg, formal_type, line) coerced.append(arg) + # coerce any var_arg + var_arg_idx = -1 + if desc.var_arg_type is not None: + var_arg_idx = len(desc.arg_types) + for i in range(len(desc.arg_types), len(args)): + arg = args[i] + arg = self.coerce(arg, desc.var_arg_type, line) + coerced.append(arg) target = self.add(CallC(desc.c_function_name, coerced, desc.return_type, desc.steals, - desc.error_kind, line)) + desc.error_kind, line, var_arg_idx)) if result_type and not is_runtime_subtype(target.type, result_type): if is_none_rprimitive(result_type): # Special case None return. The actual result may actually be a bool @@ -859,3 +874,18 @@ def translate_eq_cmp(self, ltype, line ) + + def _create_dict(self, + keys: List[Value], + values: List[Value], + line: int) -> Value: + """Create a dictionary(possibly empty) using keys and values""" + # keys and values should have the same number of items + size = len(keys) + if size > 0: + load_size_op = self.add(LoadInt(size, -1, c_int_rprimitive)) + # merge keys and values + items = [i for t in list(zip(keys, values)) for i in t] + return self.call_c(dict_build_op, [load_size_op] + items, line) + else: + return self.call_c(dict_new_op, [], line) diff --git a/mypyc/primitives/dict_ops.py b/mypyc/primitives/dict_ops.py index cc7e0ab1084a..39eb555bfd43 100644 --- a/mypyc/primitives/dict_ops.py +++ b/mypyc/primitives/dict_ops.py @@ -5,13 +5,13 @@ from mypyc.ir.ops import EmitterInterface, ERR_FALSE, ERR_MAGIC, ERR_NEVER from mypyc.ir.rtypes import ( dict_rprimitive, object_rprimitive, bool_rprimitive, int_rprimitive, - list_rprimitive, dict_next_rtuple_single, dict_next_rtuple_pair + list_rprimitive, dict_next_rtuple_single, dict_next_rtuple_pair, c_int_rprimitive ) from mypyc.primitives.registry import ( name_ref_op, method_op, binary_op, func_op, custom_op, simple_emit, negative_int_emit, call_emit, call_negative_bool_emit, - name_emit, + name_emit, c_custom_op ) @@ -88,25 +88,22 @@ error_kind=ERR_MAGIC, emit=simple_emit('{dest} = CPyDict_Get({args[0]}, {args[1]}, Py_None);')) - -def emit_new_dict(emitter: EmitterInterface, args: List[str], dest: str) -> None: - if not args: - emitter.emit_line('%s = PyDict_New();' % (dest,)) - return - - emitter.emit_line('%s = CPyDict_Build(%s, %s);' % (dest, len(args) // 2, ', '.join(args))) - +# Construct an empty dictionary. +dict_new_op = c_custom_op( + arg_types=[], + return_type=dict_rprimitive, + c_function_name='PyDict_New', + error_kind=ERR_MAGIC) # Construct a dictionary from keys and values. -# Arguments are (key1, value1, ..., keyN, valueN). -new_dict_op = custom_op( - name='builtins.dict', - arg_types=[object_rprimitive], - is_var_arg=True, - result_type=dict_rprimitive, - format_str='{dest} = {{{colon_args}}}', +# Positional argument is the number of key-value pairs +# Variable arguments are (key1, value1, ..., keyN, valueN). +dict_build_op = c_custom_op( + arg_types=[c_int_rprimitive], + return_type=dict_rprimitive, + c_function_name='CPyDict_Build', error_kind=ERR_MAGIC, - emit=emit_new_dict) + var_arg_type=object_rprimitive,) # Construct a dictionary from another dictionary. func_op( diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index 22c92b7b50ca..4ef2e4eb1faa 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -46,6 +46,7 @@ 'CFunctionDescription', [('name', str), ('arg_types', List[RType]), ('return_type', RType), + ('var_arg_type', Optional[RType]), ('c_function_name', str), ('error_kind', int), ('steals', StealsDescription), @@ -337,10 +338,25 @@ def c_method_op(name: str, return_type: RType, c_function_name: str, error_kind: int, + var_arg_type: Optional[RType] = None, steals: StealsDescription = False, priority: int = 1) -> CFunctionDescription: + """Define a c function call op that replaces a method call. + + This will be automatically generated by matching against the AST. + + Args: + name: short name of the method (for example, 'append') + arg_types: argument types; the receiver is always the first argument + return_type: type of the return value. Use void_rtype to represent void. + c_function_name: name of the C function to call + error_kind: how errors are represented in the result (one of ERR_*) + var_arg_type: type of all variable arguments + steals: description of arguments that this steals (ref count wise) + priority: if multiple ops match, the one with the highest priority is picked + """ ops = c_method_call_ops.setdefault(name, []) - desc = CFunctionDescription(name, arg_types, return_type, + desc = CFunctionDescription(name, arg_types, return_type, var_arg_type, c_function_name, error_kind, steals, priority) ops.append(desc) return desc @@ -351,10 +367,21 @@ def c_function_op(name: str, return_type: RType, c_function_name: str, error_kind: int, + var_arg_type: Optional[RType] = None, steals: StealsDescription = False, priority: int = 1) -> CFunctionDescription: + """Define a c function call op that replaces a function call. + + This will be automatically generated by matching against the AST. + + Most arguments are similar to c_method_op(). + + Args: + name: full name of the function + arg_types: positional argument types for which this applies + """ ops = c_function_ops.setdefault(name, []) - desc = CFunctionDescription(name, arg_types, return_type, + desc = CFunctionDescription(name, arg_types, return_type, var_arg_type, c_function_name, error_kind, steals, priority) ops.append(desc) return desc @@ -365,15 +392,37 @@ def c_binary_op(name: str, return_type: RType, c_function_name: str, error_kind: int, + var_arg_type: Optional[RType] = None, steals: StealsDescription = False, priority: int = 1) -> CFunctionDescription: + """Define a c function call op for a binary operation. + + This will be automatically generated by matching against the AST. + + Most arguments are similar to c_method_op(), but exactly two argument types + are expected. + """ ops = c_binary_ops.setdefault(name, []) - desc = CFunctionDescription(name, arg_types, return_type, + desc = CFunctionDescription(name, arg_types, return_type, var_arg_type, c_function_name, error_kind, steals, priority) ops.append(desc) return desc +def c_custom_op(arg_types: List[RType], + return_type: RType, + c_function_name: str, + error_kind: int, + var_arg_type: Optional[RType] = None, + steals: StealsDescription = False) -> CFunctionDescription: + """Create a one-off CallC op that can't be automatically generated from the AST. + + Most arguments are similar to c_method_op(). + """ + return CFunctionDescription('', arg_types, return_type, var_arg_type, + c_function_name, error_kind, steals, 0) + + def c_unary_op(name: str, arg_type: RType, return_type: RType, @@ -381,12 +430,20 @@ def c_unary_op(name: str, error_kind: int, steals: StealsDescription = False, priority: int = 1) -> CFunctionDescription: + """Define a c function call op for an unary operation. + + This will be automatically generated by matching against the AST. + + Most arguments are similar to c_method_op(), but exactly one argument type + is expected. + """ ops = c_unary_ops.setdefault(name, []) - desc = CFunctionDescription(name, [arg_type], return_type, + desc = CFunctionDescription(name, [arg_type], return_type, None, c_function_name, error_kind, steals, priority) ops.append(desc) return desc + # Import various modules that set up global state. import mypyc.primitives.int_ops # noqa import mypyc.primitives.str_ops # noqa diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 241a3aaddcd2..292f13e242b3 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -1088,20 +1088,22 @@ def call_python_function_with_keyword_arg(x): r1 :: object r2 :: str r3 :: tuple - r4 :: object - r5 :: dict - r6 :: object - r7 :: int + r4 :: c_int + r5 :: object + r6 :: dict + r7 :: object + r8 :: int L0: r0 = 2 r1 = int r2 = unicode_3 :: static ('base') r3 = (x) :: tuple - r4 = box(short_int, r0) - r5 = {r2: r4} - r6 = py_call_with_kwargs(r1, r3, r5) - r7 = unbox(int, r6) - return r7 + r4 = 1 + r5 = box(short_int, r0) + r6 = CPyDict_Build(r4, r2, r5) + r7 = py_call_with_kwargs(r1, r3, r6) + r8 = unbox(int, r7) + return r8 def call_python_method_with_keyword_args(xs, first, second): xs :: list first, second :: int @@ -1111,17 +1113,19 @@ def call_python_method_with_keyword_args(xs, first, second): r3 :: str r4 :: object r5 :: tuple - r6 :: object - r7 :: dict - r8 :: object - r9 :: short_int - r10 :: str - r11 :: object - r12, r13 :: str - r14 :: tuple - r15, r16 :: object - r17 :: dict - r18 :: object + r6 :: c_int + r7 :: object + r8 :: dict + r9 :: object + r10 :: short_int + r11 :: str + r12 :: object + r13, r14 :: str + r15 :: tuple + r16 :: c_int + r17, r18 :: object + r19 :: dict + r20 :: object L0: r0 = 0 r1 = unicode_4 :: static ('insert') @@ -1129,19 +1133,21 @@ L0: r3 = unicode_5 :: static ('x') r4 = box(short_int, r0) r5 = (r4) :: tuple - r6 = box(int, first) - r7 = {r3: r6} - r8 = py_call_with_kwargs(r2, r5, r7) - r9 = 1 - r10 = unicode_4 :: static ('insert') - r11 = getattr xs, r10 - r12 = unicode_5 :: static ('x') - r13 = unicode_6 :: static ('i') - r14 = () :: tuple - r15 = box(int, second) - r16 = box(short_int, r9) - r17 = {r12: r15, r13: r16} - r18 = py_call_with_kwargs(r11, r14, r17) + r6 = 1 + r7 = box(int, first) + r8 = CPyDict_Build(r6, r3, r7) + r9 = py_call_with_kwargs(r2, r5, r8) + r10 = 1 + r11 = unicode_4 :: static ('insert') + r12 = getattr xs, r11 + r13 = unicode_5 :: static ('x') + r14 = unicode_6 :: static ('i') + r15 = () :: tuple + r16 = 2 + r17 = box(int, second) + r18 = box(short_int, r10) + r19 = CPyDict_Build(r16, r13, r17, r14, r18) + r20 = py_call_with_kwargs(r12, r15, r19) return xs [case testObjectAsBoolean] @@ -1628,7 +1634,7 @@ L0: r8 = box(tuple[int, int, int], r3) r9 = r7.extend(r8) :: list r10 = tuple r7 :: list - r11 = {} + r11 = PyDict_New() r12 = py_call_with_kwargs(r6, r10, r11) r13 = unbox(tuple[int, int, int], r12) return r13 @@ -1657,7 +1663,7 @@ L0: r9 = box(tuple[int, int], r3) r10 = r8.extend(r9) :: list r11 = tuple r8 :: list - r12 = {} + r12 = PyDict_New() r13 = py_call_with_kwargs(r6, r11, r12) r14 = unbox(tuple[int, int, int], r13) return r14 @@ -1684,15 +1690,16 @@ def g(): r3 :: short_int r4 :: str r5 :: short_int - r6, r7, r8 :: object - r9, r10 :: dict - r11 :: str - r12 :: object - r13 :: tuple - r14 :: dict - r15 :: bool - r16 :: object - r17 :: tuple[int, int, int] + r6 :: c_int + r7, r8, r9 :: object + r10, r11 :: dict + r12 :: str + r13 :: object + r14 :: tuple + r15 :: dict + r16 :: bool + r17 :: object + r18 :: tuple[int, int, int] L0: r0 = unicode_3 :: static ('a') r1 = 1 @@ -1700,53 +1707,56 @@ L0: r3 = 2 r4 = unicode_5 :: static ('c') r5 = 3 - r6 = box(short_int, r1) - r7 = box(short_int, r3) - r8 = box(short_int, r5) - r9 = {r0: r6, r2: r7, r4: r8} - r10 = __main__.globals :: static - r11 = unicode_6 :: static ('f') - r12 = r10[r11] :: dict - r13 = () :: tuple - r14 = {} - r15 = r14.update(r9) (display) :: dict - r16 = py_call_with_kwargs(r12, r13, r14) - r17 = unbox(tuple[int, int, int], r16) - return r17 + r6 = 3 + r7 = box(short_int, r1) + r8 = box(short_int, r3) + r9 = box(short_int, r5) + r10 = CPyDict_Build(r6, r0, r7, r2, r8, r4, r9) + r11 = __main__.globals :: static + r12 = unicode_6 :: static ('f') + r13 = r11[r12] :: dict + r14 = () :: tuple + r15 = PyDict_New() + r16 = r15.update(r10) (display) :: dict + r17 = py_call_with_kwargs(r13, r14, r15) + r18 = unbox(tuple[int, int, int], r17) + return r18 def h(): r0 :: short_int r1 :: str r2 :: short_int r3 :: str r4 :: short_int - r5, r6 :: object - r7, r8 :: dict - r9 :: str - r10, r11 :: object - r12 :: tuple - r13 :: dict - r14 :: bool - r15 :: object - r16 :: tuple[int, int, int] + r5 :: c_int + r6, r7 :: object + r8, r9 :: dict + r10 :: str + r11, r12 :: object + r13 :: tuple + r14 :: dict + r15 :: bool + r16 :: object + r17 :: tuple[int, int, int] L0: r0 = 1 r1 = unicode_4 :: static ('b') r2 = 2 r3 = unicode_5 :: static ('c') r4 = 3 - r5 = box(short_int, r2) - r6 = box(short_int, r4) - r7 = {r1: r5, r3: r6} - r8 = __main__.globals :: static - r9 = unicode_6 :: static ('f') - r10 = r8[r9] :: dict - r11 = box(short_int, r0) - r12 = (r11) :: tuple - r13 = {} - r14 = r13.update(r7) (display) :: dict - r15 = py_call_with_kwargs(r10, r12, r13) - r16 = unbox(tuple[int, int, int], r15) - return r16 + r5 = 2 + r6 = box(short_int, r2) + r7 = box(short_int, r4) + r8 = CPyDict_Build(r5, r1, r6, r3, r7) + r9 = __main__.globals :: static + r10 = unicode_6 :: static ('f') + r11 = r9[r10] :: dict + r12 = box(short_int, r0) + r13 = (r12) :: tuple + r14 = PyDict_New() + r15 = r14.update(r8) (display) :: dict + r16 = py_call_with_kwargs(r11, r13, r14) + r17 = unbox(tuple[int, int, int], r16) + return r17 [case testFunctionCallWithDefaultArgs] def f(x: int, y: int = 3, z: str = "test") -> None: @@ -1936,7 +1946,7 @@ def f(): r21 :: bool r22, r23 :: short_int L0: - r0 = {} + r0 = PyDict_New() r1 = 1 r2 = 2 r3 = 3 diff --git a/mypyc/test-data/irbuild-dict.test b/mypyc/test-data/irbuild-dict.test index 1dd0b315dc82..2381d42050e8 100644 --- a/mypyc/test-data/irbuild-dict.test +++ b/mypyc/test-data/irbuild-dict.test @@ -45,7 +45,7 @@ def f(): r0, d :: dict r1 :: None L0: - r0 = {} + r0 = PyDict_New() d = r0 r1 = None return r1 @@ -58,19 +58,21 @@ def f(x): x :: object r0, r1 :: short_int r2 :: str - r3, r4 :: object - r5, d :: dict - r6 :: None + r3 :: c_int + r4, r5 :: object + r6, d :: dict + r7 :: None L0: r0 = 1 r1 = 2 r2 = unicode_1 :: static - r3 = box(short_int, r0) - r4 = box(short_int, r1) - r5 = {r3: r4, r2: x} - d = r5 - r6 = None - return r6 + r3 = 2 + r4 = box(short_int, r0) + r5 = box(short_int, r1) + r6 = CPyDict_Build(r3, r4, r5, r2, x) + d = r6 + r7 = None + return r7 [case testInDict] from typing import Dict @@ -202,21 +204,23 @@ def f(x, y): r0 :: short_int r1 :: str r2 :: short_int - r3 :: object - r4 :: dict - r5 :: bool - r6 :: object - r7 :: bool + r3 :: c_int + r4 :: object + r5 :: dict + r6 :: bool + r7 :: object + r8 :: bool L0: r0 = 2 r1 = unicode_3 :: static ('z') r2 = 3 - r3 = box(short_int, r0) - r4 = {x: r3} - r5 = r4.update(y) (display) :: dict - r6 = box(short_int, r2) - r7 = r4.__setitem__(r1, r6) :: dict - return r4 + r3 = 1 + r4 = box(short_int, r0) + r5 = CPyDict_Build(r3, x, r4) + r6 = r5.update(y) (display) :: dict + r7 = box(short_int, r2) + r8 = r5.__setitem__(r1, r7) :: dict + return r5 [case testDictIterationMethods] from typing import Dict diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index 766858c0be1a..ff891f26ce72 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -635,29 +635,13 @@ L3: r9 = None return r9 -[case testDel] +[case testDelList] def delList() -> None: l = [1, 2] del l[1] -def delDict() -> None: - d = {"one":1, "two":2} - del d["one"] def delListMultiple() -> None: l = [1, 2, 3, 4, 5, 6, 7] del l[1], l[2], l[3] -def delDictMultiple() -> None: - d = {"one":1, "two":2, "three":3, "four":4} - del d["one"], d["four"] -class Dummy(): - def __init__(self, x: int, y: int) -> None: - self.x = x - self.y = y -def delAttribute() -> None: - dummy = Dummy(1, 2) - del dummy.x -def delAttributeMultiple() -> None: - dummy = Dummy(1, 2) - del dummy.x, dummy.y [out] def delList(): r0, r1 :: short_int @@ -679,29 +663,6 @@ L0: r7 = l.__delitem__(r6) :: object r8 = None return r8 -def delDict(): - r0 :: str - r1 :: short_int - r2 :: str - r3 :: short_int - r4, r5 :: object - r6, d :: dict - r7 :: str - r8 :: bool - r9 :: None -L0: - r0 = unicode_1 :: static ('one') - r1 = 1 - r2 = unicode_2 :: static ('two') - r3 = 2 - r4 = box(short_int, r1) - r5 = box(short_int, r3) - r6 = {r0: r4, r2: r5} - d = r6 - r7 = unicode_1 :: static ('one') - r8 = d.__delitem__(r7) :: object - r9 = None - return r9 def delListMultiple(): r0, r1, r2, r3, r4, r5, r6 :: short_int r7, r8, r9, r10, r11, r12, r13 :: object @@ -742,6 +703,40 @@ L0: r23 = l.__delitem__(r22) :: object r24 = None return r24 + +[case testDelDict] +def delDict() -> None: + d = {"one":1, "two":2} + del d["one"] +def delDictMultiple() -> None: + d = {"one":1, "two":2, "three":3, "four":4} + del d["one"], d["four"] +[out] +def delDict(): + r0 :: str + r1 :: short_int + r2 :: str + r3 :: short_int + r4 :: c_int + r5, r6 :: object + r7, d :: dict + r8 :: str + r9 :: bool + r10 :: None +L0: + r0 = unicode_1 :: static ('one') + r1 = 1 + r2 = unicode_2 :: static ('two') + r3 = 2 + r4 = 2 + r5 = box(short_int, r1) + r6 = box(short_int, r3) + r7 = CPyDict_Build(r4, r0, r5, r2, r6) + d = r7 + r8 = unicode_1 :: static ('one') + r9 = d.__delitem__(r8) :: object + r10 = None + return r10 def delDictMultiple(): r0 :: str r1 :: short_int @@ -751,11 +746,12 @@ def delDictMultiple(): r5 :: short_int r6 :: str r7 :: short_int - r8, r9, r10, r11 :: object - r12, d :: dict - r13, r14 :: str - r15, r16 :: bool - r17 :: None + r8 :: c_int + r9, r10, r11, r12 :: object + r13, d :: dict + r14, r15 :: str + r16, r17 :: bool + r18 :: None L0: r0 = unicode_1 :: static ('one') r1 = 1 @@ -765,18 +761,32 @@ L0: r5 = 3 r6 = unicode_4 :: static ('four') r7 = 4 - r8 = box(short_int, r1) - r9 = box(short_int, r3) - r10 = box(short_int, r5) - r11 = box(short_int, r7) - r12 = {r0: r8, r2: r9, r4: r10, r6: r11} - d = r12 - r13 = unicode_1 :: static ('one') - r14 = unicode_4 :: static ('four') - r15 = d.__delitem__(r13) :: object + r8 = 4 + r9 = box(short_int, r1) + r10 = box(short_int, r3) + r11 = box(short_int, r5) + r12 = box(short_int, r7) + r13 = CPyDict_Build(r8, r0, r9, r2, r10, r4, r11, r6, r12) + d = r13 + r14 = unicode_1 :: static ('one') + r15 = unicode_4 :: static ('four') r16 = d.__delitem__(r14) :: object - r17 = None - return r17 + r17 = d.__delitem__(r15) :: object + r18 = None + return r18 + +[case testDelAttribute] +class Dummy(): + def __init__(self, x: int, y: int) -> None: + self.x = x + self.y = y +def delAttribute() -> None: + dummy = Dummy(1, 2) + del dummy.x +def delAttributeMultiple() -> None: + dummy = Dummy(1, 2) + del dummy.x, dummy.y +[out] def Dummy.__init__(self, x, y): self :: __main__.Dummy x, y :: int @@ -798,7 +808,7 @@ L0: r1 = 2 r2 = Dummy(r0, r1) dummy = r2 - r3 = unicode_7 :: static ('x') + r3 = unicode_3 :: static ('x') r4 = delattr dummy, r3 r5 = None return r5 @@ -815,9 +825,9 @@ L0: r1 = 2 r2 = Dummy(r0, r1) dummy = r2 - r3 = unicode_7 :: static ('x') + r3 = unicode_3 :: static ('x') r4 = delattr dummy, r3 - r5 = unicode_8 :: static ('y') + r5 = unicode_4 :: static ('y') r6 = delattr dummy, r5 r7 = None return r7 diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index 017811a1b0b4..e326f6f7c7cf 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -740,24 +740,26 @@ def g(x): r1 :: object r2 :: str r3 :: tuple - r4 :: object - r5 :: dict - r6 :: object - r7 :: int + r4 :: c_int + r5 :: object + r6 :: dict + r7 :: object + r8 :: int L0: r0 = 2 r1 = int r2 = unicode_1 :: static ('base') r3 = (x) :: tuple - r4 = box(short_int, r0) - r5 = {r2: r4} - dec_ref r4 - r6 = py_call_with_kwargs(r1, r3, r5) - dec_ref r3 + r4 = 1 + r5 = box(short_int, r0) + r6 = CPyDict_Build(r4, r2, r5) dec_ref r5 - r7 = unbox(int, r6) + r7 = py_call_with_kwargs(r1, r3, r6) + dec_ref r3 dec_ref r6 - return r7 + r8 = unbox(int, r7) + dec_ref r7 + return r8 [case testListAppend] from typing import List diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index 43ca79cdaadb..469bc082e762 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -25,7 +25,7 @@ list_len_op, list_get_item_op, list_set_item_op, new_list_op, list_append_op ) from mypyc.primitives.dict_ops import ( - new_dict_op, dict_update_op, dict_get_item_op, dict_set_item_op + dict_new_op, dict_update_op, dict_get_item_op, dict_set_item_op ) from mypyc.primitives.int_ops import int_neg_op from mypyc.subtype import is_subtype @@ -224,7 +224,8 @@ def test_dict_update(self) -> None: """cpy_r_r0 = CPyDict_Update(cpy_r_d, cpy_r_o) >= 0;""") def test_new_dict(self) -> None: - self.assert_emit(PrimitiveOp([], new_dict_op, 1), + self.assert_emit(CallC(dict_new_op.c_function_name, [], dict_new_op.return_type, + dict_new_op.steals, dict_new_op.error_kind, 1), """cpy_r_r0 = PyDict_New();""") def test_dict_contains(self) -> None: From 068594eb38fc550d834b7e61745177c423f2b224 Mon Sep 17 00:00:00 2001 From: Shantanu Date: Tue, 9 Jun 2020 11:02:26 -0700 Subject: [PATCH 029/138] fastparse: improve error reporting for f-string syntax errors (#8970) --- mypy/fastparse.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mypy/fastparse.py b/mypy/fastparse.py index 2aac8412f657..2dafbf4e1454 100644 --- a/mypy/fastparse.py +++ b/mypy/fastparse.py @@ -169,6 +169,11 @@ def parse(source: Union[str, bytes], tree.path = fnam tree.is_stub = is_stub_file except SyntaxError as e: + if sys.version_info < (3, 9) and e.filename == "": + # In Python 3.8 and earlier, syntax errors in f-strings have lineno relative to the + # start of the f-string. This would be misleading, as mypy will report the error as the + # lineno within the file. + e.lineno = None errors.report(e.lineno if e.lineno is not None else -1, e.offset, e.msg, blocker=True, code=codes.SYNTAX) tree = MypyFile([], [], False, {}) From 84bcb252f85d3fa6f5a122df1b30f4877b4fa71c Mon Sep 17 00:00:00 2001 From: Antoine Lecubin Date: Sat, 13 Jun 2020 01:58:32 +0900 Subject: [PATCH 030/138] Update typeshed to fix TextIOWrapper constructor (#8965) Currently it doesn't accept ZipFile.open()'s output and fails with `error: Argument 1 to "TextIOWrapper" has incompatible type "IO[bytes]"; expected "BinaryIO"` That was fixed in https://github.com/python/typeshed/commit/3058bec873ef8b2b5630df36c4cbea0e3734dc5f --- mypy/modulefinder.py | 10 ++++++---- mypy/typeshed | 2 +- mypyc/analysis.py | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index 55d226cb647a..3ca1a8db1c30 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -448,7 +448,7 @@ def default_lib_path(data_dir: str, @functools.lru_cache(maxsize=None) -def get_site_packages_dirs(python_executable: Optional[str]) -> Tuple[List[str], List[str]]: +def get_site_packages_dirs(python_executable: str) -> Tuple[List[str], List[str]]: """Find package directories for given python. This runs a subprocess call, which generates a list of the egg directories, and the site @@ -461,8 +461,6 @@ def make_abspath(path: str, root: str) -> str: else: return os.path.join(root, os.path.normpath(path)) - if python_executable is None: - return [], [] if python_executable == sys.executable: # Use running Python's package dirs site_packages = sitepkgs.getsitepackages() @@ -543,7 +541,11 @@ def compute_search_paths(sources: List[BuildSource], if alt_lib_path: mypypath.insert(0, alt_lib_path) - egg_dirs, site_packages = get_site_packages_dirs(options.python_executable) + if options.python_executable is None: + egg_dirs = [] # type: List[str] + site_packages = [] # type: List[str] + else: + egg_dirs, site_packages = get_site_packages_dirs(options.python_executable) for site_dir in site_packages: assert site_dir not in lib_path if (site_dir in mypypath or diff --git a/mypy/typeshed b/mypy/typeshed index e199c2e4bc95..df6136c4ac0b 160000 --- a/mypy/typeshed +++ b/mypy/typeshed @@ -1 +1 @@ -Subproject commit e199c2e4bc95c4df25f0270cedbee934083f4ae8 +Subproject commit df6136c4ac0bd0751699a7eeeff36e7486a90254 diff --git a/mypyc/analysis.py b/mypyc/analysis.py index a766b0dee9b8..b71e120dceea 100644 --- a/mypyc/analysis.py +++ b/mypyc/analysis.py @@ -30,7 +30,7 @@ def __init__(self, def __str__(self) -> str: lines = [] - lines.append('exits: %s' % sorted(self.exits)) + lines.append('exits: %s' % sorted(self.exits, key=lambda e: e.label)) lines.append('succ: %s' % self.succ) lines.append('pred: %s' % self.pred) return '\n'.join(lines) From 8577ea4c0cfbc9b3ccfcc74b670bd40089a783a8 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Mon, 15 Jun 2020 19:31:46 +0800 Subject: [PATCH 031/138] [mypyc] Add LoadGlobal IR (#8973) In this PR we introduce a LoadGlobal IR that loads a name in global space. It's part of mypyc/mypyc#736 and mypyc/mypyc#737, it replaces literal LoadStatic with LoadGlobal and moves the name generation logic to caller in irbuild. It will eventually replace LoadStatic. LoadGlobal simply reads a global value via a given name (its counterpart for reading an address will be a future LoadAddress). This PR also introduces a registry and a description for making LoadGlobal work with more customized loading ops (some name_ref_ops like the True and False). --- mypyc/analysis.py | 5 ++++- mypyc/codegen/emitfunc.py | 11 ++++++++++- mypyc/ir/ops.py | 34 ++++++++++++++++++++++++++++++++++ mypyc/irbuild/ll_builder.py | 25 +++++++++++++------------ mypyc/primitives/registry.py | 22 ++++++++++++++++++++++ 5 files changed, 83 insertions(+), 14 deletions(-) diff --git a/mypyc/analysis.py b/mypyc/analysis.py index b71e120dceea..025d5ad77c97 100644 --- a/mypyc/analysis.py +++ b/mypyc/analysis.py @@ -8,7 +8,7 @@ Value, ControlOp, BasicBlock, OpVisitor, Assign, LoadInt, LoadErrorValue, RegisterOp, Goto, Branch, Return, Call, Environment, Box, Unbox, Cast, Op, Unreachable, TupleGet, TupleSet, GetAttr, SetAttr, - LoadStatic, InitStatic, PrimitiveOp, MethodCall, RaiseStandardError, CallC + LoadStatic, InitStatic, PrimitiveOp, MethodCall, RaiseStandardError, CallC, LoadGlobal ) @@ -198,6 +198,9 @@ def visit_raise_standard_error(self, op: RaiseStandardError) -> GenAndKill: def visit_call_c(self, op: CallC) -> GenAndKill: return self.visit_register_op(op) + def visit_load_global(self, op: LoadGlobal) -> GenAndKill: + return self.visit_register_op(op) + class DefinedVisitor(BaseAnalysisVisitor): """Visitor for finding defined registers. diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index 063d36cceb93..41c025a21234 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -11,7 +11,7 @@ OpVisitor, Goto, Branch, Return, Assign, LoadInt, LoadErrorValue, GetAttr, SetAttr, LoadStatic, InitStatic, TupleGet, TupleSet, Call, IncRef, DecRef, Box, Cast, Unbox, BasicBlock, Value, MethodCall, PrimitiveOp, EmitterInterface, Unreachable, NAMESPACE_STATIC, - NAMESPACE_TYPE, NAMESPACE_MODULE, RaiseStandardError, CallC + NAMESPACE_TYPE, NAMESPACE_MODULE, RaiseStandardError, CallC, LoadGlobal ) from mypyc.ir.rtypes import RType, RTuple, is_c_int_rprimitive from mypyc.ir.func_ir import FuncIR, FuncDecl, FUNC_STATICMETHOD, FUNC_CLASSMETHOD @@ -423,6 +423,15 @@ def visit_call_c(self, op: CallC) -> None: args = ', '.join(self.reg(arg) for arg in op.args) self.emitter.emit_line("{}{}({});".format(dest, op.function_name, args)) + def visit_load_global(self, op: LoadGlobal) -> None: + dest = self.reg(op) + ann = '' + if op.ann: + s = repr(op.ann) + if not any(x in s for x in ('/*', '*/', '\0')): + ann = ' /* %s */' % s + self.emit_line('%s = %s;%s' % (dest, op.identifier, ann)) + # Helpers def label(self, label: BasicBlock) -> str: diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 75d67d603940..9027c79b51e3 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -1179,6 +1179,36 @@ def accept(self, visitor: 'OpVisitor[T]') -> T: return visitor.visit_call_c(self) +class LoadGlobal(RegisterOp): + """Load a global variable/pointer""" + + error_kind = ERR_NEVER + is_borrowed = True + + def __init__(self, + type: RType, + identifier: str, + line: int = -1, + ann: object = None) -> None: + super().__init__(line) + self.identifier = identifier + self.type = type + self.ann = ann # An object to pretty print with the load + + def sources(self) -> List[Value]: + return [] + + def to_str(self, env: Environment) -> str: + ann = ' ({})'.format(repr(self.ann)) if self.ann else '' + # return env.format('%r = %s%s', self, self.identifier, ann) + # TODO: a hack to prevent lots of failed IR tests when developing prototype + # eventually we will change all the related tests + return env.format('%r = %s :: static%s ', self, self.identifier[10:], ann) + + def accept(self, visitor: 'OpVisitor[T]') -> T: + return visitor.visit_load_global(self) + + @trait class OpVisitor(Generic[T]): """Generic visitor over ops (uses the visitor design pattern).""" @@ -1273,6 +1303,10 @@ def visit_raise_standard_error(self, op: RaiseStandardError) -> T: def visit_call_c(self, op: CallC) -> T: raise NotImplementedError + @abstractmethod + def visit_load_global(self, op: LoadGlobal) -> T: + raise NotImplementedError + # TODO: Should this live somewhere else? LiteralsMap = Dict[Tuple[Type[object], Union[int, float, str, bytes, complex]], str] diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 7a43d51a7230..e8935381eccb 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -20,7 +20,7 @@ BasicBlock, Environment, Op, LoadInt, Value, Register, Assign, Branch, Goto, Call, Box, Unbox, Cast, GetAttr, LoadStatic, MethodCall, PrimitiveOp, OpDescription, RegisterOp, CallC, - RaiseStandardError, Unreachable, LoadErrorValue, + RaiseStandardError, Unreachable, LoadErrorValue, LoadGlobal, NAMESPACE_TYPE, NAMESPACE_MODULE, NAMESPACE_STATIC, ) from mypyc.ir.rtypes import ( @@ -32,6 +32,7 @@ from mypyc.ir.class_ir import ClassIR, all_concrete_classes from mypyc.common import ( FAST_ISINSTANCE_MAX_SUBCLASSES, MAX_LITERAL_SHORT_INT, + STATIC_PREFIX ) from mypyc.primitives.registry import ( binary_ops, unary_ops, method_ops, func_ops, @@ -427,30 +428,30 @@ def none_object(self) -> Value: return self.add(PrimitiveOp([], none_object_op, line=-1)) def literal_static_name(self, value: Union[int, float, complex, str, bytes]) -> str: - return self.mapper.literal_static_name(self.current_module, value) + return STATIC_PREFIX + self.mapper.literal_static_name(self.current_module, value) def load_static_int(self, value: int) -> Value: """Loads a static integer Python 'int' object into a register.""" if abs(value) > MAX_LITERAL_SHORT_INT: - static_symbol = self.literal_static_name(value) - return self.add(LoadStatic(int_rprimitive, static_symbol, ann=value)) + identifier = self.literal_static_name(value) + return self.add(LoadGlobal(int_rprimitive, identifier, ann=value)) else: return self.add(LoadInt(value)) def load_static_float(self, value: float) -> Value: """Loads a static float value into a register.""" - static_symbol = self.literal_static_name(value) - return self.add(LoadStatic(float_rprimitive, static_symbol, ann=value)) + identifier = self.literal_static_name(value) + return self.add(LoadGlobal(float_rprimitive, identifier, ann=value)) def load_static_bytes(self, value: bytes) -> Value: """Loads a static bytes value into a register.""" - static_symbol = self.literal_static_name(value) - return self.add(LoadStatic(object_rprimitive, static_symbol, ann=value)) + identifier = self.literal_static_name(value) + return self.add(LoadGlobal(object_rprimitive, identifier, ann=value)) def load_static_complex(self, value: complex) -> Value: """Loads a static complex value into a register.""" - static_symbol = self.literal_static_name(value) - return self.add(LoadStatic(object_rprimitive, static_symbol, ann=value)) + identifier = self.literal_static_name(value) + return self.add(LoadGlobal(object_rprimitive, identifier, ann=value)) def load_static_unicode(self, value: str) -> Value: """Loads a static unicode value into a register. @@ -458,8 +459,8 @@ def load_static_unicode(self, value: str) -> Value: This is useful for more than just unicode literals; for example, method calls also require a PyObject * form for the name of the method. """ - static_symbol = self.literal_static_name(value) - return self.add(LoadStatic(str_rprimitive, static_symbol, ann=value)) + identifier = self.literal_static_name(value) + return self.add(LoadGlobal(str_rprimitive, identifier, ann=value)) def load_static_checked(self, typ: RType, identifier: str, module_name: Optional[str] = None, namespace: str = NAMESPACE_STATIC, diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index 4ef2e4eb1faa..388b9717eef8 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -52,6 +52,14 @@ ('steals', StealsDescription), ('priority', int)]) +# A description for C load operations including LoadGlobal and LoadAddress +CLoadDescription = NamedTuple( + 'CLoadDescription', [('name', str), + ('return_type', RType), + ('identifier', str), # name of the target to load + ('cast_str', str), # string represents optional type cast + ('load_address', bool)]) # True for LoadAddress otherwise LoadGlobal + # Primitive binary ops (key is operator such as '+') binary_ops = {} # type: Dict[str, List[OpDescription]] @@ -79,6 +87,9 @@ # CallC op for unary ops c_unary_ops = {} # type: Dict[str, List[CFunctionDescription]] +# LoadGlobal/LoadAddress op for reading global names +c_name_ref_ops = {} # type: Dict[str, CLoadDescription] + def simple_emit(template: str) -> EmitCallback: """Construct a simple PrimitiveOp emit callback function. @@ -444,6 +455,17 @@ def c_unary_op(name: str, return desc +def c_name_ref_op(name: str, + return_type: RType, + identifier: str, + cast_str: Optional[str] = None, + load_address: bool = False) -> CLoadDescription: + assert name not in c_name_ref_ops, 'already defined: %s' % name + cast_str = cast_str if cast_str else "" + desc = CLoadDescription(name, return_type, identifier, cast_str, load_address) + c_name_ref_ops[name] = desc + return desc + # Import various modules that set up global state. import mypyc.primitives.int_ops # noqa import mypyc.primitives.str_ops # noqa From a810e0c7bed408af343035a38775f70c467f4b9b Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Wed, 17 Jun 2020 19:20:38 +0800 Subject: [PATCH 032/138] [mypyc] translate call_emit primitive ops to CallC (#9012) Related issue: mypyc/mypyc#734 This PR translates PrimitiveOps with call_emit, which can be represented by CallC pretty straightforwardly, to CallC. Mostly translating the description. After this PR and another following PR which handles the ones whose descriptions are explicitly used in irbuild, we'll able to see what's missing for CallC by analyzing the remaining primitives. --- mypyc/primitives/dict_ops.py | 38 +++++++++---------- mypyc/primitives/int_ops.py | 57 ++++++++++++++-------------- mypyc/primitives/list_ops.py | 21 +++++----- mypyc/primitives/misc_ops.py | 13 ++++--- mypyc/primitives/set_ops.py | 40 +++++++++---------- mypyc/primitives/str_ops.py | 34 +++++++++-------- mypyc/test-data/analysis.test | 2 +- mypyc/test-data/irbuild-basic.test | 8 ++-- mypyc/test-data/irbuild-classes.test | 4 +- mypyc/test-data/irbuild-nested.test | 4 +- mypyc/test-data/irbuild-set.test | 6 +-- mypyc/test-data/irbuild-try.test | 2 +- 12 files changed, 113 insertions(+), 116 deletions(-) diff --git a/mypyc/primitives/dict_ops.py b/mypyc/primitives/dict_ops.py index 39eb555bfd43..e3c270194bb4 100644 --- a/mypyc/primitives/dict_ops.py +++ b/mypyc/primitives/dict_ops.py @@ -11,7 +11,7 @@ from mypyc.primitives.registry import ( name_ref_op, method_op, binary_op, func_op, custom_op, simple_emit, negative_int_emit, call_emit, call_negative_bool_emit, - name_emit, c_custom_op + name_emit, c_custom_op, c_method_op ) @@ -73,12 +73,12 @@ emit=call_negative_bool_emit('CPyDict_UpdateFromAny')) # dict.get(key, default) -method_op( +c_method_op( name='get', arg_types=[dict_rprimitive, object_rprimitive, object_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyDict_Get')) + return_type=object_rprimitive, + c_function_name='CPyDict_Get', + error_kind=ERR_MAGIC) # dict.get(key) method_op( @@ -115,6 +115,7 @@ priority=2) # Generic one-argument dict constructor: dict(obj) + func_op( name='builtins.dict', arg_types=[object_rprimitive], @@ -123,31 +124,28 @@ emit=call_emit('CPyDict_FromAny')) # dict.keys() -method_op( +c_method_op( name='keys', arg_types=[dict_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyDict_KeysView') -) + return_type=object_rprimitive, + c_function_name='CPyDict_KeysView', + error_kind=ERR_MAGIC) # dict.values() -method_op( +c_method_op( name='values', arg_types=[dict_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyDict_ValuesView') -) + return_type=object_rprimitive, + c_function_name='CPyDict_ValuesView', + error_kind=ERR_MAGIC) # dict.items() -method_op( +c_method_op( name='items', arg_types=[dict_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyDict_ItemsView') -) + return_type=object_rprimitive, + c_function_name='CPyDict_ItemsView', + error_kind=ERR_MAGIC) # list(dict.keys()) dict_keys_op = custom_op( diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index 7e1e70100d4c..490f074a1d25 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -12,8 +12,8 @@ str_rprimitive, RType ) from mypyc.primitives.registry import ( - name_ref_op, binary_op, func_op, custom_op, - simple_emit, call_emit, name_emit, c_unary_op, CFunctionDescription + name_ref_op, binary_op, custom_op, simple_emit, call_emit, name_emit, + c_unary_op, CFunctionDescription, c_function_op ) # These int constructors produce object_rprimitives that then need to be unboxed @@ -28,47 +28,46 @@ is_borrowed=True) # Convert from a float to int. We could do a bit better directly. -func_op( +c_function_op( name='builtins.int', arg_types=[float_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyLong_FromFloat'), - priority=1) + return_type=object_rprimitive, + c_function_name='CPyLong_FromFloat', + error_kind=ERR_MAGIC) # int(string) -func_op( +c_function_op( name='builtins.int', arg_types=[str_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyLong_FromStr'), - priority=1) + return_type=object_rprimitive, + c_function_name='CPyLong_FromStr', + error_kind=ERR_MAGIC) # int(string, base) -func_op( +c_function_op( name='builtins.int', arg_types=[str_rprimitive, int_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyLong_FromStrWithBase'), - priority=1) + return_type=object_rprimitive, + c_function_name='CPyLong_FromStrWithBase', + error_kind=ERR_MAGIC) # str(n) on ints -func_op(name='builtins.str', - arg_types=[int_rprimitive], - result_type=str_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyTagged_Str'), - priority=2) +c_function_op( + name='builtins.str', + arg_types=[int_rprimitive], + return_type=str_rprimitive, + c_function_name='CPyTagged_Str', + error_kind=ERR_MAGIC, + priority=2) # We need a specialization for str on bools also since the int one is wrong... -func_op(name='builtins.str', - arg_types=[bool_rprimitive], - result_type=str_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyBool_Str'), - priority=3) +c_function_op( + name='builtins.str', + arg_types=[bool_rprimitive], + return_type=str_rprimitive, + c_function_name='CPyBool_Str', + error_kind=ERR_MAGIC, + priority=3) def int_binary_op(op: str, c_func_name: str, diff --git a/mypyc/primitives/list_ops.py b/mypyc/primitives/list_ops.py index 76254dd35292..3da8048a6d78 100644 --- a/mypyc/primitives/list_ops.py +++ b/mypyc/primitives/list_ops.py @@ -8,7 +8,7 @@ ) from mypyc.primitives.registry import ( name_ref_op, func_op, method_op, custom_op, name_emit, - call_emit, call_negative_bool_emit, c_function_op, c_binary_op + call_emit, call_negative_bool_emit, c_function_op, c_binary_op, c_method_op ) @@ -117,19 +117,20 @@ def emit_new(emitter: EmitterInterface, args: List[str], dest: str) -> None: emit=call_emit('CPyList_Pop')) # list.count(obj) -method_op( +c_method_op( name='count', arg_types=[list_rprimitive, object_rprimitive], - result_type=short_int_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyList_Count')) + return_type=short_int_rprimitive, + c_function_name='CPyList_Count', + error_kind=ERR_MAGIC) # list * int -c_binary_op(name='*', - arg_types=[list_rprimitive, int_rprimitive], - return_type=list_rprimitive, - c_function_name='CPySequence_Multiply', - error_kind=ERR_MAGIC) +c_binary_op( + name='*', + arg_types=[list_rprimitive, int_rprimitive], + return_type=list_rprimitive, + c_function_name='CPySequence_Multiply', + error_kind=ERR_MAGIC) # int * list c_binary_op(name='*', diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py index 743ec28dd533..45e06d485812 100644 --- a/mypyc/primitives/misc_ops.py +++ b/mypyc/primitives/misc_ops.py @@ -7,7 +7,7 @@ ) from mypyc.primitives.registry import ( name_ref_op, simple_emit, unary_op, func_op, custom_op, call_emit, name_emit, - call_negative_magic_emit + call_negative_magic_emit, c_function_op ) @@ -53,11 +53,12 @@ is_borrowed=True) # id(obj) -func_op(name='builtins.id', - arg_types=[object_rprimitive], - result_type=int_rprimitive, - error_kind=ERR_NEVER, - emit=call_emit('CPyTagged_Id')) +c_function_op( + name='builtins.id', + arg_types=[object_rprimitive], + return_type=int_rprimitive, + c_function_name='CPyTagged_Id', + error_kind=ERR_NEVER) # Return the result of obj.__await()__ or obj.__iter__() (if no __await__ exists) coro_op = custom_op(name='get_coroutine_obj', diff --git a/mypyc/primitives/set_ops.py b/mypyc/primitives/set_ops.py index 2ffd6d9e3f3a..6795a19651a9 100644 --- a/mypyc/primitives/set_ops.py +++ b/mypyc/primitives/set_ops.py @@ -1,8 +1,8 @@ """Primitive set (and frozenset) ops.""" from mypyc.primitives.registry import ( - func_op, method_op, binary_op, - simple_emit, negative_int_emit, call_emit, call_negative_bool_emit, + func_op, method_op, binary_op, simple_emit, negative_int_emit, + call_negative_bool_emit, c_function_op, c_method_op ) from mypyc.ir.ops import ERR_MAGIC, ERR_FALSE, ERR_NEVER, EmitterInterface from mypyc.ir.rtypes import object_rprimitive, bool_rprimitive, set_rprimitive, int_rprimitive @@ -19,22 +19,20 @@ ) # set(obj) -func_op( +c_function_op( name='builtins.set', arg_types=[object_rprimitive], - result_type=set_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('PySet_New') -) + return_type=set_rprimitive, + c_function_name='PySet_New', + error_kind=ERR_MAGIC) # frozenset(obj) -func_op( +c_function_op( name='builtins.frozenset', arg_types=[object_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('PyFrozenSet_New') -) + return_type=object_rprimitive, + c_function_name='PyFrozenSet_New', + error_kind=ERR_MAGIC) def emit_len(emitter: EmitterInterface, args: List[str], dest: str) -> None: @@ -64,13 +62,12 @@ def emit_len(emitter: EmitterInterface, args: List[str], dest: str) -> None: ) # set.remove(obj) -method_op( +c_method_op( name='remove', arg_types=[set_rprimitive, object_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_FALSE, - emit=call_emit('CPySet_Remove') -) + return_type=bool_rprimitive, + c_function_name='CPySet_Remove', + error_kind=ERR_FALSE) # set.discard(obj) method_op( @@ -111,10 +108,9 @@ def emit_len(emitter: EmitterInterface, args: List[str], dest: str) -> None: ) # set.pop() -method_op( +c_method_op( name='pop', arg_types=[set_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('PySet_Pop') -) + return_type=object_rprimitive, + c_function_name='PySet_Pop', + error_kind=ERR_MAGIC) diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py index 2e261131257b..69a972bf0715 100644 --- a/mypyc/primitives/str_ops.py +++ b/mypyc/primitives/str_ops.py @@ -7,8 +7,8 @@ RType, object_rprimitive, str_rprimitive, bool_rprimitive, int_rprimitive, list_rprimitive ) from mypyc.primitives.registry import ( - func_op, binary_op, simple_emit, name_ref_op, method_op, call_emit, name_emit, - c_method_op, c_binary_op + binary_op, simple_emit, name_ref_op, method_op, call_emit, name_emit, + c_method_op, c_binary_op, c_function_op ) @@ -20,18 +20,19 @@ is_borrowed=True) # str(obj) -func_op(name='builtins.str', - arg_types=[object_rprimitive], - result_type=str_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('PyObject_Str')) +c_function_op( + name='builtins.str', + arg_types=[object_rprimitive], + return_type=str_rprimitive, + c_function_name='PyObject_Str', + error_kind=ERR_MAGIC) # str1 + str2 -binary_op(op='+', - arg_types=[str_rprimitive, str_rprimitive], - result_type=str_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('PyUnicode_Concat')) +c_binary_op(name='+', + arg_types=[str_rprimitive, str_rprimitive], + return_type=str_rprimitive, + c_function_name='PyUnicode_Concat', + error_kind=ERR_MAGIC) # str.join(obj) c_method_op( @@ -43,12 +44,13 @@ ) # str[index] (for an int index) -method_op( +c_method_op( name='__getitem__', arg_types=[str_rprimitive, int_rprimitive], - result_type=str_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyStr_GetItem')) + return_type=str_rprimitive, + c_function_name='CPyStr_GetItem', + error_kind=ERR_MAGIC +) # str.split(...) str_split_types = [str_rprimitive, str_rprimitive, int_rprimitive] # type: List[RType] diff --git a/mypyc/test-data/analysis.test b/mypyc/test-data/analysis.test index 22e7648f121d..0155c22feb93 100644 --- a/mypyc/test-data/analysis.test +++ b/mypyc/test-data/analysis.test @@ -525,7 +525,7 @@ def lol(x): r11, r12 :: int L0: L1: - r0 = id x :: object + r0 = CPyTagged_Id(x) st = r0 goto L10 L2: diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 292f13e242b3..2e9408cac0a2 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -166,14 +166,14 @@ def f(x, y): r2 :: bool r3 :: str L0: - r1 = str x :: object + r1 = PyObject_Str(x) r2 = bool r1 :: object if r2 goto L1 else goto L2 :: bool L1: r0 = r1 goto L3 L2: - r3 = str y :: object + r3 = PyObject_Str(y) r0 = r3 L3: return r0 @@ -216,14 +216,14 @@ def f(x, y): r2 :: bool r3 :: str L0: - r1 = str x :: object + r1 = PyObject_Str(x) r2 = bool r1 :: object if r2 goto L2 else goto L1 :: bool L1: r0 = r1 goto L3 L2: - r3 = str y :: object + r3 = PyObject_Str(y) r0 = r3 L3: return r0 diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index 5a42307e7ba0..74973b43df7f 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -243,7 +243,7 @@ def A.foo(self, x): x :: int r0 :: str L0: - r0 = str x :: int + r0 = CPyTagged_Str(x) return r0 def B.foo(self, x): self :: __main__.B @@ -263,7 +263,7 @@ def C.foo(self, x): x :: object r0 :: int L0: - r0 = id x :: object + r0 = CPyTagged_Id(x) return r0 def C.foo__B_glue(self, x): self :: __main__.C diff --git a/mypyc/test-data/irbuild-nested.test b/mypyc/test-data/irbuild-nested.test index e8301778d42f..a8dc398f0b93 100644 --- a/mypyc/test-data/irbuild-nested.test +++ b/mypyc/test-data/irbuild-nested.test @@ -167,7 +167,7 @@ L0: r1 = r0.inner inner = r1 r2 = unicode_4 :: static ('!') - r3 = s + r2 + r3 = PyUnicode_Concat(s, r2) return r3 def c(num): num :: float @@ -206,7 +206,7 @@ L0: r1 = r0.inner inner = r1 r2 = unicode_5 :: static ('?') - r3 = s + r2 + r3 = PyUnicode_Concat(s, r2) return r3 def d(num): num :: float diff --git a/mypyc/test-data/irbuild-set.test b/mypyc/test-data/irbuild-set.test index b0c3c70ff59a..f109627b0dd5 100644 --- a/mypyc/test-data/irbuild-set.test +++ b/mypyc/test-data/irbuild-set.test @@ -45,7 +45,7 @@ def f(l): l :: list r0 :: set L0: - r0 = set l :: object + r0 = PySet_New(l) return r0 [case testSetSize] @@ -126,7 +126,7 @@ L0: x = r0 r1 = 1 r2 = box(short_int, r1) - r3 = x.remove(r2) :: set + r3 = CPySet_Remove(x, r2) r4 = None return x @@ -202,7 +202,7 @@ def f(s): r0 :: object r1 :: int L0: - r0 = s.pop() :: set + r0 = PySet_Pop(s) r1 = unbox(int, r0) return r1 diff --git a/mypyc/test-data/irbuild-try.test b/mypyc/test-data/irbuild-try.test index 77af852ad957..5df1420ce349 100644 --- a/mypyc/test-data/irbuild-try.test +++ b/mypyc/test-data/irbuild-try.test @@ -75,7 +75,7 @@ L2: goto L4 L3: r4 = unicode_2 :: static ('hi') - r5 = str r4 :: object + r5 = PyObject_Str(r4) L4: goto L8 L5: (handler for L1, L2, L3, L4) From d3edd60ce14ec9e113835e6889db46fb8a7e085a Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Wed, 17 Jun 2020 19:41:50 +0300 Subject: [PATCH 033/138] Fix exception causes all over the codebase (#8998) In some parts of the code, an exception is being caught and replaced with a more user-friendly error. In these cases the syntax `raise new_error from old_error` needs to be used. Python 3's exception chaining means it shows not only the traceback of the current exception, but that of the original exception (and possibly more.) This is regardless of `raise from`. The usage of `raise from` tells Python to put a more accurate message between the tracebacks. Instead of this: ``` During handling of the above exception, another exception occurred: ``` You'll get this: ``` The above exception was the direct cause of the following exception: ``` The first is inaccurate, because it signifies a bug in the exception-handling code itself, which is a separate situation than wrapping an exception. --- mypy/build.py | 4 ++-- mypy/dmypy/client.py | 4 ++-- mypy/dmypy_util.py | 4 ++-- mypy/ipc.py | 14 +++++++------- mypy/main.py | 5 +++-- mypy/moduleinspect.py | 2 +- mypy/stubgen.py | 4 ++-- mypy/stubtest.py | 2 +- mypy/stubutil.py | 4 ++-- mypy/suggestions.py | 4 ++-- mypy/util.py | 2 +- mypyc/test/test_run.py | 2 +- 12 files changed, 26 insertions(+), 25 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index e7f3a047dbe7..f372f3d087a1 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -1988,13 +1988,13 @@ def parse_file(self) -> None: raise CompileError([ "mypy: can't read file '{}': {}".format( self.path, os.strerror(ioerr.errno))], - module_with_blocker=self.id) + module_with_blocker=self.id) from ioerr except (UnicodeDecodeError, DecodeError) as decodeerr: if self.path.endswith('.pyd'): err = "mypy: stubgen does not support .pyd files: '{}'".format(self.path) else: err = "mypy: can't decode file '{}': {}".format(self.path, str(decodeerr)) - raise CompileError([err], module_with_blocker=self.id) + raise CompileError([err], module_with_blocker=self.id) from decodeerr else: assert source is not None self.source_hash = compute_hash(source) diff --git a/mypy/dmypy/client.py b/mypy/dmypy/client.py index 80f03743086c..b02ac23a0be9 100644 --- a/mypy/dmypy/client.py +++ b/mypy/dmypy/client.py @@ -534,8 +534,8 @@ def read_status(status_file: str) -> Dict[str, object]: with open(status_file) as f: try: data = json.load(f) - except Exception: - raise BadStatus("Malformed status file (not JSON)") + except Exception as e: + raise BadStatus("Malformed status file (not JSON)") from e if not isinstance(data, dict): raise BadStatus("Invalid status file (not a dict)") return data diff --git a/mypy/dmypy_util.py b/mypy/dmypy_util.py index 9918e3c3b26f..f598742d2474 100644 --- a/mypy/dmypy_util.py +++ b/mypy/dmypy_util.py @@ -24,8 +24,8 @@ def receive(connection: IPCBase) -> Any: raise OSError("No data received") try: data = json.loads(bdata.decode('utf8')) - except Exception: - raise OSError("Data received is not valid JSON") + except Exception as e: + raise OSError("Data received is not valid JSON") from e if not isinstance(data, dict): raise OSError("Data received is not a dict (%s)" % str(type(data))) return data diff --git a/mypy/ipc.py b/mypy/ipc.py index 02c70eb82829..83d3ca787329 100644 --- a/mypy/ipc.py +++ b/mypy/ipc.py @@ -109,7 +109,7 @@ def write(self, data: bytes) -> None: assert err == 0, err assert bytes_written == len(data) except WindowsError as e: - raise IPCException("Failed to write with error: {}".format(e.winerror)) + raise IPCException("Failed to write with error: {}".format(e.winerror)) from e else: self.connection.sendall(data) self.connection.shutdown(socket.SHUT_WR) @@ -131,11 +131,11 @@ def __init__(self, name: str, timeout: Optional[float]) -> None: timeout = int(self.timeout * 1000) if self.timeout else _winapi.NMPWAIT_WAIT_FOREVER try: _winapi.WaitNamedPipe(self.name, timeout) - except FileNotFoundError: - raise IPCException("The NamedPipe at {} was not found.".format(self.name)) + except FileNotFoundError as e: + raise IPCException("The NamedPipe at {} was not found.".format(self.name)) from e except WindowsError as e: if e.winerror == _winapi.ERROR_SEM_TIMEOUT: - raise IPCException("Timed out waiting for connection.") + raise IPCException("Timed out waiting for connection.") from e else: raise try: @@ -150,7 +150,7 @@ def __init__(self, name: str, timeout: Optional[float]) -> None: ) except WindowsError as e: if e.winerror == _winapi.ERROR_PIPE_BUSY: - raise IPCException("The connection is busy.") + raise IPCException("The connection is busy.") from e else: raise _winapi.SetNamedPipeHandleState(self.connection, @@ -237,8 +237,8 @@ def __enter__(self) -> 'IPCServer': else: try: self.connection, _ = self.sock.accept() - except socket.timeout: - raise IPCException('The socket timed out') + except socket.timeout as e: + raise IPCException('The socket timed out') from e return self def __exit__(self, diff --git a/mypy/main.py b/mypy/main.py index a1a6fc17166d..8e80364234b4 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -192,10 +192,11 @@ def _python_executable_from_version(python_version: Tuple[int, int]) -> str: ['-c', 'import sys; print(sys.executable)'], stderr=subprocess.STDOUT).decode().strip() return sys_exe - except (subprocess.CalledProcessError, FileNotFoundError): + except (subprocess.CalledProcessError, FileNotFoundError) as e: raise PythonExecutableInferenceError( 'failed to find a Python executable matching version {},' - ' perhaps try --python-executable, or --no-site-packages?'.format(python_version)) + ' perhaps try --python-executable, or --no-site-packages?'.format(python_version) + ) from e def infer_python_executable(options: Options, diff --git a/mypy/moduleinspect.py b/mypy/moduleinspect.py index a4c7bcc13438..d54746260123 100644 --- a/mypy/moduleinspect.py +++ b/mypy/moduleinspect.py @@ -44,7 +44,7 @@ def get_package_properties(package_id: str) -> ModuleProperties: try: package = importlib.import_module(package_id) except BaseException as e: - raise InspectError(str(e)) + raise InspectError(str(e)) from e name = getattr(package, '__name__', None) file = getattr(package, '__file__', None) path = getattr(package, '__path__', None) # type: Optional[List[str]] diff --git a/mypy/stubgen.py b/mypy/stubgen.py index 75fa94e8e630..972863416668 100755 --- a/mypy/stubgen.py +++ b/mypy/stubgen.py @@ -1191,7 +1191,7 @@ def collect_build_targets(options: Options, mypy_opts: MypyOptions) -> Tuple[Lis try: source_list = create_source_list(options.files, mypy_opts) except InvalidSourceList as e: - raise SystemExit(str(e)) + raise SystemExit(str(e)) from e py_modules = [StubSource(m.module, m.path) for m in source_list] c_modules = [] @@ -1362,7 +1362,7 @@ def generate_asts_for_modules(py_modules: List[StubSource], try: res = build(list(py_modules), mypy_options) except CompileError as e: - raise SystemExit("Critical error during semantic analysis: {}".format(e)) + raise SystemExit("Critical error during semantic analysis: {}".format(e)) from e for mod in py_modules: mod.ast = res.graph[mod.module].tree diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 3dc0468868bd..535f049d9b2e 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -957,7 +957,7 @@ def build_stubs(modules: List[str], options: Options, find_submodules: bool = Fa except mypy.errors.CompileError as e: output = [_style("error: ", color="red", bold=True), "failed mypy compile.\n", str(e)] print("".join(output)) - raise RuntimeError + raise RuntimeError from e if res.errors: output = [_style("error: ", color="red", bold=True), "failed mypy build.\n"] print("".join(output) + "\n".join(res.errors)) diff --git a/mypy/stubutil.py b/mypy/stubutil.py index 51f9ef6e39ff..d21ba4059913 100644 --- a/mypy/stubutil.py +++ b/mypy/stubutil.py @@ -91,7 +91,7 @@ def find_module_path_and_all_py2(module: str, except subprocess.CalledProcessError as e: path = find_module_path_using_py2_sys_path(module, interpreter) if path is None: - raise CantImport(module, str(e)) + raise CantImport(module, str(e)) from e return path, None output = output_bytes.decode('ascii').strip().splitlines() module_path = output[0] @@ -153,7 +153,7 @@ def find_module_path_and_all_py3(inspect: ModuleInspect, # Fall back to finding the module using sys.path. path = find_module_path_using_sys_path(module, sys.path) if path is None: - raise CantImport(module, str(e)) + raise CantImport(module, str(e)) from e return path, None if mod.is_c_module: return None diff --git a/mypy/suggestions.py b/mypy/suggestions.py index d8a927b39590..0a41b134db6f 100644 --- a/mypy/suggestions.py +++ b/mypy/suggestions.py @@ -568,8 +568,8 @@ def find_node_by_file_and_line(self, file: str, line: int) -> Tuple[str, SymbolN raise SuggestionFailure('Source file is not a Python file') try: modname, _ = self.finder.crawl_up(os.path.normpath(file)) - except InvalidSourceList: - raise SuggestionFailure('Invalid source file name: ' + file) + except InvalidSourceList as e: + raise SuggestionFailure('Invalid source file name: ' + file) from e if modname not in self.graph: raise SuggestionFailure('Unknown module: ' + modname) # We must be sure about any edits in this file as this might affect the line numbers. diff --git a/mypy/util.py b/mypy/util.py index dd59f287c9ed..6f6252136d64 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -132,7 +132,7 @@ def decode_python_encoding(source: bytes, pyversion: Tuple[int, int]) -> str: try: source_text = source.decode(encoding) except LookupError as lookuperr: - raise DecodeError(str(lookuperr)) + raise DecodeError(str(lookuperr)) from lookuperr return source_text diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index 0e3151e1a09c..f57ea5b94166 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -82,7 +82,7 @@ def run_setup(script_name: str, script_args: List[str]) -> bool: # "interrupted" as the argument. Convert it back so that # pytest will exit instead of just failing the test. if code == "interrupted": - raise KeyboardInterrupt + raise KeyboardInterrupt from e return code == 0 or code is None From a3b5933d36c1ee934fe49b7924dacffecd92b9fb Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Thu, 18 Jun 2020 19:15:41 +0800 Subject: [PATCH 034/138] [mypyc] Translate more primitive ops to CallC (#9014) Related issue: mypyc/mypyc#734 Mainly translate primitive ops that use call_emit and with their descriptions used elsewhere. Int ops are intentionally left out because they make a lot of changes to IR tests, therefore making them in a separate PR. --- mypyc/irbuild/builder.py | 2 +- mypyc/irbuild/expression.py | 2 +- mypyc/irbuild/ll_builder.py | 2 +- mypyc/primitives/dict_ops.py | 19 ++++++------- mypyc/primitives/list_ops.py | 38 ++++++++++++------------- mypyc/primitives/tuple_ops.py | 27 +++++++++--------- mypyc/test-data/exceptions.test | 6 ++-- mypyc/test-data/irbuild-any.test | 2 +- mypyc/test-data/irbuild-basic.test | 10 +++---- mypyc/test-data/irbuild-classes.test | 2 +- mypyc/test-data/irbuild-generics.test | 2 +- mypyc/test-data/irbuild-lists.test | 14 ++++----- mypyc/test-data/irbuild-optional.test | 6 ++-- mypyc/test-data/irbuild-statements.test | 2 +- mypyc/test-data/irbuild-tuple.test | 14 ++++----- mypyc/test-data/refcount.test | 6 ++-- mypyc/test/test_emitfunc.py | 8 ++++-- 17 files changed, 83 insertions(+), 79 deletions(-) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 0d3f91e50ae8..17559b90bcdf 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -517,7 +517,7 @@ def process_iterator_tuple_assignment(self, self.activate_block(ok_block) for litem in reversed(post_star_vals): - ritem = self.primitive_op(list_pop_last, [iter_list], line) + ritem = self.call_c(list_pop_last, [iter_list], line) self.assign(litem, ritem, line) # Assign the starred value diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 25e086bea38d..9daf2b302875 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -463,7 +463,7 @@ def transform_tuple_expr(builder: IRBuilder, expr: TupleExpr) -> Value: def _visit_tuple_display(builder: IRBuilder, expr: TupleExpr) -> Value: """Create a list, then turn it into a tuple.""" val_as_list = _visit_list_display(builder, expr.items, expr.line) - return builder.primitive_op(list_tuple_op, [val_as_list], expr.line) + return builder.call_c(list_tuple_op, [val_as_list], expr.line) def transform_dict_expr(builder: IRBuilder, expr: DictExpr) -> Value: diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index e8935381eccb..cdd36c1a514d 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -270,7 +270,7 @@ def py_call(self, pos_args_list = self.primitive_op(new_list_op, pos_arg_values, line) for star_arg_value in star_arg_values: self.primitive_op(list_extend_op, [pos_args_list, star_arg_value], line) - pos_args_tuple = self.primitive_op(list_tuple_op, [pos_args_list], line) + pos_args_tuple = self.call_c(list_tuple_op, [pos_args_list], line) kw_args_dict = self.make_dict(kw_arg_key_value_pairs, line) diff --git a/mypyc/primitives/dict_ops.py b/mypyc/primitives/dict_ops.py index e3c270194bb4..ee780f819372 100644 --- a/mypyc/primitives/dict_ops.py +++ b/mypyc/primitives/dict_ops.py @@ -11,7 +11,7 @@ from mypyc.primitives.registry import ( name_ref_op, method_op, binary_op, func_op, custom_op, simple_emit, negative_int_emit, call_emit, call_negative_bool_emit, - name_emit, c_custom_op, c_method_op + name_emit, c_custom_op, c_method_op, c_function_op ) @@ -103,25 +103,24 @@ return_type=dict_rprimitive, c_function_name='CPyDict_Build', error_kind=ERR_MAGIC, - var_arg_type=object_rprimitive,) + var_arg_type=object_rprimitive) # Construct a dictionary from another dictionary. -func_op( +c_function_op( name='builtins.dict', arg_types=[dict_rprimitive], - result_type=dict_rprimitive, + return_type=dict_rprimitive, + c_function_name='PyDict_Copy', error_kind=ERR_MAGIC, - emit=call_emit('PyDict_Copy'), priority=2) # Generic one-argument dict constructor: dict(obj) - -func_op( +c_function_op( name='builtins.dict', arg_types=[object_rprimitive], - result_type=dict_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyDict_FromAny')) + return_type=dict_rprimitive, + c_function_name='CPyDict_FromAny', + error_kind=ERR_MAGIC) # dict.keys() c_method_op( diff --git a/mypyc/primitives/list_ops.py b/mypyc/primitives/list_ops.py index 3da8048a6d78..ddacc7c9b0a5 100644 --- a/mypyc/primitives/list_ops.py +++ b/mypyc/primitives/list_ops.py @@ -49,20 +49,20 @@ def emit_new(emitter: EmitterInterface, args: List[str], dest: str) -> None: # list[index] (for an integer index) -list_get_item_op = method_op( +list_get_item_op = c_method_op( name='__getitem__', arg_types=[list_rprimitive, int_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyList_GetItem')) + return_type=object_rprimitive, + c_function_name='CPyList_GetItem', + error_kind=ERR_MAGIC) # Version with no int bounds check for when it is known to be short -method_op( +c_method_op( name='__getitem__', arg_types=[list_rprimitive, short_int_rprimitive], - result_type=object_rprimitive, + return_type=object_rprimitive, + c_function_name='CPyList_GetItemShort', error_kind=ERR_MAGIC, - emit=call_emit('CPyList_GetItemShort'), priority=2) # This is unsafe because it assumes that the index is a non-negative short integer @@ -76,13 +76,13 @@ def emit_new(emitter: EmitterInterface, args: List[str], dest: str) -> None: emit=call_emit('CPyList_GetItemUnsafe')) # list[index] = obj -list_set_item_op = method_op( +list_set_item_op = c_method_op( name='__setitem__', arg_types=[list_rprimitive, int_rprimitive, object_rprimitive], - steals=[False, False, True], - result_type=bool_rprimitive, + return_type=bool_rprimitive, + c_function_name='CPyList_SetItem', error_kind=ERR_FALSE, - emit=call_emit('CPyList_SetItem')) + steals=[False, False, True]) # list.append(obj) list_append_op = method_op( @@ -101,20 +101,20 @@ def emit_new(emitter: EmitterInterface, args: List[str], dest: str) -> None: emit=call_emit('CPyList_Extend')) # list.pop() -list_pop_last = method_op( +list_pop_last = c_method_op( name='pop', arg_types=[list_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyList_PopLast')) + return_type=object_rprimitive, + c_function_name='CPyList_PopLast', + error_kind=ERR_MAGIC) # list.pop(index) -list_pop = method_op( +list_pop = c_method_op( name='pop', arg_types=[list_rprimitive, int_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyList_Pop')) + return_type=object_rprimitive, + c_function_name='CPyList_Pop', + error_kind=ERR_MAGIC) # list.count(obj) c_method_op( diff --git a/mypyc/primitives/tuple_ops.py b/mypyc/primitives/tuple_ops.py index 338d7fbfed5e..4373fd5da18e 100644 --- a/mypyc/primitives/tuple_ops.py +++ b/mypyc/primitives/tuple_ops.py @@ -10,17 +10,18 @@ EmitterInterface, ERR_NEVER, ERR_MAGIC ) from mypyc.ir.rtypes import tuple_rprimitive, int_rprimitive, list_rprimitive, object_rprimitive -from mypyc.primitives.registry import func_op, method_op, custom_op, call_emit, simple_emit +from mypyc.primitives.registry import ( + func_op, c_method_op, custom_op, simple_emit, c_function_op +) # tuple[index] (for an int index) -tuple_get_item_op = method_op( +tuple_get_item_op = c_method_op( name='__getitem__', arg_types=[tuple_rprimitive, int_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPySequenceTuple_GetItem')) - + return_type=object_rprimitive, + c_function_name='CPySequenceTuple_GetItem', + error_kind=ERR_MAGIC) # Construct a boxed tuple from items: (item1, item2, ...) new_tuple_op = custom_op( @@ -49,18 +50,18 @@ def emit_len(emitter: EmitterInterface, args: List[str], dest: str) -> None: emit=emit_len) # Construct tuple from a list. -list_tuple_op = func_op( +list_tuple_op = c_function_op( name='builtins.tuple', arg_types=[list_rprimitive], - result_type=tuple_rprimitive, + return_type=tuple_rprimitive, + c_function_name='PyList_AsTuple', error_kind=ERR_MAGIC, - emit=call_emit('PyList_AsTuple'), priority=2) # Construct tuple from an arbitrary (iterable) object. -func_op( +c_function_op( name='builtins.tuple', arg_types=[object_rprimitive], - result_type=tuple_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('PySequence_Tuple')) + return_type=tuple_rprimitive, + c_function_name='PySequence_Tuple', + error_kind=ERR_MAGIC) diff --git a/mypyc/test-data/exceptions.test b/mypyc/test-data/exceptions.test index 332e6e7585cc..81560fce4471 100644 --- a/mypyc/test-data/exceptions.test +++ b/mypyc/test-data/exceptions.test @@ -14,7 +14,7 @@ def f(x): r2, r3 :: int L0: r0 = 0 - r1 = x[r0] :: list + r1 = CPyList_GetItemShort(x, r0) if is_error(r1) goto L3 (error at f:3) else goto L1 L1: r2 = unbox(int, r1) @@ -51,7 +51,7 @@ L1: r2 = None inc_ref z :: int r3 = box(int, z) - r4 = x.__setitem__(y, r3) :: list + r4 = CPyList_SetItem(x, y, r3) if not r4 goto L3 (error at f:4) else goto L2 :: bool L2: r5 = None @@ -144,7 +144,7 @@ L1: r2 = i < l :: int if r2 goto L2 else goto L7 :: bool L2: - r3 = a[i] :: list + r3 = CPyList_GetItem(a, i) if is_error(r3) goto L8 (error at sum:6) else goto L3 L3: r4 = unbox(int, r3) diff --git a/mypyc/test-data/irbuild-any.test b/mypyc/test-data/irbuild-any.test index 9d692741e259..ff5fe9272ad5 100644 --- a/mypyc/test-data/irbuild-any.test +++ b/mypyc/test-data/irbuild-any.test @@ -116,7 +116,7 @@ L0: r5 = a.__setitem__(r3, r4) :: object r6 = box(int, n) r7 = l.__setitem__(a, r6) :: object - r8 = l.__setitem__(n, a) :: list + r8 = CPyList_SetItem(l, n, a) r9 = box(int, n) r10 = [a, r9] r11 = None diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 2e9408cac0a2..41805058a940 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -754,7 +754,7 @@ L0: r6 = (r4, r5) r7 = 0 r8 = box(tuple[int, int], r6) - r9 = a.__setitem__(r7, r8) :: list + r9 = CPyList_SetItem(a, r7, r8) r10 = True r11 = box(bool, r10) y = r11 @@ -1633,7 +1633,7 @@ L0: r7 = [] r8 = box(tuple[int, int, int], r3) r9 = r7.extend(r8) :: list - r10 = tuple r7 :: list + r10 = PyList_AsTuple(r7) r11 = PyDict_New() r12 = py_call_with_kwargs(r6, r10, r11) r13 = unbox(tuple[int, int, int], r12) @@ -1662,7 +1662,7 @@ L0: r8 = [r7] r9 = box(tuple[int, int], r3) r10 = r8.extend(r9) :: list - r11 = tuple r8 :: list + r11 = PyList_AsTuple(r8) r12 = PyDict_New() r13 = py_call_with_kwargs(r6, r11, r12) r14 = unbox(tuple[int, int, int], r13) @@ -2387,7 +2387,7 @@ def g(a, b, c): r2, r3 :: int L0: r0 = a.__getitem__(c) - r1 = b[c] :: list + r1 = CPyList_GetItem(b, c) r2 = unbox(int, r1) r3 = r0 + r2 :: int return r3 @@ -3261,7 +3261,7 @@ L1: unreachable L2: r2 = 0 - r3 = r0[r2] :: list + r3 = CPyList_GetItemShort(r0, r2) r4 = unbox(int, r3) return r4 diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index 74973b43df7f..82fdab578ca4 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -60,7 +60,7 @@ L0: r3 = [c] a = r3 r4 = 0 - r5 = a[r4] :: list + r5 = CPyList_GetItemShort(a, r4) r6 = cast(__main__.C, r5) d = r6 r7 = d.x diff --git a/mypyc/test-data/irbuild-generics.test b/mypyc/test-data/irbuild-generics.test index 422b4668972f..a7d7d973c7c9 100644 --- a/mypyc/test-data/irbuild-generics.test +++ b/mypyc/test-data/irbuild-generics.test @@ -20,7 +20,7 @@ def g(x): r2 :: list L0: r0 = 0 - r1 = x[r0] :: list + r1 = CPyList_GetItemShort(x, r0) r2 = [r1] return r2 def h(x, y): diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test index de760d71914b..e1b34d565a8a 100644 --- a/mypyc/test-data/irbuild-lists.test +++ b/mypyc/test-data/irbuild-lists.test @@ -10,7 +10,7 @@ def f(x): r2 :: int L0: r0 = 0 - r1 = x[r0] :: list + r1 = CPyList_GetItemShort(x, r0) r2 = unbox(int, r1) return r2 @@ -26,7 +26,7 @@ def f(x): r2 :: list L0: r0 = 0 - r1 = x[r0] :: list + r1 = CPyList_GetItemShort(x, r0) r2 = cast(list, r1) return r2 @@ -45,10 +45,10 @@ def f(x): r5 :: int L0: r0 = 0 - r1 = x[r0] :: list + r1 = CPyList_GetItemShort(x, r0) r2 = cast(list, r1) r3 = 1 - r4 = r2[r3] :: list + r4 = CPyList_GetItemShort(r2, r3) r5 = unbox(int, r4) return r5 @@ -67,7 +67,7 @@ L0: r0 = 1 r1 = 0 r2 = box(short_int, r0) - r3 = x.__setitem__(r1, r2) :: list + r3 = CPyList_SetItem(x, r1, r2) r4 = None return r4 @@ -188,11 +188,11 @@ L1: r3 = r2 < r1 :: short_int if r3 goto L2 else goto L4 :: bool L2: - r4 = l[i] :: list + r4 = CPyList_GetItem(l, i) r5 = 1 r6 = box(short_int, r5) r7 = r4 += r6 - r8 = l.__setitem__(i, r7) :: list + r8 = CPyList_SetItem(l, i, r7) L3: r9 = 1 r10 = r2 + r9 :: short_int diff --git a/mypyc/test-data/irbuild-optional.test b/mypyc/test-data/irbuild-optional.test index df13146ecc82..82c8c38363ce 100644 --- a/mypyc/test-data/irbuild-optional.test +++ b/mypyc/test-data/irbuild-optional.test @@ -197,11 +197,11 @@ L0: r0 = 0 r1 = 0 r2 = box(short_int, r0) - r3 = x.__setitem__(r1, r2) :: list + r3 = CPyList_SetItem(x, r1, r2) r4 = None r5 = 1 r6 = box(None, r4) - r7 = x.__setitem__(r5, r6) :: list + r7 = CPyList_SetItem(x, r5, r6) r8 = None return r8 @@ -334,7 +334,7 @@ def f(x): r2 :: union[int, str] L0: r0 = 0 - r1 = x[r0] :: list + r1 = CPyList_GetItemShort(x, r0) r2 = cast(union[int, str], r1) return r2 diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index ff891f26ce72..d3afc48a6e80 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -559,7 +559,7 @@ L0: a.x = r1; r2 = is_error r3 = t[1] r4 = r3[0] - r5 = l.__setitem__(r0, r4) :: list + r5 = CPyList_SetItem(l, r0, r4) r6 = r3[1] r7 = unbox(int, r6) z = r7 diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index fb48f3890a22..a8c31dc1cd4d 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -57,9 +57,9 @@ def f(x): r2 :: object r3 :: bool L0: - r0 = tuple x :: list + r0 = PyList_AsTuple(x) r1 = 1 - r2 = r0[r1] :: tuple + r2 = CPySequenceTuple_GetItem(r0, r1) r3 = unbox(bool, r2) return r3 @@ -96,7 +96,7 @@ L0: r3 = box(tuple[int, int], r2) t = r3 r4 = 1 - r5 = t[r4] :: tuple + r5 = CPySequenceTuple_GetItem(t, r4) r6 = unbox(int, r5) return r6 @@ -124,7 +124,7 @@ L0: r7 = r5.extend(y) :: list r8 = box(short_int, r2) r9 = r5.append(r8) :: list - r10 = tuple r5 :: list + r10 = PyList_AsTuple(r5) return r10 [case testTupleFor] @@ -150,7 +150,7 @@ L1: r3 = r1 < r2 :: int if r3 goto L2 else goto L4 :: bool L2: - r4 = xs[r1] :: tuple + r4 = CPySequenceTuple_GetItem(xs, r1) r5 = cast(str, r4) x = r5 L3: @@ -185,11 +185,11 @@ L0: if b goto L1 else goto L2 :: bool L1: r0 = 0 - r1 = nt[r0] :: tuple + r1 = CPySequenceTuple_GetItem(nt, r0) r2 = unbox(int, r1) return r2 L2: r3 = 1 - r4 = nt[r3] :: tuple + r4 = CPySequenceTuple_GetItem(nt, r3) r5 = unbox(int, r4) return r5 diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index e326f6f7c7cf..255e84a05f56 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -630,12 +630,12 @@ def f(a, b): r6 :: None L0: r0 = 0 - r1 = b[r0] :: list + r1 = CPyList_GetItemShort(b, r0) r2 = unbox(int, r1) dec_ref r1 r3 = 0 r4 = box(int, r2) - r5 = a.__setitem__(r3, r4) :: list + r5 = CPyList_SetItem(a, r3, r4) r6 = None return r6 @@ -693,7 +693,7 @@ L0: r1 = [r0] a = r1 r2 = 0 - r3 = a[r2] :: list + r3 = CPyList_GetItemShort(a, r2) dec_ref a r4 = cast(__main__.C, r3) d = r4 diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index 469bc082e762..ec656bcf9917 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -156,11 +156,15 @@ def test_dec_ref_tuple_nested(self) -> None: self.assert_emit(DecRef(self.tt), 'CPyTagged_DecRef(cpy_r_tt.f0.f0);') def test_list_get_item(self) -> None: - self.assert_emit(PrimitiveOp([self.m, self.k], list_get_item_op, 55), + self.assert_emit(CallC(list_get_item_op.c_function_name, [self.m, self.k], + list_get_item_op.return_type, list_get_item_op.steals, + list_get_item_op.error_kind, 55), """cpy_r_r0 = CPyList_GetItem(cpy_r_m, cpy_r_k);""") def test_list_set_item(self) -> None: - self.assert_emit(PrimitiveOp([self.l, self.n, self.o], list_set_item_op, 55), + self.assert_emit(CallC(list_set_item_op.c_function_name, [self.l, self.n, self.o], + list_set_item_op.return_type, list_set_item_op.steals, + list_set_item_op.error_kind, 55), """cpy_r_r0 = CPyList_SetItem(cpy_r_l, cpy_r_n, cpy_r_o);""") def test_box(self) -> None: From d39970f3ac7b786e4e970f1c2bd62f37579500da Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 18 Jun 2020 15:51:22 +0100 Subject: [PATCH 035/138] Empty dummy commit to trigger builds From dc2c392138ebe85fa106b2896f4532b75809ab30 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 18 Jun 2020 17:02:42 +0100 Subject: [PATCH 036/138] Don't consider comparing True and False as a dangerous comparison (#9021) Fixes #9011. --- mypy/checkexpr.py | 4 ++++ test-data/unit/check-literal.test | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 450993a90c4d..5af114767357 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -2306,6 +2306,10 @@ def dangerous_comparison(self, left: Type, right: Type, left = map_instance_to_supertype(left, abstract_set) right = map_instance_to_supertype(right, abstract_set) return not is_overlapping_types(left.args[0], right.args[0]) + if isinstance(left, LiteralType) and isinstance(right, LiteralType): + if isinstance(left.value, bool) and isinstance(right.value, bool): + # Comparing different booleans is not dangerous. + return False return not is_overlapping_types(left, right, ignore_promotions=False) def get_operator_method(self, op: str) -> str: diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index 781f7a6378dc..3ff8b17f90b7 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -3228,3 +3228,18 @@ y: Literal[F.A] reveal_type(x) # N: Revealed type is 'Literal[__main__.Foo.A]' reveal_type(y) # N: Revealed type is 'Literal[__main__.Foo.A]' [builtins fixtures/tuple.pyi] + +[case testStrictEqualityLiteralTrueVsFalse] +# mypy: strict-equality + +class C: + a = True + + def update(self) -> None: + self.a = False + +c = C() +assert c.a is True +c.update() +assert c.a is False +[builtins fixtures/bool.pyi] From 71e4540e63796f2d7e4a59b293003e292100b49a Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 18 Jun 2020 17:26:17 +0100 Subject: [PATCH 037/138] Empty dummy commit to trigger builds (2) From be01236bcdb9a9da66e68dd0d45ff0f9a604e44a Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Fri, 19 Jun 2020 00:36:45 +0800 Subject: [PATCH 038/138] [mypyc] Merge int ops(for int_rprimitive) to CallC (#9019) Related issue: mypyc/mypyc#734 int_compare_op of short_int_rprimitive remain unchanged because they'll need some low-level arithmetic. --- mypyc/ir/ops.py | 2 +- mypyc/primitives/int_ops.py | 24 ++--- mypyc/test-data/analysis.test | 30 +++---- mypyc/test-data/exceptions.test | 6 +- mypyc/test-data/irbuild-basic.test | 100 ++++++++++----------- mypyc/test-data/irbuild-classes.test | 14 +-- mypyc/test-data/irbuild-generics.test | 4 +- mypyc/test-data/irbuild-lists.test | 2 +- mypyc/test-data/irbuild-nested.test | 20 ++--- mypyc/test-data/irbuild-optional.test | 4 +- mypyc/test-data/irbuild-statements.test | 42 ++++----- mypyc/test-data/irbuild-strip-asserts.test | 2 +- mypyc/test-data/irbuild-tuple.test | 2 +- mypyc/test-data/refcount.test | 62 ++++++------- mypyc/test/test_emitfunc.py | 12 ++- 15 files changed, 168 insertions(+), 158 deletions(-) diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 9027c79b51e3..78820074e2d6 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -1203,7 +1203,7 @@ def to_str(self, env: Environment) -> str: # return env.format('%r = %s%s', self, self.identifier, ann) # TODO: a hack to prevent lots of failed IR tests when developing prototype # eventually we will change all the related tests - return env.format('%r = %s :: static%s ', self, self.identifier[10:], ann) + return env.format('%r = %s :: static%s', self, self.identifier[10:], ann) def accept(self, visitor: 'OpVisitor[T]') -> T: return visitor.visit_load_global(self) diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index 490f074a1d25..acd54ea38fb9 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -12,8 +12,8 @@ str_rprimitive, RType ) from mypyc.primitives.registry import ( - name_ref_op, binary_op, custom_op, simple_emit, call_emit, name_emit, - c_unary_op, CFunctionDescription, c_function_op + name_ref_op, binary_op, custom_op, simple_emit, name_emit, + c_unary_op, CFunctionDescription, c_function_op, c_binary_op ) # These int constructors produce object_rprimitives that then need to be unboxed @@ -70,20 +70,20 @@ priority=3) -def int_binary_op(op: str, c_func_name: str, - result_type: RType = int_rprimitive, +def int_binary_op(name: str, c_function_name: str, + return_type: RType = int_rprimitive, error_kind: int = ERR_NEVER) -> None: - binary_op(op=op, - arg_types=[int_rprimitive, int_rprimitive], - result_type=result_type, - error_kind=error_kind, - format_str='{dest} = {args[0]} %s {args[1]} :: int' % op, - emit=call_emit(c_func_name)) + c_binary_op(name=name, + arg_types=[int_rprimitive, int_rprimitive], + return_type=return_type, + c_function_name=c_function_name, + error_kind=error_kind) -def int_compare_op(op: str, c_func_name: str) -> None: - int_binary_op(op, c_func_name, bool_rprimitive) +def int_compare_op(name: str, c_function_name: str) -> None: + int_binary_op(name, c_function_name, bool_rprimitive) # Generate a straight compare if we know both sides are short + op = name binary_op(op=op, arg_types=[short_int_rprimitive, short_int_rprimitive], result_type=bool_rprimitive, diff --git a/mypyc/test-data/analysis.test b/mypyc/test-data/analysis.test index 0155c22feb93..76c98e9fba40 100644 --- a/mypyc/test-data/analysis.test +++ b/mypyc/test-data/analysis.test @@ -21,7 +21,7 @@ def f(a): L0: r0 = 1 x = r0 - r1 = x == a :: int + r1 = CPyTagged_IsEq(x, a) if r1 goto L1 else goto L2 :: bool L1: r2 = 1 @@ -64,7 +64,7 @@ L0: r0 = 1 x = r0 r1 = 1 - r2 = x == r1 :: int + r2 = CPyTagged_IsEq(x, r1) if r2 goto L1 else goto L2 :: bool L1: return a @@ -156,7 +156,7 @@ def f(a): r5 :: None L0: r0 = 1 - r1 = a == r0 :: int + r1 = CPyTagged_IsEq(a, r0) if r1 goto L1 else goto L2 :: bool L1: r2 = 1 @@ -210,11 +210,11 @@ def f(n): L0: L1: r0 = 5 - r1 = n < r0 :: int + r1 = CPyTagged_IsLt(n, r0) if r1 goto L2 else goto L3 :: bool L2: r2 = 1 - r3 = n + r2 :: int + r3 = CPyTagged_Add(n, r2) n = r3 m = n goto L1 @@ -262,13 +262,13 @@ L0: y = r1 L1: r2 = 1 - r3 = n < r2 :: int + r3 = CPyTagged_IsLt(n, r2) if r3 goto L2 else goto L6 :: bool L2: n = y L3: r4 = 2 - r5 = n < r4 :: int + r5 = CPyTagged_IsLt(n, r4) if r5 goto L4 else goto L5 :: bool L4: r6 = 1 @@ -319,7 +319,7 @@ L1: r2 = f(a) if is_error(r2) goto L3 (error at f:3) else goto L2 L2: - r3 = r2 + a :: int + r3 = CPyTagged_Add(r2, a) return r3 L3: r4 = :: int @@ -349,11 +349,11 @@ def f(a): r2 :: None L0: L1: - r0 = a < a :: int + r0 = CPyTagged_IsLt(a, a) if r0 goto L2 else goto L6 :: bool L2: L3: - r1 = a < a :: int + r1 = CPyTagged_IsLt(a, a) if r1 goto L4 else goto L5 :: bool L4: y = a @@ -422,7 +422,7 @@ def f(a): x :: int r2, r3 :: short_int L0: - r0 = a == a :: int + r0 = CPyTagged_IsEq(a, a) if r0 goto L1 else goto L2 :: bool L1: r1 = 2 @@ -472,13 +472,13 @@ L0: r1 = 0 i = r1 L1: - r2 = i <= a :: int + r2 = CPyTagged_IsLe(i, a) if r2 goto L2 else goto L3 :: bool L2: - r3 = sum + i :: int + r3 = CPyTagged_Add(sum, i) sum = r3 r4 = 1 - r5 = i + r4 :: int + r5 = CPyTagged_Add(i, r4) i = r5 goto L1 L3: @@ -558,7 +558,7 @@ L9: unreachable L10: r10 = 1 - r11 = st + r10 :: int + r11 = CPyTagged_Add(st, r10) return r11 L11: r12 = :: int diff --git a/mypyc/test-data/exceptions.test b/mypyc/test-data/exceptions.test index 81560fce4471..edac4269afc8 100644 --- a/mypyc/test-data/exceptions.test +++ b/mypyc/test-data/exceptions.test @@ -141,7 +141,7 @@ L0: r1 = 0 i = r1 L1: - r2 = i < l :: int + r2 = CPyTagged_IsLt(i, l) if r2 goto L2 else goto L7 :: bool L2: r3 = CPyList_GetItem(a, i) @@ -151,12 +151,12 @@ L3: dec_ref r3 if is_error(r4) goto L8 (error at sum:6) else goto L4 L4: - r5 = sum + r4 :: int + r5 = CPyTagged_Add(sum, r4) dec_ref sum :: int dec_ref r4 :: int sum = r5 r6 = 1 - r7 = i + r6 :: int + r7 = CPyTagged_Add(i, r6) dec_ref i :: int i = r7 goto L1 diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 41805058a940..1ae55b75940b 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -80,8 +80,8 @@ def f(x, y): r1, r2 :: int L0: r0 = 1 - r1 = y + r0 :: int - r2 = x * r1 :: int + r1 = CPyTagged_Add(y, r0) + r2 = CPyTagged_Multiply(x, r1) return r2 [case testIf] @@ -95,7 +95,7 @@ def f(x, y): r0 :: bool r1 :: short_int L0: - r0 = x < y :: int + r0 = CPyTagged_IsLt(x, y) if r0 goto L1 else goto L2 :: bool L1: r1 = 1 @@ -116,7 +116,7 @@ def f(x, y): r0 :: bool r1, r2 :: short_int L0: - r0 = x < y :: int + r0 = CPyTagged_IsLt(x, y) if r0 goto L1 else goto L2 :: bool L1: r1 = 1 @@ -141,10 +141,10 @@ def f(x, y): r0, r1 :: bool r2, r3 :: short_int L0: - r0 = x < y :: int + r0 = CPyTagged_IsLt(x, y) if r0 goto L1 else goto L3 :: bool L1: - r1 = x > y :: int + r1 = CPyTagged_IsGt(x, y) if r1 goto L2 else goto L3 :: bool L2: r2 = 1 @@ -191,10 +191,10 @@ def f(x, y): r0, r1 :: bool r2, r3 :: short_int L0: - r0 = x < y :: int + r0 = CPyTagged_IsLt(x, y) if r0 goto L2 else goto L1 :: bool L1: - r1 = x > y :: int + r1 = CPyTagged_IsGt(x, y) if r1 goto L2 else goto L3 :: bool L2: r2 = 1 @@ -239,7 +239,7 @@ def f(x, y): r0 :: bool r1 :: short_int L0: - r0 = x < y :: int + r0 = CPyTagged_IsLt(x, y) if r0 goto L2 else goto L1 :: bool L1: r1 = 1 @@ -258,10 +258,10 @@ def f(x, y): r0, r1 :: bool r2 :: short_int L0: - r0 = x < y :: int + r0 = CPyTagged_IsLt(x, y) if r0 goto L1 else goto L2 :: bool L1: - r1 = x > y :: int + r1 = CPyTagged_IsGt(x, y) if r1 goto L3 else goto L2 :: bool L2: r2 = 1 @@ -281,10 +281,10 @@ def f(x, y): r1 :: int L0: L1: - r0 = x > y :: int + r0 = CPyTagged_IsGt(x, y) if r0 goto L2 else goto L3 :: bool L2: - r1 = x - y :: int + r1 = CPyTagged_Subtract(x, y) x = r1 goto L1 L3: @@ -306,10 +306,10 @@ L0: r0 = 1 x = r0 L1: - r1 = x > y :: int + r1 = CPyTagged_IsGt(x, y) if r1 goto L2 else goto L3 :: bool L2: - r2 = x - y :: int + r2 = CPyTagged_Subtract(x, y) x = r2 goto L1 L3: @@ -352,7 +352,7 @@ def f(x, y): r1, r2 :: short_int r3 :: None L0: - r0 = x < y :: int + r0 = CPyTagged_IsLt(x, y) if r0 goto L1 else goto L2 :: bool L1: r1 = 1 @@ -382,19 +382,19 @@ def f(n): r7, r8, r9 :: int L0: r0 = 1 - r1 = n <= r0 :: int + r1 = CPyTagged_IsLe(n, r0) if r1 goto L1 else goto L2 :: bool L1: r2 = 1 return r2 L2: r3 = 1 - r4 = n - r3 :: int + r4 = CPyTagged_Subtract(n, r3) r5 = f(r4) r6 = 2 - r7 = n - r6 :: int + r7 = CPyTagged_Subtract(n, r6) r8 = f(r7) - r9 = r5 + r8 :: int + r9 = CPyTagged_Add(r5, r8) return r9 L3: unreachable @@ -432,7 +432,7 @@ def f(n): r5, r6 :: short_int L0: r0 = 0 - r1 = n < r0 :: int + r1 = CPyTagged_IsLt(n, r0) if r1 goto L1 else goto L2 :: bool L1: r2 = 1 @@ -440,7 +440,7 @@ L1: goto L6 L2: r3 = 0 - r4 = n == r3 :: int + r4 = CPyTagged_IsEq(n, r3) if r4 goto L3 else goto L4 :: bool L3: r5 = 1 @@ -478,7 +478,7 @@ def f(n): r3, r4 :: short_int L0: r0 = 0 - r1 = n == r0 :: int + r1 = CPyTagged_IsEq(n, r0) if r1 goto L1 else goto L2 :: bool L1: r3 = 0 @@ -505,7 +505,7 @@ L0: r0 = 0 x = r0 r1 = 1 - r2 = x += r1 :: int + r2 = CPyTagged_Add(x, r1) x = r2 return x @@ -1021,7 +1021,7 @@ def absolute_value(x): r2, r3 :: int L0: r0 = 0 - r1 = x > r0 :: int + r1 = CPyTagged_IsGt(x, r0) if r1 goto L1 else goto L2 :: bool L1: r2 = x @@ -1193,7 +1193,7 @@ def num(x): r2, r3 :: short_int L0: r0 = 0 - r1 = x != r0 :: int + r1 = CPyTagged_IsNe(x, r0) if r1 goto L1 else goto L2 :: bool L1: r2 = 1 @@ -1211,7 +1211,7 @@ def lst(x): L0: r0 = len x :: list r1 = 0 - r2 = r0 != r1 :: short_int + r2 = CPyTagged_IsNe(r0, r1) if r2 goto L1 else goto L2 :: bool L1: r3 = 1 @@ -1260,7 +1260,7 @@ L0: L1: r2 = unbox(int, x) r3 = 0 - r4 = r2 != r3 :: int + r4 = CPyTagged_IsNe(r2, r3) if r4 goto L2 else goto L3 :: bool L2: r5 = 1 @@ -1894,25 +1894,25 @@ L0: r9 = r8 L1: r10 = len r7 :: list - r11 = r9 < r10 :: short_int + r11 = CPyTagged_IsLt(r9, r10) if r11 goto L2 else goto L8 :: bool L2: r12 = r7[r9] :: unsafe list r13 = unbox(int, r12) x = r13 r14 = 2 - r15 = x != r14 :: int + r15 = CPyTagged_IsNe(x, r14) if r15 goto L4 else goto L3 :: bool L3: goto L7 L4: r16 = 3 - r17 = x != r16 :: int + r17 = CPyTagged_IsNe(x, r16) if r17 goto L6 else goto L5 :: bool L5: goto L7 L6: - r18 = x * x :: int + r18 = CPyTagged_Multiply(x, x) r19 = box(int, r18) r20 = r0.append(r19) :: list L7: @@ -1958,25 +1958,25 @@ L0: r9 = r8 L1: r10 = len r7 :: list - r11 = r9 < r10 :: short_int + r11 = CPyTagged_IsLt(r9, r10) if r11 goto L2 else goto L8 :: bool L2: r12 = r7[r9] :: unsafe list r13 = unbox(int, r12) x = r13 r14 = 2 - r15 = x != r14 :: int + r15 = CPyTagged_IsNe(x, r14) if r15 goto L4 else goto L3 :: bool L3: goto L7 L4: r16 = 3 - r17 = x != r16 :: int + r17 = CPyTagged_IsNe(x, r16) if r17 goto L6 else goto L5 :: bool L5: goto L7 L6: - r18 = x * x :: int + r18 = CPyTagged_Multiply(x, x) r19 = box(int, x) r20 = box(int, r18) r21 = r0.__setitem__(r19, r20) :: dict @@ -2019,7 +2019,7 @@ L0: r1 = r0 L1: r2 = len l :: list - r3 = r1 < r2 :: short_int + r3 = CPyTagged_IsLt(r1, r2) if r3 goto L2 else goto L4 :: bool L2: r4 = l[r1] :: unsafe list @@ -2041,7 +2041,7 @@ L4: r13 = r12 L5: r14 = len l :: list - r15 = r13 < r14 :: short_int + r15 = CPyTagged_IsLt(r13, r14) if r15 goto L6 else goto L8 :: bool L6: r16 = l[r13] :: unsafe list @@ -2052,8 +2052,8 @@ L6: y0 = r19 r20 = r17[2] z0 = r20 - r21 = x0 + y0 :: int - r22 = r21 + z0 :: int + r21 = CPyTagged_Add(x0, y0) + r22 = CPyTagged_Add(r21, z0) r23 = box(int, r22) r24 = r11.append(r23) :: list L7: @@ -2086,13 +2086,13 @@ L0: L1: r2 = self.left r3 = self.right - r4 = r2 + r3 :: int + r4 = CPyTagged_Add(r2, r3) r1 = r4 goto L3 L2: r5 = self.left r6 = self.right - r7 = r5 - r6 :: int + r7 = CPyTagged_Subtract(r5, r6) r1 = r7 L3: return r1 @@ -2114,7 +2114,7 @@ def PropertyHolder.twice_value(self): L0: r0 = 2 r1 = self.value - r2 = r0 * r1 :: int + r2 = CPyTagged_Multiply(r0, r1) return r2 [case testPropertyDerivedGen] @@ -2187,7 +2187,7 @@ def BaseProperty.next(self): L0: r0 = self._incrementer r1 = 1 - r2 = r0 + r1 :: int + r2 = CPyTagged_Add(r0, r1) r3 = BaseProperty(r2) return r3 def BaseProperty.__init__(self, value): @@ -2389,7 +2389,7 @@ L0: r0 = a.__getitem__(c) r1 = CPyList_GetItem(b, c) r2 = unbox(int, r1) - r3 = r0 + r2 :: int + r3 = CPyTagged_Add(r0, r2) return r3 [case testTypeAlias_toplevel] @@ -2588,14 +2588,14 @@ def f(x, y, z): L0: r0 = g(x) r1 = g(y) - r3 = r0 < r1 :: int + r3 = CPyTagged_IsLt(r0, r1) if r3 goto L2 else goto L1 :: bool L1: r2 = r3 goto L3 L2: r4 = g(z) - r5 = r1 > r4 :: int + r5 = CPyTagged_IsGt(r1, r4) r2 = r5 L3: return r2 @@ -3053,7 +3053,7 @@ L2: r4 = unbox(int, r3) i = r4 r5 = 0 - r6 = i == r5 :: int + r6 = CPyTagged_IsEq(i, r5) if r6 goto L3 else goto L4 :: bool L3: r7 = True @@ -3085,7 +3085,7 @@ L2: r4 = unbox(int, r3) i = r4 r5 = 0 - r6 = i == r5 :: int + r6 = CPyTagged_IsEq(i, r5) r7 = !r6 if r7 goto L3 else goto L4 :: bool L3: @@ -3308,7 +3308,7 @@ L1: unreachable L2: r2 = 1 - r3 = r0 - r2 :: int + r3 = CPyTagged_Subtract(r0, r2) return r3 [case testFinalRestrictedTypeVar] diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index 82fdab578ca4..99880d75de17 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -65,7 +65,7 @@ L0: d = r6 r7 = d.x r8 = 1 - r9 = r7 + r8 :: int + r9 = CPyTagged_Add(r7, r8) return r9 [case testMethodCall] @@ -84,7 +84,7 @@ def A.f(self, x, y): r1 :: int L0: r0 = 10 - r1 = x + r0 :: int + r1 = CPyTagged_Add(x, r0) return r1 def g(a): a :: __main__.A @@ -146,7 +146,7 @@ L1: r6 = self.next r7 = cast(__main__.Node, r6) r8 = r7.length() - r9 = r5 + r8 :: int + r9 = CPyTagged_Add(r5, r8) return r9 L2: r10 = 1 @@ -214,7 +214,7 @@ def increment(o): L0: r0 = o.x r1 = 1 - r2 = r0 += r1 :: int + r2 = CPyTagged_Add(r0, r1) o.x = r2; r3 = is_error return o @@ -705,7 +705,7 @@ def C.foo(x): r1 :: int L0: r0 = 10 - r1 = r0 + x :: int + r1 = CPyTagged_Add(r0, x) return r1 def C.bar(cls, x): cls :: object @@ -714,7 +714,7 @@ def C.bar(cls, x): r1 :: int L0: r0 = 10 - r1 = r0 + x :: int + r1 = CPyTagged_Add(r0, x) return r1 def lol(): r0 :: short_int @@ -728,7 +728,7 @@ L0: r2 = __main__.C :: type r3 = 2 r4 = C.bar(r2, r3) - r5 = r1 + r4 :: int + r5 = CPyTagged_Add(r1, r4) return r5 [case testSuper1] diff --git a/mypyc/test-data/irbuild-generics.test b/mypyc/test-data/irbuild-generics.test index a7d7d973c7c9..08c90cac7bf0 100644 --- a/mypyc/test-data/irbuild-generics.test +++ b/mypyc/test-data/irbuild-generics.test @@ -68,7 +68,7 @@ L0: r4 = 2 r5 = c.x r6 = unbox(int, r5) - r7 = r4 + r6 :: int + r7 = CPyTagged_Add(r4, r6) r8 = None return r8 @@ -129,7 +129,7 @@ L0: r1 = unbox(int, r0) y = r1 r2 = 1 - r3 = y + r2 :: int + r3 = CPyTagged_Add(y, r2) r4 = box(int, r3) r5 = x.set(r4) r6 = 2 diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test index e1b34d565a8a..1455f02ac05e 100644 --- a/mypyc/test-data/irbuild-lists.test +++ b/mypyc/test-data/irbuild-lists.test @@ -185,7 +185,7 @@ L0: r2 = r0 i = r2 L1: - r3 = r2 < r1 :: short_int + r3 = CPyTagged_IsLt(r2, r1) if r3 goto L2 else goto L4 :: bool L2: r4 = CPyList_GetItem(l, i) diff --git a/mypyc/test-data/irbuild-nested.test b/mypyc/test-data/irbuild-nested.test index a8dc398f0b93..711e6ea8af3d 100644 --- a/mypyc/test-data/irbuild-nested.test +++ b/mypyc/test-data/irbuild-nested.test @@ -369,7 +369,7 @@ L0: r7 = py_call(r6) r8 = unbox(int, r7) r9 = r0.num - r10 = r8 + r9 :: int + r10 = CPyTagged_Add(r8, r9) return r10 def inner_c_obj.__get__(__mypyc_self__, instance, owner): __mypyc_self__, instance, owner, r0 :: object @@ -518,7 +518,7 @@ L0: r2.__mypyc_env__ = r0; r3 = is_error r4 = r0.x r5 = 1 - r6 = r4 += r5 :: int + r6 = CPyTagged_Add(r4, r5) r0.x = r6; r7 = is_error r8 = c_a_b_obj() r8.__mypyc_env__ = r2; r9 = is_error @@ -676,7 +676,7 @@ L0: foo = r1 r2 = r0.a r3 = 1 - r4 = r2 + r3 :: int + r4 = CPyTagged_Add(r2, r3) return r4 def bar_f_obj.__get__(__mypyc_self__, instance, owner): __mypyc_self__, instance, owner, r0 :: object @@ -733,18 +733,18 @@ L0: r1 = r0.baz baz = r1 r2 = 0 - r3 = n == r2 :: int + r3 = CPyTagged_IsEq(n, r2) if r3 goto L1 else goto L2 :: bool L1: r4 = 0 return r4 L2: r5 = 1 - r6 = n - r5 :: int + r6 = CPyTagged_Subtract(n, r5) r7 = box(int, r6) r8 = py_call(baz, r7) r9 = unbox(int, r8) - r10 = n + r9 :: int + r10 = CPyTagged_Add(n, r9) return r10 def f(a): a :: int @@ -780,7 +780,7 @@ L0: r16 = box(int, r14) r17 = py_call(r15, r16) r18 = unbox(int, r17) - r19 = r13 + r18 :: int + r19 = CPyTagged_Add(r13, r18) return r19 [case testLambdas] @@ -875,14 +875,14 @@ def baz(n): r4, r5, r6 :: int L0: r0 = 0 - r1 = n == r0 :: int + r1 = CPyTagged_IsEq(n, r0) if r1 goto L1 else goto L2 :: bool L1: r2 = 0 return r2 L2: r3 = 1 - r4 = n - r3 :: int + r4 = CPyTagged_Subtract(n, r3) r5 = baz(r4) - r6 = n + r5 :: int + r6 = CPyTagged_Add(n, r5) return r6 diff --git a/mypyc/test-data/irbuild-optional.test b/mypyc/test-data/irbuild-optional.test index 82c8c38363ce..5d104e1c553b 100644 --- a/mypyc/test-data/irbuild-optional.test +++ b/mypyc/test-data/irbuild-optional.test @@ -266,7 +266,7 @@ L0: r1 = box(None, r0) x = r1 r2 = 1 - r3 = y == r2 :: int + r3 = CPyTagged_IsEq(y, r2) if r3 goto L1 else goto L2 :: bool L1: r4 = box(int, y) @@ -312,7 +312,7 @@ L0: L1: r2 = unbox(int, x) r3 = 1 - r4 = r2 + r3 :: int + r4 = CPyTagged_Add(r2, r3) return r4 L2: r5 = cast(__main__.A, x) diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index d3afc48a6e80..8b9fb36c6fde 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -21,10 +21,10 @@ L0: r3 = r1 i = r3 L1: - r4 = r3 < r2 :: short_int + r4 = CPyTagged_IsLt(r3, r2) if r4 goto L2 else goto L4 :: bool L2: - r5 = x + i :: int + r5 = CPyTagged_Add(x, i) x = r5 L3: r6 = 1 @@ -53,7 +53,7 @@ L0: r2 = r0 i = r2 L1: - r3 = r2 > r1 :: short_int + r3 = CPyTagged_IsGt(r2, r1) if r3 goto L2 else goto L4 :: bool L2: L3: @@ -83,7 +83,7 @@ L0: n = r0 L1: r1 = 5 - r2 = n < r1 :: int + r2 = CPyTagged_IsLt(n, r1) if r2 goto L2 else goto L3 :: bool L2: L3: @@ -107,7 +107,7 @@ L0: r2 = r0 n = r2 L1: - r3 = r2 < r1 :: short_int + r3 = CPyTagged_IsLt(r2, r1) if r3 goto L2 else goto L4 :: bool L2: goto L4 @@ -142,12 +142,12 @@ L0: n = r0 L1: r1 = 5 - r2 = n < r1 :: int + r2 = CPyTagged_IsLt(n, r1) if r2 goto L2 else goto L6 :: bool L2: L3: r3 = 4 - r4 = n < r3 :: int + r4 = CPyTagged_IsLt(n, r3) if r4 goto L4 else goto L5 :: bool L4: L5: @@ -172,7 +172,7 @@ L0: n = r0 L1: r1 = 5 - r2 = n < r1 :: int + r2 = CPyTagged_IsLt(n, r1) if r2 goto L2 else goto L3 :: bool L2: goto L1 @@ -197,7 +197,7 @@ L0: r2 = r0 n = r2 L1: - r3 = r2 < r1 :: short_int + r3 = CPyTagged_IsLt(r2, r1) if r3 goto L2 else goto L4 :: bool L2: L3: @@ -231,12 +231,12 @@ L0: n = r0 L1: r1 = 5 - r2 = n < r1 :: int + r2 = CPyTagged_IsLt(n, r1) if r2 goto L2 else goto L6 :: bool L2: L3: r3 = 4 - r4 = n < r3 :: int + r4 = CPyTagged_IsLt(n, r3) if r4 goto L4 else goto L5 :: bool L4: goto L3 @@ -271,13 +271,13 @@ L0: r2 = r1 L1: r3 = len ls :: list - r4 = r2 < r3 :: short_int + r4 = CPyTagged_IsLt(r2, r3) if r4 goto L2 else goto L4 :: bool L2: r5 = ls[r2] :: unsafe list r6 = unbox(int, r5) x = r6 - r7 = y + x :: int + r7 = CPyTagged_Add(y, x) y = r7 L3: r8 = 1 @@ -388,9 +388,9 @@ L2: r11 = d[r10] :: dict r12 = unbox(int, r11) r13 = 2 - r14 = r12 % r13 :: int + r14 = CPyTagged_Remainder(r12, r13) r15 = 0 - r16 = r14 != r15 :: int + r16 = CPyTagged_IsNe(r14, r15) if r16 goto L3 else goto L4 :: bool L3: goto L5 @@ -398,7 +398,7 @@ L4: r17 = box(int, key) r18 = d[r17] :: dict r19 = unbox(int, r18) - r20 = s + r19 :: int + r20 = CPyTagged_Add(s, r19) s = r20 L5: r21 = assert size(d) == r3 @@ -860,13 +860,13 @@ L0: r3 = r2 L1: r4 = len a :: list - r5 = r3 < r4 :: short_int + r5 = CPyTagged_IsLt(r3, r4) if r5 goto L2 else goto L4 :: bool L2: r6 = a[r3] :: unsafe list r7 = unbox(int, r6) x = r7 - r8 = i + x :: int + r8 = CPyTagged_Add(i, x) L3: r9 = 1 r10 = r1 + r9 :: short_int @@ -943,7 +943,7 @@ L0: r2 = iter b :: object L1: r3 = len a :: list - r4 = r1 < r3 :: short_int + r4 = CPyTagged_IsLt(r1, r3) if r4 goto L2 else goto L7 :: bool L2: r5 = next r2 :: object @@ -998,10 +998,10 @@ L1: if is_error(r6) goto L6 else goto L2 L2: r7 = len b :: list - r8 = r2 < r7 :: short_int + r8 = CPyTagged_IsLt(r2, r7) if r8 goto L3 else goto L6 :: bool L3: - r9 = r5 < r4 :: short_int + r9 = CPyTagged_IsLt(r5, r4) if r9 goto L4 else goto L6 :: bool L4: r10 = unbox(bool, r6) diff --git a/mypyc/test-data/irbuild-strip-asserts.test b/mypyc/test-data/irbuild-strip-asserts.test index a27b0bd29c24..bffb86ed4d3b 100644 --- a/mypyc/test-data/irbuild-strip-asserts.test +++ b/mypyc/test-data/irbuild-strip-asserts.test @@ -11,7 +11,7 @@ def g(): L0: r0 = 1 r1 = 2 - r2 = r0 + r1 :: int + r2 = CPyTagged_Add(r0, r1) r3 = box(int, r2) x = r3 return x diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index a8c31dc1cd4d..e7a8d09d73c3 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -147,7 +147,7 @@ L0: r1 = r0 L1: r2 = len xs :: tuple - r3 = r1 < r2 :: int + r3 = CPyTagged_IsLt(r1, r2) if r3 goto L2 else goto L4 :: bool L2: r4 = CPySequenceTuple_GetItem(xs, r1) diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index 255e84a05f56..c37e605b426e 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -56,7 +56,7 @@ L0: inc_ref x :: int y = x z = x - r1 = y + z :: int + r1 = CPyTagged_Add(y, z) dec_ref y :: int dec_ref z :: int return r1 @@ -82,7 +82,7 @@ L0: r1 = 2 y = r1 r2 = 1 - r3 = x == r2 :: int + r3 = CPyTagged_IsEq(x, r2) if r3 goto L3 else goto L4 :: bool L1: return x @@ -107,9 +107,9 @@ def f(a, b): r1, x, r2, y :: int L0: r0 = 1 - r1 = a + r0 :: int + r1 = CPyTagged_Add(a, r0) x = r1 - r2 = x + a :: int + r2 = CPyTagged_Add(x, a) dec_ref x :: int y = r2 return y @@ -133,7 +133,7 @@ L0: y = a r0 = 1 x = r0 - r1 = x + y :: int + r1 = CPyTagged_Add(x, y) dec_ref x :: int dec_ref y :: int return r1 @@ -222,7 +222,7 @@ def f(a): r3 :: short_int r4, y :: int L0: - r0 = a == a :: int + r0 = CPyTagged_IsEq(a, a) if r0 goto L1 else goto L2 :: bool L1: r1 = 1 @@ -235,7 +235,7 @@ L2: goto L4 L3: r3 = 1 - r4 = a + r3 :: int + r4 = CPyTagged_Add(a, r3) dec_ref a :: int y = r4 return y @@ -260,7 +260,7 @@ def f(a): r2, r3 :: short_int r4, y :: int L0: - r0 = a == a :: int + r0 = CPyTagged_IsEq(a, a) if r0 goto L1 else goto L2 :: bool L1: r1 = 2 @@ -272,7 +272,7 @@ L2: a = r2 L3: r3 = 1 - r4 = a + r3 :: int + r4 = CPyTagged_Add(a, r3) dec_ref a :: int y = r4 return y @@ -291,7 +291,7 @@ def f(a): r0 :: bool r1 :: short_int L0: - r0 = a == a :: int + r0 = CPyTagged_IsEq(a, a) if r0 goto L1 else goto L3 :: bool L1: r1 = 1 @@ -322,7 +322,7 @@ L0: inc_ref x :: int dec_ref x :: int x = x - r1 = x + a :: int + r1 = CPyTagged_Add(x, a) dec_ref x :: int dec_ref a :: int return r1 @@ -344,15 +344,15 @@ def f(a): r4, r5 :: int L0: r0 = 1 - r1 = a + r0 :: int + r1 = CPyTagged_Add(a, r0) a = r1 r2 = 1 x = r2 r3 = 1 - r4 = x + r3 :: int + r4 = CPyTagged_Add(x, r3) dec_ref x :: int x = r4 - r5 = a + x :: int + r5 = CPyTagged_Add(a, x) dec_ref a :: int dec_ref x :: int return r5 @@ -372,7 +372,7 @@ L0: r0 = 1 x = r0 r1 = 1 - r2 = x + r1 :: int + r2 = CPyTagged_Add(x, r1) dec_ref x :: int x = r2 dec_ref x :: int @@ -394,7 +394,7 @@ L0: r0 = 1 y = r0 r1 = 1 - r2 = y + r1 :: int + r2 = CPyTagged_Add(y, r1) dec_ref y :: int x = r2 dec_ref x :: int @@ -411,10 +411,10 @@ def f(a: int) -> int: def f(a): a, r0, x, r1 :: int L0: - r0 = a + a :: int + r0 = CPyTagged_Add(a, a) a = r0 x = a - r1 = x + x :: int + r1 = CPyTagged_Add(x, x) dec_ref x :: int x = r1 return x @@ -428,9 +428,9 @@ def f(a: int) -> int: def f(a): a, r0, x, r1, y :: int L0: - r0 = a + a :: int + r0 = CPyTagged_Add(a, a) x = r0 - r1 = x + x :: int + r1 = CPyTagged_Add(x, x) dec_ref x :: int y = r1 return y @@ -447,12 +447,12 @@ def f(a): y, r2, z :: int r3 :: None L0: - r0 = a + a :: int + r0 = CPyTagged_Add(a, a) x = r0 dec_ref x :: int r1 = 1 y = r1 - r2 = y + y :: int + r2 = CPyTagged_Add(y, y) dec_ref y :: int z = r2 dec_ref z :: int @@ -471,12 +471,12 @@ def f(a): x, r2 :: int r3 :: None L0: - r0 = a + a :: int + r0 = CPyTagged_Add(a, a) a = r0 dec_ref a :: int r1 = 1 x = r1 - r2 = x + x :: int + r2 = CPyTagged_Add(x, x) dec_ref x :: int x = r2 dec_ref x :: int @@ -510,17 +510,17 @@ L0: y = r1 r2 = 3 z = r2 - r3 = z == z :: int + r3 = CPyTagged_IsEq(z, z) if r3 goto L3 else goto L4 :: bool L1: return z L2: r4 = 1 a = r4 - r5 = x + y :: int + r5 = CPyTagged_Add(x, y) dec_ref x :: int dec_ref y :: int - r6 = r5 - a :: int + r6 = CPyTagged_Subtract(r5, a) dec_ref r5 :: int dec_ref a :: int return r6 @@ -557,14 +557,14 @@ L0: r1 = 0 i = r1 L1: - r2 = i <= a :: int + r2 = CPyTagged_IsLe(i, a) if r2 goto L2 else goto L4 :: bool L2: - r3 = sum + i :: int + r3 = CPyTagged_Add(sum, i) dec_ref sum :: int sum = r3 r4 = 1 - r5 = i + r4 :: int + r5 = CPyTagged_Add(i, r4) dec_ref i :: int i = r5 goto L1 @@ -584,7 +584,7 @@ def f(a): r1, r2 :: int L0: r0 = 1 - r1 = a + r0 :: int + r1 = CPyTagged_Add(a, r0) r2 = f(r1) dec_ref r1 :: int return r2 diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index ec656bcf9917..7bc0e38eaeeb 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -19,7 +19,7 @@ from mypyc.irbuild.vtable import compute_vtable from mypyc.codegen.emit import Emitter, EmitterContext from mypyc.codegen.emitfunc import generate_native_function, FunctionEmitterVisitor -from mypyc.primitives.registry import binary_ops +from mypyc.primitives.registry import binary_ops, c_binary_ops from mypyc.primitives.misc_ops import none_object_op, true_op, false_op from mypyc.primitives.list_ops import ( list_len_op, list_get_item_op, list_set_item_op, new_list_op, list_append_op @@ -264,6 +264,16 @@ def assert_emit_binary_op(self, left: Value, right: Value, expected: str) -> None: + # TODO: merge this + if op in c_binary_ops: + c_ops = c_binary_ops[op] + for c_desc in c_ops: + if (is_subtype(left.type, c_desc.arg_types[0]) + and is_subtype(right.type, c_desc.arg_types[1])): + self.assert_emit(CallC(c_desc.c_function_name, [left, right], + c_desc.return_type, c_desc.steals, c_desc.error_kind, + 55), expected) + return ops = binary_ops[op] for desc in ops: if (is_subtype(left.type, desc.arg_types[0]) From 9354d4a615fedd6c32632be87a2198fa7d4dd042 Mon Sep 17 00:00:00 2001 From: Michael Sullivan Date: Thu, 18 Jun 2020 12:55:13 -0700 Subject: [PATCH 039/138] Test commit please ignore From e50ba7e2c0c31a20ff0bf262bb0894ebd36af1c1 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 19 Jun 2020 11:41:47 +0100 Subject: [PATCH 040/138] Edits to discussion of unreachable code in the docs (#8997) Make it closer the style used elsewhere. It's also more concise now. --- docs/source/common_issues.rst | 45 +++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/docs/source/common_issues.rst b/docs/source/common_issues.rst index 704c9a23f76d..8b326408abc6 100644 --- a/docs/source/common_issues.rst +++ b/docs/source/common_issues.rst @@ -809,40 +809,46 @@ not necessary: def test(self, t: List[int]) -> Sequence[str]: # type: ignore[override] ... -Unreachable code during typechecking ------------------------------------- +Unreachable code +---------------- -Sometimes a part of the code can become unreachable, even if not immediately obvious. -It is important to note that in such cases, that part of the code will *not* be type-checked -by mypy anymore. Consider the following code snippet: +Mypy may consider some code as *unreachable*, even if it might not be +immediately obvious why. It's important to note that mypy will *not* +type check such code. Consider this example: .. code-block:: python class Foo: - bar:str = '' + bar: str = '' def bar() -> None: foo: Foo = Foo() return - x:int = 'abc' + x: int = 'abc' # Unreachable -- no error -It is trivial to notice that any statement after ``return`` is unreachable and hence mypy will -not complain about the mis-typed code below it. For a more subtle example, consider: +It's easy to see that any statement after ``return`` is unreachable, +and hence mypy will not complain about the mis-typed code below +it. For a more subtle example, consider this code: .. code-block:: python class Foo: - bar:str = '' + bar: str = '' def bar() -> None: foo: Foo = Foo() assert foo.bar is None - x:int = 'abc' + x: int = 'abc' # Unreachable -- no error -Again, mypy will not throw any errors because the type of ``foo.bar`` says it's ``str`` and never ``None``. -Hence the ``assert`` statement will always fail and the statement below will never be executed. -Note that in Python, ``None`` is not a null-reference but an object of type ``NoneType``. This can -also be demonstrated by the following: +Again, mypy will not report any errors. The type of ``foo.bar`` is +``str``, and mypy reasons that it can never be ``None``. Hence the +``assert`` statement will always fail and the statement below will +never be executed. (Note that in Python, ``None`` is not an empty +reference but an object of type ``None``.) + +In this example mypy will go on to check the last line and report an +error, since mypy thinks that the condition could be either True or +False: .. code-block:: python @@ -853,10 +859,7 @@ also be demonstrated by the following: foo: Foo = Foo() if not foo.bar: return - x:int = 'abc' - -Here mypy will go on to check the last line as well and report ``Incompatible types in assignment``. + x: int = 'abc' # Reachable -- error -If we want to let mypy warn us of such unreachable code blocks, we can use the ``--warn-unreachable`` -option. With this mypy will throw ``Statement is unreachable`` error along with the line number from -where the unreachable block starts. +If you use the :option:`--warn-unreachable ` flag, mypy will generate +an error about each unreachable code block. From 95eff27adb1fae7b1ec4a7315524ebfdcebf6e2b Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Mon, 22 Jun 2020 18:26:45 +0100 Subject: [PATCH 041/138] Update MANIFEST to include more files needed for testing (#9033) Co-authored-by: Michael R. Crusoe <1330696+mr-c@users.noreply.github.com> --- MANIFEST.in | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 611ffe249b5c..2d644e3ff980 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,14 +2,16 @@ recursive-include scripts * recursive-include test-data * recursive-include extensions * recursive-include docs * +recursive-include misc proper_plugin.py recursive-include mypy/typeshed *.py *.pyi recursive-include mypy/xml *.xsd *.xslt *.css -recursive-include mypyc/lib-rt *.c *.h *.tmpl +recursive-include mypyc/lib-rt *.c *.h *.tmpl *.py *.cc recursive-include mypyc/ir *.py recursive-include mypyc/codegen *.py recursive-include mypyc/irbuild *.py recursive-include mypyc/primitives *.py recursive-include mypyc/transform *.py +recursive-include mypyc/external *.cc *.h Makefile *.pump LICENSE README recursive-include mypyc/test *.py recursive-include mypyc/test-data *.test recursive-include mypyc/test-data/fixtures *.py *.pyi @@ -17,3 +19,5 @@ recursive-include mypyc/doc *.rst *.py *.md Makefile *.bat include mypy_bootstrap.ini include mypy_self_check.ini include LICENSE +include runtests.py +include pytest.ini From 6ee562a8f3e69ac134f8c501b79b3712c9e24bbd Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Thu, 25 Jun 2020 23:25:29 +0800 Subject: [PATCH 042/138] [mypyc] new error_kind and branch variant to handle call_negative_bool_emit (#9035) Related: mypyc/mypyc#734 and mypyc/mypyc#741. Introducing ERR_NEG_INT error_kind and NEG_INT_EXPR branch variant to support checking the return value of a call is non-negative. set.discard is used as an example. With some modifications, this would also support negative_int_emit. --- mypyc/codegen/emitfunc.py | 7 +++++-- mypyc/common.py | 3 +++ mypyc/ir/ops.py | 4 ++++ mypyc/ir/rtypes.py | 28 ++++++++++++++++++------- mypyc/irbuild/ll_builder.py | 4 ++-- mypyc/primitives/dict_ops.py | 4 ++-- mypyc/primitives/set_ops.py | 15 ++++++------- mypyc/test-data/irbuild-basic.test | 10 ++++----- mypyc/test-data/irbuild-dict.test | 4 ++-- mypyc/test-data/irbuild-set.test | 4 ++-- mypyc/test-data/irbuild-statements.test | 4 ++-- mypyc/test-data/refcount.test | 2 +- mypyc/test/test_irbuild.py | 6 ++++-- mypyc/test/test_refcount.py | 6 ++++-- mypyc/transform/exceptions.py | 5 ++++- 15 files changed, 69 insertions(+), 37 deletions(-) diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index 41c025a21234..60e74570ca3d 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -13,7 +13,7 @@ BasicBlock, Value, MethodCall, PrimitiveOp, EmitterInterface, Unreachable, NAMESPACE_STATIC, NAMESPACE_TYPE, NAMESPACE_MODULE, RaiseStandardError, CallC, LoadGlobal ) -from mypyc.ir.rtypes import RType, RTuple, is_c_int_rprimitive +from mypyc.ir.rtypes import RType, RTuple, is_int32_rprimitive, is_int64_rprimitive from mypyc.ir.func_ir import FuncIR, FuncDecl, FUNC_STATICMETHOD, FUNC_CLASSMETHOD from mypyc.ir.class_ir import ClassIR @@ -105,6 +105,9 @@ def visit_branch(self, op: Branch) -> None: if op.op == Branch.BOOL_EXPR: expr_result = self.reg(op.left) # right isn't used cond = '{}{}'.format(neg, expr_result) + elif op.op == Branch.NEG_INT_EXPR: + expr_result = self.reg(op.left) + cond = '{} < 0'.format(expr_result) elif op.op == Branch.IS_ERROR: typ = op.left.type compare = '!=' if op.negated else '==' @@ -179,7 +182,7 @@ def visit_assign(self, op: Assign) -> None: def visit_load_int(self, op: LoadInt) -> None: dest = self.reg(op) - if is_c_int_rprimitive(op.type): + if is_int32_rprimitive(op.type) or is_int64_rprimitive(op.type): self.emit_line('%s = %d;' % (dest, op.value)) else: self.emit_line('%s = %d;' % (dest, op.value * 2)) diff --git a/mypyc/common.py b/mypyc/common.py index 3ba2d4aae026..96f5e9079443 100644 --- a/mypyc/common.py +++ b/mypyc/common.py @@ -1,3 +1,4 @@ +import sys from typing import Dict, Any from typing_extensions import Final @@ -28,6 +29,8 @@ # Maximal number of subclasses for a class to trigger fast path in isinstance() checks. FAST_ISINSTANCE_MAX_SUBCLASSES = 2 # type: Final +IS_32_BIT_PLATFORM = sys.maxsize < (1 << 31) # type: Final + def decorator_helper_name(func_name: str) -> str: return '__mypyc_{}_decorator_helper__'.format(func_name) diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 78820074e2d6..dcd9f04bad5f 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -293,6 +293,8 @@ def terminated(self) -> bool: ERR_MAGIC = 1 # type: Final # Generates false (bool) on exception ERR_FALSE = 2 # type: Final +# Generates negative integer on exception +ERR_NEG_INT = 3 # type: Final # Hack: using this line number for an op will suppress it in tracebacks NO_TRACEBACK_LINE_NO = -10000 @@ -413,10 +415,12 @@ class Branch(ControlOp): BOOL_EXPR = 100 # type: Final IS_ERROR = 101 # type: Final + NEG_INT_EXPR = 102 # type: Final op_names = { BOOL_EXPR: ('%r', 'bool'), IS_ERROR: ('is_error(%r)', ''), + NEG_INT_EXPR: ('%r < 0', ''), } # type: Final def __init__(self, diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index 9615139f6461..c3dd6f3f8226 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -25,7 +25,7 @@ from typing_extensions import Final, ClassVar, TYPE_CHECKING -from mypyc.common import JsonDict, short_name +from mypyc.common import JsonDict, short_name, IS_32_BIT_PLATFORM from mypyc.namegen import NameGenerator if TYPE_CHECKING: @@ -174,7 +174,9 @@ def __init__(self, self.is_unboxed = is_unboxed self._ctype = ctype self.is_refcounted = is_refcounted - if ctype in ('CPyTagged', 'Py_ssize_t'): + # TODO: For low-level integers, they actually don't have undefined values + # we need to figure out some way to represent here. + if ctype in ('CPyTagged', 'int32_t', 'int64_t'): self.c_undefined = 'CPY_INT_TAG' elif ctype == 'PyObject *': # Boxed types use the null pointer as the error value. @@ -234,9 +236,17 @@ def __repr__(self) -> str: short_int_rprimitive = RPrimitive('short_int', is_unboxed=True, is_refcounted=False, ctype='CPyTagged') # type: Final -# low level integer (corresponds to C's 'int'). -c_int_rprimitive = RPrimitive('c_int', is_unboxed=True, is_refcounted=False, - ctype='Py_ssize_t') # type: Final +# low level integer (corresponds to C's 'int's). +int32_rprimitive = RPrimitive('int32', is_unboxed=True, is_refcounted=False, + ctype='int32_t') # type: Final +int64_rprimitive = RPrimitive('int64', is_unboxed=True, is_refcounted=False, + ctype='int64_t') # type: Final +# integer alias +c_int_rprimitive = int32_rprimitive +if IS_32_BIT_PLATFORM: + c_pyssize_t_rprimitive = int32_rprimitive +else: + c_pyssize_t_rprimitive = int64_rprimitive # Floats are represent as 'float' PyObject * values. (In the future # we'll likely switch to a more efficient, unboxed representation.) @@ -279,8 +289,12 @@ def is_short_int_rprimitive(rtype: RType) -> bool: return rtype is short_int_rprimitive -def is_c_int_rprimitive(rtype: RType) -> bool: - return rtype is c_int_rprimitive +def is_int32_rprimitive(rtype: RType) -> bool: + return rtype is int32_rprimitive + + +def is_int64_rprimitive(rtype: RType) -> bool: + return rtype is int64_rprimitive def is_float_rprimitive(rtype: RType) -> bool: diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index cdd36c1a514d..e105bb8c8fdf 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -26,7 +26,7 @@ from mypyc.ir.rtypes import ( RType, RUnion, RInstance, optional_value_type, int_rprimitive, float_rprimitive, bool_rprimitive, list_rprimitive, str_rprimitive, is_none_rprimitive, object_rprimitive, - c_int_rprimitive + c_pyssize_t_rprimitive ) from mypyc.ir.func_ir import FuncDecl, FuncSignature from mypyc.ir.class_ir import ClassIR, all_concrete_classes @@ -884,7 +884,7 @@ def _create_dict(self, # keys and values should have the same number of items size = len(keys) if size > 0: - load_size_op = self.add(LoadInt(size, -1, c_int_rprimitive)) + load_size_op = self.add(LoadInt(size, -1, c_pyssize_t_rprimitive)) # merge keys and values items = [i for t in list(zip(keys, values)) for i in t] return self.call_c(dict_build_op, [load_size_op] + items, line) diff --git a/mypyc/primitives/dict_ops.py b/mypyc/primitives/dict_ops.py index ee780f819372..e32a33c94ce7 100644 --- a/mypyc/primitives/dict_ops.py +++ b/mypyc/primitives/dict_ops.py @@ -5,7 +5,7 @@ from mypyc.ir.ops import EmitterInterface, ERR_FALSE, ERR_MAGIC, ERR_NEVER from mypyc.ir.rtypes import ( dict_rprimitive, object_rprimitive, bool_rprimitive, int_rprimitive, - list_rprimitive, dict_next_rtuple_single, dict_next_rtuple_pair, c_int_rprimitive + list_rprimitive, dict_next_rtuple_single, dict_next_rtuple_pair, c_pyssize_t_rprimitive ) from mypyc.primitives.registry import ( @@ -99,7 +99,7 @@ # Positional argument is the number of key-value pairs # Variable arguments are (key1, value1, ..., keyN, valueN). dict_build_op = c_custom_op( - arg_types=[c_int_rprimitive], + arg_types=[c_pyssize_t_rprimitive], return_type=dict_rprimitive, c_function_name='CPyDict_Build', error_kind=ERR_MAGIC, diff --git a/mypyc/primitives/set_ops.py b/mypyc/primitives/set_ops.py index 6795a19651a9..a81b309b944b 100644 --- a/mypyc/primitives/set_ops.py +++ b/mypyc/primitives/set_ops.py @@ -4,8 +4,10 @@ func_op, method_op, binary_op, simple_emit, negative_int_emit, call_negative_bool_emit, c_function_op, c_method_op ) -from mypyc.ir.ops import ERR_MAGIC, ERR_FALSE, ERR_NEVER, EmitterInterface -from mypyc.ir.rtypes import object_rprimitive, bool_rprimitive, set_rprimitive, int_rprimitive +from mypyc.ir.ops import ERR_MAGIC, ERR_FALSE, ERR_NEVER, ERR_NEG_INT, EmitterInterface +from mypyc.ir.rtypes import ( + object_rprimitive, bool_rprimitive, set_rprimitive, int_rprimitive, c_int_rprimitive +) from typing import List @@ -70,13 +72,12 @@ def emit_len(emitter: EmitterInterface, args: List[str], dest: str) -> None: error_kind=ERR_FALSE) # set.discard(obj) -method_op( +c_method_op( name='discard', arg_types=[set_rprimitive, object_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_FALSE, - emit=call_negative_bool_emit('PySet_Discard') -) + return_type=c_int_rprimitive, + c_function_name='PySet_Discard', + error_kind=ERR_NEG_INT) # set.add(obj) set_add_op = method_op( diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 1ae55b75940b..8f2f908c80ff 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -1088,7 +1088,7 @@ def call_python_function_with_keyword_arg(x): r1 :: object r2 :: str r3 :: tuple - r4 :: c_int + r4 :: native_int r5 :: object r6 :: dict r7 :: object @@ -1113,7 +1113,7 @@ def call_python_method_with_keyword_args(xs, first, second): r3 :: str r4 :: object r5 :: tuple - r6 :: c_int + r6 :: native_int r7 :: object r8 :: dict r9 :: object @@ -1122,7 +1122,7 @@ def call_python_method_with_keyword_args(xs, first, second): r12 :: object r13, r14 :: str r15 :: tuple - r16 :: c_int + r16 :: native_int r17, r18 :: object r19 :: dict r20 :: object @@ -1690,7 +1690,7 @@ def g(): r3 :: short_int r4 :: str r5 :: short_int - r6 :: c_int + r6 :: native_int r7, r8, r9 :: object r10, r11 :: dict r12 :: str @@ -1727,7 +1727,7 @@ def h(): r2 :: short_int r3 :: str r4 :: short_int - r5 :: c_int + r5 :: native_int r6, r7 :: object r8, r9 :: dict r10 :: str diff --git a/mypyc/test-data/irbuild-dict.test b/mypyc/test-data/irbuild-dict.test index 2381d42050e8..c6090822bf30 100644 --- a/mypyc/test-data/irbuild-dict.test +++ b/mypyc/test-data/irbuild-dict.test @@ -58,7 +58,7 @@ def f(x): x :: object r0, r1 :: short_int r2 :: str - r3 :: c_int + r3 :: native_int r4, r5 :: object r6, d :: dict r7 :: None @@ -204,7 +204,7 @@ def f(x, y): r0 :: short_int r1 :: str r2 :: short_int - r3 :: c_int + r3 :: native_int r4 :: object r5 :: dict r6 :: bool diff --git a/mypyc/test-data/irbuild-set.test b/mypyc/test-data/irbuild-set.test index f109627b0dd5..5a3babb2967f 100644 --- a/mypyc/test-data/irbuild-set.test +++ b/mypyc/test-data/irbuild-set.test @@ -141,14 +141,14 @@ def f(): r0, x :: set r1 :: short_int r2 :: object - r3 :: bool + r3 :: int32 r4 :: None L0: r0 = set x = r0 r1 = 1 r2 = box(short_int, r1) - r3 = x.discard(r2) :: set + r3 = PySet_Discard(x, r2) r4 = None return x diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index 8b9fb36c6fde..3587c8ec3c02 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -717,7 +717,7 @@ def delDict(): r1 :: short_int r2 :: str r3 :: short_int - r4 :: c_int + r4 :: native_int r5, r6 :: object r7, d :: dict r8 :: str @@ -746,7 +746,7 @@ def delDictMultiple(): r5 :: short_int r6 :: str r7 :: short_int - r8 :: c_int + r8 :: native_int r9, r10, r11, r12 :: object r13, d :: dict r14, r15 :: str diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index c37e605b426e..c356d8fa23a5 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -740,7 +740,7 @@ def g(x): r1 :: object r2 :: str r3 :: tuple - r4 :: c_int + r4 :: native_int r5 :: object r6 :: dict r7 :: object diff --git a/mypyc/test/test_irbuild.py b/mypyc/test/test_irbuild.py index 6ed57f95054f..c8606aba059e 100644 --- a/mypyc/test/test_irbuild.py +++ b/mypyc/test/test_irbuild.py @@ -6,7 +6,7 @@ from mypy.test.data import DataDrivenTestCase from mypy.errors import CompileError -from mypyc.common import TOP_LEVEL_NAME +from mypyc.common import TOP_LEVEL_NAME, IS_32_BIT_PLATFORM from mypyc.ir.func_ir import format_func from mypyc.test.testutil import ( ICODE_GEN_BUILTINS, use_custom_builtins, MypycDataSuite, build_ir_for_single_file, @@ -42,7 +42,9 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: """Perform a runtime checking transformation test case.""" with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase): expected_output = remove_comment_lines(testcase.output) - + # replace native_int with platform specific ints + int_format_str = 'int32' if IS_32_BIT_PLATFORM else 'int64' + expected_output = [s.replace('native_int', int_format_str) for s in expected_output] try: ir = build_ir_for_single_file(testcase.input, options) except CompileError as e: diff --git a/mypyc/test/test_refcount.py b/mypyc/test/test_refcount.py index 7897ebf9d044..2c026ae56afc 100644 --- a/mypyc/test/test_refcount.py +++ b/mypyc/test/test_refcount.py @@ -10,7 +10,7 @@ from mypy.test.data import DataDrivenTestCase from mypy.errors import CompileError -from mypyc.common import TOP_LEVEL_NAME +from mypyc.common import TOP_LEVEL_NAME, IS_32_BIT_PLATFORM from mypyc.ir.func_ir import format_func from mypyc.transform.refcount import insert_ref_count_opcodes from mypyc.test.testutil import ( @@ -32,7 +32,9 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: """Perform a runtime checking transformation test case.""" with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase): expected_output = remove_comment_lines(testcase.output) - + # replace native_int with platform specific ints + int_format_str = 'int32' if IS_32_BIT_PLATFORM else 'int64' + expected_output = [s.replace('native_int', int_format_str) for s in expected_output] try: ir = build_ir_for_single_file(testcase.input) except CompileError as e: diff --git a/mypyc/transform/exceptions.py b/mypyc/transform/exceptions.py index 649ad5a500dd..d1f82e56829c 100644 --- a/mypyc/transform/exceptions.py +++ b/mypyc/transform/exceptions.py @@ -13,7 +13,7 @@ from mypyc.ir.ops import ( BasicBlock, LoadErrorValue, Return, Branch, RegisterOp, ERR_NEVER, ERR_MAGIC, - ERR_FALSE, NO_TRACEBACK_LINE_NO, + ERR_FALSE, ERR_NEG_INT, NO_TRACEBACK_LINE_NO, ) from mypyc.ir.func_ir import FuncIR @@ -74,6 +74,9 @@ def split_blocks_at_errors(blocks: List[BasicBlock], # Op returns a C false value on error. variant = Branch.BOOL_EXPR negated = True + elif op.error_kind == ERR_NEG_INT: + variant = Branch.NEG_INT_EXPR + negated = False else: assert False, 'unknown error kind %d' % op.error_kind From 59f491470bfba4247dac76f96e8e560cd2d7e4b6 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Sat, 27 Jun 2020 01:29:13 +0800 Subject: [PATCH 043/138] [mypyc] handle negative_int_emit and Truncate op (#9050) Related: mypyc/mypyc#734 --- mypyc/analysis.py | 6 ++++- mypyc/codegen/emitfunc.py | 8 +++++- mypyc/ir/ops.py | 38 +++++++++++++++++++++++++++ mypyc/irbuild/ll_builder.py | 15 +++++++---- mypyc/primitives/misc_ops.py | 17 +++++++----- mypyc/primitives/registry.py | 19 ++++++++++---- mypyc/test-data/irbuild-basic.test | 20 +++++++------- mypyc/test-data/irbuild-optional.test | 32 +++++++++++----------- 8 files changed, 112 insertions(+), 43 deletions(-) diff --git a/mypyc/analysis.py b/mypyc/analysis.py index 025d5ad77c97..e9f0b63f628f 100644 --- a/mypyc/analysis.py +++ b/mypyc/analysis.py @@ -8,7 +8,8 @@ Value, ControlOp, BasicBlock, OpVisitor, Assign, LoadInt, LoadErrorValue, RegisterOp, Goto, Branch, Return, Call, Environment, Box, Unbox, Cast, Op, Unreachable, TupleGet, TupleSet, GetAttr, SetAttr, - LoadStatic, InitStatic, PrimitiveOp, MethodCall, RaiseStandardError, CallC, LoadGlobal + LoadStatic, InitStatic, PrimitiveOp, MethodCall, RaiseStandardError, CallC, LoadGlobal, + Truncate ) @@ -198,6 +199,9 @@ def visit_raise_standard_error(self, op: RaiseStandardError) -> GenAndKill: def visit_call_c(self, op: CallC) -> GenAndKill: return self.visit_register_op(op) + def visit_truncate(self, op: Truncate) -> GenAndKill: + return self.visit_register_op(op) + def visit_load_global(self, op: LoadGlobal) -> GenAndKill: return self.visit_register_op(op) diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index 60e74570ca3d..ef88b8c21305 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -11,7 +11,7 @@ OpVisitor, Goto, Branch, Return, Assign, LoadInt, LoadErrorValue, GetAttr, SetAttr, LoadStatic, InitStatic, TupleGet, TupleSet, Call, IncRef, DecRef, Box, Cast, Unbox, BasicBlock, Value, MethodCall, PrimitiveOp, EmitterInterface, Unreachable, NAMESPACE_STATIC, - NAMESPACE_TYPE, NAMESPACE_MODULE, RaiseStandardError, CallC, LoadGlobal + NAMESPACE_TYPE, NAMESPACE_MODULE, RaiseStandardError, CallC, LoadGlobal, Truncate ) from mypyc.ir.rtypes import RType, RTuple, is_int32_rprimitive, is_int64_rprimitive from mypyc.ir.func_ir import FuncIR, FuncDecl, FUNC_STATICMETHOD, FUNC_CLASSMETHOD @@ -426,6 +426,12 @@ def visit_call_c(self, op: CallC) -> None: args = ', '.join(self.reg(arg) for arg in op.args) self.emitter.emit_line("{}{}({});".format(dest, op.function_name, args)) + def visit_truncate(self, op: Truncate) -> None: + dest = self.reg(op) + value = self.reg(op.src) + # for C backend the generated code are straight assignments + self.emit_line("{} = {};".format(dest, value)) + def visit_load_global(self, op: LoadGlobal) -> None: dest = self.reg(op) ann = '' diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index dcd9f04bad5f..2eb53b444130 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -1183,6 +1183,40 @@ def accept(self, visitor: 'OpVisitor[T]') -> T: return visitor.visit_call_c(self) +class Truncate(RegisterOp): + """truncate src: src_type to dst_type + + Truncate a value from type with more bits to type with less bits + + both src_type and dst_type should be non-reference counted integer types or bool + especially note that int_rprimitive is reference counted so should never be used here + """ + + error_kind = ERR_NEVER + + def __init__(self, + src: Value, + src_type: RType, + dst_type: RType, + line: int = -1) -> None: + super().__init__(line) + self.src = src + self.src_type = src_type + self.type = dst_type + + def sources(self) -> List[Value]: + return [self.src] + + def stolen(self) -> List[Value]: + return [] + + def to_str(self, env: Environment) -> str: + return env.format("%r = truncate %r: %r to %r", self, self.src, self.src_type, self.type) + + def accept(self, visitor: 'OpVisitor[T]') -> T: + return visitor.visit_truncate(self) + + class LoadGlobal(RegisterOp): """Load a global variable/pointer""" @@ -1307,6 +1341,10 @@ def visit_raise_standard_error(self, op: RaiseStandardError) -> T: def visit_call_c(self, op: CallC) -> T: raise NotImplementedError + @abstractmethod + def visit_truncate(self, op: Truncate) -> T: + raise NotImplementedError + @abstractmethod def visit_load_global(self, op: LoadGlobal) -> T: raise NotImplementedError diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index e105bb8c8fdf..9dc95ebc31b9 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -19,7 +19,7 @@ from mypyc.ir.ops import ( BasicBlock, Environment, Op, LoadInt, Value, Register, Assign, Branch, Goto, Call, Box, Unbox, Cast, GetAttr, - LoadStatic, MethodCall, PrimitiveOp, OpDescription, RegisterOp, CallC, + LoadStatic, MethodCall, PrimitiveOp, OpDescription, RegisterOp, CallC, Truncate, RaiseStandardError, Unreachable, LoadErrorValue, LoadGlobal, NAMESPACE_TYPE, NAMESPACE_MODULE, NAMESPACE_STATIC, ) @@ -707,14 +707,19 @@ def call_c(self, coerced.append(arg) target = self.add(CallC(desc.c_function_name, coerced, desc.return_type, desc.steals, desc.error_kind, line, var_arg_idx)) - if result_type and not is_runtime_subtype(target.type, result_type): + if desc.truncated_type is None: + result = target + else: + truncate = self.add(Truncate(target, desc.return_type, desc.truncated_type)) + result = truncate + if result_type and not is_runtime_subtype(result.type, result_type): if is_none_rprimitive(result_type): # Special case None return. The actual result may actually be a bool # and so we can't just coerce it. - target = self.none() + result = self.none() else: - target = self.coerce(target, result_type, line) - return target + result = self.coerce(target, result_type, line) + return result def matching_call_c(self, candidates: List[CFunctionDescription], diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py index 45e06d485812..c7c2dc03e3c7 100644 --- a/mypyc/primitives/misc_ops.py +++ b/mypyc/primitives/misc_ops.py @@ -1,9 +1,9 @@ """Miscellaneous primitive ops.""" -from mypyc.ir.ops import ERR_NEVER, ERR_MAGIC, ERR_FALSE +from mypyc.ir.ops import ERR_NEVER, ERR_MAGIC, ERR_FALSE, ERR_NEG_INT from mypyc.ir.rtypes import ( RTuple, none_rprimitive, bool_rprimitive, object_rprimitive, str_rprimitive, - int_rprimitive, dict_rprimitive + int_rprimitive, dict_rprimitive, c_int_rprimitive ) from mypyc.primitives.registry import ( name_ref_op, simple_emit, unary_op, func_op, custom_op, call_emit, name_emit, @@ -150,11 +150,14 @@ is_borrowed=True) # isinstance(obj, cls) -func_op('builtins.isinstance', - arg_types=[object_rprimitive, object_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_MAGIC, - emit=call_negative_magic_emit('PyObject_IsInstance')) +c_function_op( + name='builtins.isinstance', + arg_types=[object_rprimitive, object_rprimitive], + return_type=c_int_rprimitive, + c_function_name='PyObject_IsInstance', + error_kind=ERR_NEG_INT, + truncated_type=bool_rprimitive +) # Faster isinstance(obj, cls) that only works with native classes and doesn't perform # type checking of the type argument. diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index 388b9717eef8..edac766eef22 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -47,6 +47,7 @@ ('arg_types', List[RType]), ('return_type', RType), ('var_arg_type', Optional[RType]), + ('truncated_type', Optional[RType]), ('c_function_name', str), ('error_kind', int), ('steals', StealsDescription), @@ -350,6 +351,7 @@ def c_method_op(name: str, c_function_name: str, error_kind: int, var_arg_type: Optional[RType] = None, + truncated_type: Optional[RType] = None, steals: StealsDescription = False, priority: int = 1) -> CFunctionDescription: """Define a c function call op that replaces a method call. @@ -363,11 +365,14 @@ def c_method_op(name: str, c_function_name: name of the C function to call error_kind: how errors are represented in the result (one of ERR_*) var_arg_type: type of all variable arguments + truncated_type: type to truncated to(See Truncate for info) + if it's defined both return_type and it should be non-referenced + integer types or bool type steals: description of arguments that this steals (ref count wise) priority: if multiple ops match, the one with the highest priority is picked """ ops = c_method_call_ops.setdefault(name, []) - desc = CFunctionDescription(name, arg_types, return_type, var_arg_type, + desc = CFunctionDescription(name, arg_types, return_type, var_arg_type, truncated_type, c_function_name, error_kind, steals, priority) ops.append(desc) return desc @@ -379,6 +384,7 @@ def c_function_op(name: str, c_function_name: str, error_kind: int, var_arg_type: Optional[RType] = None, + truncated_type: Optional[RType] = None, steals: StealsDescription = False, priority: int = 1) -> CFunctionDescription: """Define a c function call op that replaces a function call. @@ -392,7 +398,7 @@ def c_function_op(name: str, arg_types: positional argument types for which this applies """ ops = c_function_ops.setdefault(name, []) - desc = CFunctionDescription(name, arg_types, return_type, var_arg_type, + desc = CFunctionDescription(name, arg_types, return_type, var_arg_type, truncated_type, c_function_name, error_kind, steals, priority) ops.append(desc) return desc @@ -404,6 +410,7 @@ def c_binary_op(name: str, c_function_name: str, error_kind: int, var_arg_type: Optional[RType] = None, + truncated_type: Optional[RType] = None, steals: StealsDescription = False, priority: int = 1) -> CFunctionDescription: """Define a c function call op for a binary operation. @@ -414,7 +421,7 @@ def c_binary_op(name: str, are expected. """ ops = c_binary_ops.setdefault(name, []) - desc = CFunctionDescription(name, arg_types, return_type, var_arg_type, + desc = CFunctionDescription(name, arg_types, return_type, var_arg_type, truncated_type, c_function_name, error_kind, steals, priority) ops.append(desc) return desc @@ -425,12 +432,13 @@ def c_custom_op(arg_types: List[RType], c_function_name: str, error_kind: int, var_arg_type: Optional[RType] = None, + truncated_type: Optional[RType] = None, steals: StealsDescription = False) -> CFunctionDescription: """Create a one-off CallC op that can't be automatically generated from the AST. Most arguments are similar to c_method_op(). """ - return CFunctionDescription('', arg_types, return_type, var_arg_type, + return CFunctionDescription('', arg_types, return_type, var_arg_type, truncated_type, c_function_name, error_kind, steals, 0) @@ -439,6 +447,7 @@ def c_unary_op(name: str, return_type: RType, c_function_name: str, error_kind: int, + truncated_type: Optional[RType] = None, steals: StealsDescription = False, priority: int = 1) -> CFunctionDescription: """Define a c function call op for an unary operation. @@ -449,7 +458,7 @@ def c_unary_op(name: str, is expected. """ ops = c_unary_ops.setdefault(name, []) - desc = CFunctionDescription(name, [arg_type], return_type, None, + desc = CFunctionDescription(name, [arg_type], return_type, None, truncated_type, c_function_name, error_kind, steals, priority) ops.append(desc) return desc diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 8f2f908c80ff..272a28e2b0ec 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -1502,19 +1502,21 @@ def main() -> None: def foo(x): x :: union[int, str] r0 :: object - r1 :: bool - r2 :: __main__.B - r3 :: __main__.A + r1 :: int32 + r2 :: bool + r3 :: __main__.B + r4 :: __main__.A L0: r0 = int - r1 = isinstance x, r0 - if r1 goto L1 else goto L2 :: bool + r1 = PyObject_IsInstance(x, r0) + r2 = truncate r1: int32 to builtins.bool + if r2 goto L1 else goto L2 :: bool L1: - r2 = B() - return r2 -L2: - r3 = A() + r3 = B() return r3 +L2: + r4 = A() + return r4 def main(): r0 :: short_int r1 :: object diff --git a/mypyc/test-data/irbuild-optional.test b/mypyc/test-data/irbuild-optional.test index 5d104e1c553b..b6d5f5f8ccb3 100644 --- a/mypyc/test-data/irbuild-optional.test +++ b/mypyc/test-data/irbuild-optional.test @@ -299,25 +299,27 @@ def f(x: Union[int, A]) -> int: def f(x): x :: union[int, __main__.A] r0 :: object - r1 :: bool - r2 :: int - r3 :: short_int - r4 :: int - r5 :: __main__.A - r6 :: int + r1 :: int32 + r2 :: bool + r3 :: int + r4 :: short_int + r5 :: int + r6 :: __main__.A + r7 :: int L0: r0 = int - r1 = isinstance x, r0 - if r1 goto L1 else goto L2 :: bool + r1 = PyObject_IsInstance(x, r0) + r2 = truncate r1: int32 to builtins.bool + if r2 goto L1 else goto L2 :: bool L1: - r2 = unbox(int, x) - r3 = 1 - r4 = CPyTagged_Add(r2, r3) - return r4 + r3 = unbox(int, x) + r4 = 1 + r5 = CPyTagged_Add(r3, r4) + return r5 L2: - r5 = cast(__main__.A, x) - r6 = r5.a - return r6 + r6 = cast(__main__.A, x) + r7 = r6.a + return r7 L3: unreachable From 8219564d365ba29dda8c4383594d9db91f26feec Mon Sep 17 00:00:00 2001 From: Brian Mboya Date: Sat, 27 Jun 2020 00:20:25 +0300 Subject: [PATCH 044/138] ignore all vim temporary files (#9022) --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3b454ed5bdbb..fb1fa11acf8a 100644 --- a/.gitignore +++ b/.gitignore @@ -23,9 +23,12 @@ dmypy.json # IDEs .idea -*.swp .vscode +# vim temporary files +.*.sw? +*.sw? + # Operating Systems .DS_Store From 650596a628b7726a4a5f4e600f1fa487b8036d11 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sun, 28 Jun 2020 12:25:49 -0700 Subject: [PATCH 045/138] sync typeshed (#9064) Prepare for merge dance on https://github.com/python/typeshed/pull/4258 --- mypy/typeshed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/typeshed b/mypy/typeshed index df6136c4ac0b..0dd3258ed2e8 160000 --- a/mypy/typeshed +++ b/mypy/typeshed @@ -1 +1 @@ -Subproject commit df6136c4ac0bd0751699a7eeeff36e7486a90254 +Subproject commit 0dd3258ed2e8e5d16bc8575cddb91d66fae46389 From b67151b80f6611e805cfbfb9444d69a0b1634b03 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 28 Jun 2020 12:27:35 -0700 Subject: [PATCH 046/138] pythoneval.test: update for typeshed change (#9037) Change this so we can land https://github.com/python/typeshed/pull/4258 --- test-data/unit/pythoneval.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index a37cf2959d02..e3eaf8a00ff3 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1083,8 +1083,8 @@ _testTypedDictGet.py:8: note: Revealed type is 'builtins.str' _testTypedDictGet.py:9: error: TypedDict "D" has no key 'z' _testTypedDictGet.py:10: error: All overload variants of "get" of "Mapping" require at least one argument _testTypedDictGet.py:10: note: Possible overload variants: -_testTypedDictGet.py:10: note: def get(self, k: str) -> object -_testTypedDictGet.py:10: note: def [_T] get(self, k: str, default: object) -> object +_testTypedDictGet.py:10: note: def get(self, key: str) -> object +_testTypedDictGet.py:10: note: def [_T] get(self, key: str, default: object) -> object _testTypedDictGet.py:12: note: Revealed type is 'builtins.object*' [case testTypedDictMappingMethods] From 007b7af9f96df9d23744d93fdf54e83bcdc01630 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sun, 28 Jun 2020 12:54:12 -0700 Subject: [PATCH 047/138] sync typeshed (#9065) --- mypy/typeshed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/typeshed b/mypy/typeshed index 0dd3258ed2e8..fe58699ca5c9 160000 --- a/mypy/typeshed +++ b/mypy/typeshed @@ -1 +1 @@ -Subproject commit 0dd3258ed2e8e5d16bc8575cddb91d66fae46389 +Subproject commit fe58699ca5c9ee4838378adb88aaf9323e9bbcf0 From eae1860bef0a6fe06753459bf97633a41e789ed7 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Tue, 30 Jun 2020 18:03:10 +0800 Subject: [PATCH 048/138] [mypyc] Support argument reordering in CallC (#9067) Related: mypyc/mypyc#734. Support argument reordering via adding a new field to the description. It will solve the difference in args ordering between python syntax and C calling requirement (mostly with in ops). It should never be used together with variable args. --- mypyc/irbuild/ll_builder.py | 4 + mypyc/primitives/dict_ops.py | 25 +++--- mypyc/primitives/registry.py | 21 +++-- mypyc/test-data/irbuild-dict.test | 124 ++++++++++++++++-------------- mypyc/test/test_emitfunc.py | 15 ++-- 5 files changed, 105 insertions(+), 84 deletions(-) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 9dc95ebc31b9..44ee62feddf9 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -697,6 +697,10 @@ def call_c(self, arg = args[i] arg = self.coerce(arg, formal_type, line) coerced.append(arg) + # reorder args if necessary + if desc.ordering is not None: + assert desc.var_arg_type is None + coerced = [coerced[i] for i in desc.ordering] # coerce any var_arg var_arg_idx = -1 if desc.var_arg_type is not None: diff --git a/mypyc/primitives/dict_ops.py b/mypyc/primitives/dict_ops.py index e32a33c94ce7..1ee2e40920b7 100644 --- a/mypyc/primitives/dict_ops.py +++ b/mypyc/primitives/dict_ops.py @@ -2,16 +2,17 @@ from typing import List -from mypyc.ir.ops import EmitterInterface, ERR_FALSE, ERR_MAGIC, ERR_NEVER +from mypyc.ir.ops import EmitterInterface, ERR_FALSE, ERR_MAGIC, ERR_NEVER, ERR_NEG_INT from mypyc.ir.rtypes import ( dict_rprimitive, object_rprimitive, bool_rprimitive, int_rprimitive, - list_rprimitive, dict_next_rtuple_single, dict_next_rtuple_pair, c_pyssize_t_rprimitive + list_rprimitive, dict_next_rtuple_single, dict_next_rtuple_pair, c_pyssize_t_rprimitive, + c_int_rprimitive ) from mypyc.primitives.registry import ( - name_ref_op, method_op, binary_op, func_op, custom_op, - simple_emit, negative_int_emit, call_emit, call_negative_bool_emit, - name_emit, c_custom_op, c_method_op, c_function_op + name_ref_op, method_op, func_op, custom_op, + simple_emit, call_emit, call_negative_bool_emit, + name_emit, c_custom_op, c_method_op, c_function_op, c_binary_op ) @@ -39,12 +40,14 @@ emit=call_negative_bool_emit('CPyDict_SetItem')) # key in dict -binary_op(op='in', - arg_types=[object_rprimitive, dict_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_MAGIC, - format_str='{dest} = {args[0]} in {args[1]} :: dict', - emit=negative_int_emit('{dest} = PyDict_Contains({args[1]}, {args[0]});')) +c_binary_op( + name='in', + arg_types=[object_rprimitive, dict_rprimitive], + return_type=c_int_rprimitive, + c_function_name='PyDict_Contains', + error_kind=ERR_NEG_INT, + truncated_type=bool_rprimitive, + ordering=[1, 0]) # dict1.update(dict2) dict_update_op = method_op( diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index edac766eef22..726f3b28c5ad 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -51,6 +51,7 @@ ('c_function_name', str), ('error_kind', int), ('steals', StealsDescription), + ('ordering', Optional[List[int]]), ('priority', int)]) # A description for C load operations including LoadGlobal and LoadAddress @@ -352,6 +353,7 @@ def c_method_op(name: str, error_kind: int, var_arg_type: Optional[RType] = None, truncated_type: Optional[RType] = None, + ordering: Optional[List[int]] = None, steals: StealsDescription = False, priority: int = 1) -> CFunctionDescription: """Define a c function call op that replaces a method call. @@ -368,12 +370,17 @@ def c_method_op(name: str, truncated_type: type to truncated to(See Truncate for info) if it's defined both return_type and it should be non-referenced integer types or bool type + ordering: optional ordering of the arguments, if defined, + reorders the arguments accordingly. + should never be used together with var_arg_type. + all the other arguments(such as arg_types) are in the order + accepted by the python syntax(before reordering) steals: description of arguments that this steals (ref count wise) priority: if multiple ops match, the one with the highest priority is picked """ ops = c_method_call_ops.setdefault(name, []) desc = CFunctionDescription(name, arg_types, return_type, var_arg_type, truncated_type, - c_function_name, error_kind, steals, priority) + c_function_name, error_kind, steals, ordering, priority) ops.append(desc) return desc @@ -385,6 +392,7 @@ def c_function_op(name: str, error_kind: int, var_arg_type: Optional[RType] = None, truncated_type: Optional[RType] = None, + ordering: Optional[List[int]] = None, steals: StealsDescription = False, priority: int = 1) -> CFunctionDescription: """Define a c function call op that replaces a function call. @@ -399,7 +407,7 @@ def c_function_op(name: str, """ ops = c_function_ops.setdefault(name, []) desc = CFunctionDescription(name, arg_types, return_type, var_arg_type, truncated_type, - c_function_name, error_kind, steals, priority) + c_function_name, error_kind, steals, ordering, priority) ops.append(desc) return desc @@ -411,6 +419,7 @@ def c_binary_op(name: str, error_kind: int, var_arg_type: Optional[RType] = None, truncated_type: Optional[RType] = None, + ordering: Optional[List[int]] = None, steals: StealsDescription = False, priority: int = 1) -> CFunctionDescription: """Define a c function call op for a binary operation. @@ -422,7 +431,7 @@ def c_binary_op(name: str, """ ops = c_binary_ops.setdefault(name, []) desc = CFunctionDescription(name, arg_types, return_type, var_arg_type, truncated_type, - c_function_name, error_kind, steals, priority) + c_function_name, error_kind, steals, ordering, priority) ops.append(desc) return desc @@ -433,13 +442,14 @@ def c_custom_op(arg_types: List[RType], error_kind: int, var_arg_type: Optional[RType] = None, truncated_type: Optional[RType] = None, + ordering: Optional[List[int]] = None, steals: StealsDescription = False) -> CFunctionDescription: """Create a one-off CallC op that can't be automatically generated from the AST. Most arguments are similar to c_method_op(). """ return CFunctionDescription('', arg_types, return_type, var_arg_type, truncated_type, - c_function_name, error_kind, steals, 0) + c_function_name, error_kind, steals, ordering, 0) def c_unary_op(name: str, @@ -448,6 +458,7 @@ def c_unary_op(name: str, c_function_name: str, error_kind: int, truncated_type: Optional[RType] = None, + ordering: Optional[List[int]] = None, steals: StealsDescription = False, priority: int = 1) -> CFunctionDescription: """Define a c function call op for an unary operation. @@ -459,7 +470,7 @@ def c_unary_op(name: str, """ ops = c_unary_ops.setdefault(name, []) desc = CFunctionDescription(name, [arg_type], return_type, None, truncated_type, - c_function_name, error_kind, steals, priority) + c_function_name, error_kind, steals, ordering, priority) ops.append(desc) return desc diff --git a/mypyc/test-data/irbuild-dict.test b/mypyc/test-data/irbuild-dict.test index c6090822bf30..5844d12ed095 100644 --- a/mypyc/test-data/irbuild-dict.test +++ b/mypyc/test-data/irbuild-dict.test @@ -86,18 +86,20 @@ def f(d): d :: dict r0 :: short_int r1 :: object - r2, r3, r4 :: bool + r2 :: int32 + r3, r4, r5 :: bool L0: r0 = 4 r1 = box(short_int, r0) - r2 = r1 in d :: dict - if r2 goto L1 else goto L2 :: bool + r2 = PyDict_Contains(d, r1) + r3 = truncate r2: int32 to builtins.bool + if r3 goto L1 else goto L2 :: bool L1: - r3 = True - return r3 -L2: - r4 = False + r4 = True return r4 +L2: + r5 = False + return r5 L3: unreachable @@ -113,19 +115,21 @@ def f(d): d :: dict r0 :: short_int r1 :: object - r2, r3, r4, r5 :: bool + r2 :: int32 + r3, r4, r5, r6 :: bool L0: r0 = 4 r1 = box(short_int, r0) - r2 = r1 in d :: dict - r3 = !r2 - if r3 goto L1 else goto L2 :: bool + r2 = PyDict_Contains(d, r1) + r3 = truncate r2: int32 to builtins.bool + r4 = !r3 + if r4 goto L1 else goto L2 :: bool L1: - r4 = True - return r4 -L2: - r5 = False + r5 = True return r5 +L2: + r6 = False + return r6 L3: unreachable @@ -242,20 +246,21 @@ def print_dict_methods(d1, d2): r7 :: object v, r8 :: int r9 :: object - r10 :: bool - r11 :: None - r12, r13 :: bool - r14, r15 :: short_int - r16 :: int - r17 :: object - r18 :: tuple[bool, int, object, object] - r19 :: int - r20 :: bool - r21, r22 :: object - r23, r24, k :: int - r25, r26, r27, r28, r29 :: object - r30, r31, r32 :: bool - r33 :: None + r10 :: int32 + r11 :: bool + r12 :: None + r13, r14 :: bool + r15, r16 :: short_int + r17 :: int + r18 :: object + r19 :: tuple[bool, int, object, object] + r20 :: int + r21 :: bool + r22, r23 :: object + r24, r25, k :: int + r26, r27, r28, r29, r30 :: object + r31, r32, r33 :: bool + r34 :: None L0: r0 = 0 r1 = r0 @@ -272,47 +277,48 @@ L2: r8 = unbox(int, r7) v = r8 r9 = box(int, v) - r10 = r9 in d2 :: dict - if r10 goto L3 else goto L4 :: bool + r10 = PyDict_Contains(d2, r9) + r11 = truncate r10: int32 to builtins.bool + if r11 goto L3 else goto L4 :: bool L3: - r11 = None - return r11 + r12 = None + return r12 L4: L5: - r12 = assert size(d1) == r2 + r13 = assert size(d1) == r2 goto L1 L6: - r13 = no_err_occurred + r14 = no_err_occurred L7: - r14 = 0 - r15 = r14 - r16 = len d2 :: dict - r17 = item_iter d2 :: dict + r15 = 0 + r16 = r15 + r17 = len d2 :: dict + r18 = item_iter d2 :: dict L8: - r18 = next_item r17, offset=r15 - r19 = r18[1] - r15 = r19 - r20 = r18[0] - if r20 goto L9 else goto L11 :: bool + r19 = next_item r18, offset=r16 + r20 = r19[1] + r16 = r20 + r21 = r19[0] + if r21 goto L9 else goto L11 :: bool L9: - r21 = r18[2] - r22 = r18[3] - r23 = unbox(int, r21) + r22 = r19[2] + r23 = r19[3] r24 = unbox(int, r22) - k = r23 - v = r24 - r25 = box(int, k) - r26 = d2[r25] :: dict - r27 = box(int, v) - r28 = r26 += r27 - r29 = box(int, k) - r30 = d2.__setitem__(r29, r28) :: dict + r25 = unbox(int, r23) + k = r24 + v = r25 + r26 = box(int, k) + r27 = d2[r26] :: dict + r28 = box(int, v) + r29 = r27 += r28 + r30 = box(int, k) + r31 = d2.__setitem__(r30, r29) :: dict L10: - r31 = assert size(d2) == r16 + r32 = assert size(d2) == r17 goto L8 L11: - r32 = no_err_occurred + r33 = no_err_occurred L12: - r33 = None - return r33 + r34 = None + return r34 diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index 7bc0e38eaeeb..5277427a95b2 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -235,12 +235,7 @@ def test_new_dict(self) -> None: def test_dict_contains(self) -> None: self.assert_emit_binary_op( 'in', self.b, self.o, self.d, - """int __tmp1 = PyDict_Contains(cpy_r_d, cpy_r_o); - if (__tmp1 < 0) - cpy_r_r0 = 2; - else - cpy_r_r0 = __tmp1; - """) + """cpy_r_r0 = PyDict_Contains(cpy_r_d, cpy_r_o);""") def assert_emit(self, op: Op, expected: str) -> None: self.emitter.fragments = [] @@ -270,9 +265,11 @@ def assert_emit_binary_op(self, for c_desc in c_ops: if (is_subtype(left.type, c_desc.arg_types[0]) and is_subtype(right.type, c_desc.arg_types[1])): - self.assert_emit(CallC(c_desc.c_function_name, [left, right], - c_desc.return_type, c_desc.steals, c_desc.error_kind, - 55), expected) + args = [left, right] + if c_desc.ordering is not None: + args = [args[i] for i in c_desc.ordering] + self.assert_emit(CallC(c_desc.c_function_name, args, c_desc.return_type, + c_desc.steals, c_desc.error_kind, 55), expected) return ops = binary_ops[op] for desc in ops: From a04bdbfec48796afa20049c9d419d6cc5ecbeb7e Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Thu, 2 Jul 2020 17:57:43 +0800 Subject: [PATCH 049/138] [mypyc] Support ERR_ALWAYS (#9073) Related to mypyc/mypyc#734, with a focus on exceptions related ops. This PR adds a new error kind: ERR_ALWAYS, which indicates the op always fails. It adds temporary false value to ensure such behavior in the exception handling transform and makes the raise op void. --- mypyc/codegen/emitfunc.py | 26 ++++++++----- mypyc/ir/ops.py | 7 +++- mypyc/irbuild/statement.py | 4 +- mypyc/primitives/exc_ops.py | 15 +++----- mypyc/test-data/irbuild-basic.test | 6 +-- mypyc/test-data/irbuild-statements.test | 9 ++--- mypyc/test-data/irbuild-try.test | 49 ++++++++++++------------- mypyc/transform/exceptions.py | 25 ++++++++++--- 8 files changed, 79 insertions(+), 62 deletions(-) diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index ef88b8c21305..6d6b46b277f5 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -130,15 +130,7 @@ def visit_branch(self, op: Branch) -> None: self.emit_line('if ({}) {{'.format(cond)) - if op.traceback_entry is not None: - globals_static = self.emitter.static_name('globals', self.module_name) - self.emit_line('CPy_AddTraceback("%s", "%s", %d, %s);' % ( - self.source_path.replace("\\", "\\\\"), - op.traceback_entry[0], - op.traceback_entry[1], - globals_static)) - if DEBUG_ERRORS: - self.emit_line('assert(PyErr_Occurred() != NULL && "failure w/o err!");') + self.emit_traceback(op) self.emit_lines( 'goto %s;' % self.label(op.true), @@ -422,7 +414,10 @@ def visit_raise_standard_error(self, op: RaiseStandardError) -> None: self.emitter.emit_line('{} = 0;'.format(self.reg(op))) def visit_call_c(self, op: CallC) -> None: - dest = self.get_dest_assign(op) + if op.is_void: + dest = '' + else: + dest = self.get_dest_assign(op) args = ', '.join(self.reg(arg) for arg in op.args) self.emitter.emit_line("{}{}({});".format(dest, op.function_name, args)) @@ -472,3 +467,14 @@ def emit_dec_ref(self, dest: str, rtype: RType, is_xdec: bool) -> None: def emit_declaration(self, line: str) -> None: self.declarations.emit_line(line) + + def emit_traceback(self, op: Branch) -> None: + if op.traceback_entry is not None: + globals_static = self.emitter.static_name('globals', self.module_name) + self.emit_line('CPy_AddTraceback("%s", "%s", %d, %s);' % ( + self.source_path.replace("\\", "\\\\"), + op.traceback_entry[0], + op.traceback_entry[1], + globals_static)) + if DEBUG_ERRORS: + self.emit_line('assert(PyErr_Occurred() != NULL && "failure w/o err!");') diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 2eb53b444130..0344d49af72a 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -295,6 +295,8 @@ def terminated(self) -> bool: ERR_FALSE = 2 # type: Final # Generates negative integer on exception ERR_NEG_INT = 3 # type: Final +# Always fails +ERR_ALWAYS = 4 # type: Final # Hack: using this line number for an op will suppress it in tracebacks NO_TRACEBACK_LINE_NO = -10000 @@ -1167,7 +1169,10 @@ def __init__(self, def to_str(self, env: Environment) -> str: args_str = ', '.join(env.format('%r', arg) for arg in self.args) - return env.format('%r = %s(%s)', self, self.function_name, args_str) + if self.is_void: + return env.format('%s(%s)', self.function_name, args_str) + else: + return env.format('%r = %s(%s)', self, self.function_name, args_str) def sources(self) -> List[Value]: return self.args diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py index a3c65a99c7f8..1f669930c634 100644 --- a/mypyc/irbuild/statement.py +++ b/mypyc/irbuild/statement.py @@ -243,7 +243,7 @@ def transform_raise_stmt(builder: IRBuilder, s: RaiseStmt) -> None: return exc = builder.accept(s.expr) - builder.primitive_op(raise_exception_op, [exc], s.line) + builder.call_c(raise_exception_op, [exc], s.line) builder.add(Unreachable()) @@ -614,7 +614,7 @@ def transform_assert_stmt(builder: IRBuilder, a: AssertStmt) -> None: message = builder.accept(a.msg) exc_type = builder.load_module_attr_by_fullname('builtins.AssertionError', a.line) exc = builder.py_call(exc_type, [message], a.line) - builder.primitive_op(raise_exception_op, [exc], a.line) + builder.call_c(raise_exception_op, [exc], a.line) builder.add(Unreachable()) builder.activate_block(ok_block) diff --git a/mypyc/primitives/exc_ops.py b/mypyc/primitives/exc_ops.py index ea79203b8b1f..a42f8d3c0aa4 100644 --- a/mypyc/primitives/exc_ops.py +++ b/mypyc/primitives/exc_ops.py @@ -1,21 +1,18 @@ """Exception-related primitive ops.""" -from mypyc.ir.ops import ERR_NEVER, ERR_FALSE +from mypyc.ir.ops import ERR_NEVER, ERR_FALSE, ERR_ALWAYS from mypyc.ir.rtypes import bool_rprimitive, object_rprimitive, void_rtype, exc_rtuple from mypyc.primitives.registry import ( - simple_emit, call_emit, call_void_emit, call_and_fail_emit, custom_op, + simple_emit, call_emit, call_void_emit, call_and_fail_emit, custom_op, c_custom_op ) # If the argument is a class, raise an instance of the class. Otherwise, assume # that the argument is an exception object, and raise it. -# -# TODO: Making this raise conditionally is kind of hokey. -raise_exception_op = custom_op( +raise_exception_op = c_custom_op( arg_types=[object_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_FALSE, - format_str='raise_exception({args[0]}); {dest} = 0', - emit=call_and_fail_emit('CPy_Raise')) + return_type=void_rtype, + c_function_name='CPy_Raise', + error_kind=ERR_ALWAYS) # Raise StopIteration exception with the specified value (which can be NULL). set_stop_iteration_value = custom_op( diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 272a28e2b0ec..b47af32ad533 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -1322,24 +1322,22 @@ def foo(): r0 :: object r1 :: str r2, r3 :: object - r4 :: bool L0: r0 = builtins :: module r1 = unicode_1 :: static ('Exception') r2 = getattr r0, r1 r3 = py_call(r2) - raise_exception(r3); r4 = 0 + CPy_Raise(r3) unreachable def bar(): r0 :: object r1 :: str r2 :: object - r3 :: bool L0: r0 = builtins :: module r1 = unicode_1 :: static ('Exception') r2 = getattr r0, r1 - raise_exception(r2); r3 = 0 + CPy_Raise(r2) unreachable [case testModuleTopLevel_toplevel] diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index 3587c8ec3c02..d9732218f684 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -614,8 +614,7 @@ def complex_msg(x, s): r4 :: object r5 :: str r6, r7 :: object - r8 :: bool - r9 :: None + r8 :: None L0: r0 = builtins.None :: object r1 = x is not r0 @@ -629,11 +628,11 @@ L2: r5 = unicode_3 :: static ('AssertionError') r6 = getattr r4, r5 r7 = py_call(r6, s) - raise_exception(r7); r8 = 0 + CPy_Raise(r7) unreachable L3: - r9 = None - return r9 + r8 = None + return r8 [case testDelList] def delList() -> None: diff --git a/mypyc/test-data/irbuild-try.test b/mypyc/test-data/irbuild-try.test index 5df1420ce349..f5cee4864957 100644 --- a/mypyc/test-data/irbuild-try.test +++ b/mypyc/test-data/irbuild-try.test @@ -277,14 +277,13 @@ def a(b): r1 :: object r2 :: str r3, r4 :: object - r5 :: bool - r6, r7, r8 :: tuple[object, object, object] - r9 :: str - r10 :: object - r11 :: str - r12, r13 :: object - r14, r15 :: bool - r16 :: None + r5, r6, r7 :: tuple[object, object, object] + r8 :: str + r9 :: object + r10 :: str + r11, r12 :: object + r13, r14 :: bool + r15 :: None L0: L1: if b goto L2 else goto L3 :: bool @@ -294,39 +293,39 @@ L2: r2 = unicode_2 :: static ('Exception') r3 = getattr r1, r2 r4 = py_call(r3, r0) - raise_exception(r4); r5 = 0 + CPy_Raise(r4) unreachable L3: L4: L5: - r7 = :: tuple[object, object, object] - r6 = r7 + r6 = :: tuple[object, object, object] + r5 = r6 goto L7 L6: (handler for L1, L2, L3) - r8 = error_catch - r6 = r8 + r7 = error_catch + r5 = r7 L7: - r9 = unicode_3 :: static ('finally') - r10 = builtins :: module - r11 = unicode_4 :: static ('print') - r12 = getattr r10, r11 - r13 = py_call(r12, r9) - if is_error(r6) goto L9 else goto L8 + r8 = unicode_3 :: static ('finally') + r9 = builtins :: module + r10 = unicode_4 :: static ('print') + r11 = getattr r9, r10 + r12 = py_call(r11, r8) + if is_error(r5) goto L9 else goto L8 L8: - reraise_exc; r14 = 0 + reraise_exc; r13 = 0 unreachable L9: goto L13 L10: (handler for L7, L8) - if is_error(r6) goto L12 else goto L11 + if is_error(r5) goto L12 else goto L11 L11: - restore_exc_info r6 + restore_exc_info r5 L12: - r15 = keep_propagating + r14 = keep_propagating unreachable L13: - r16 = None - return r16 + r15 = None + return r15 [case testWith] from typing import Any diff --git a/mypyc/transform/exceptions.py b/mypyc/transform/exceptions.py index d1f82e56829c..755ba6091663 100644 --- a/mypyc/transform/exceptions.py +++ b/mypyc/transform/exceptions.py @@ -12,10 +12,11 @@ from typing import List, Optional from mypyc.ir.ops import ( - BasicBlock, LoadErrorValue, Return, Branch, RegisterOp, ERR_NEVER, ERR_MAGIC, - ERR_FALSE, ERR_NEG_INT, NO_TRACEBACK_LINE_NO, + BasicBlock, LoadErrorValue, Return, Branch, RegisterOp, LoadInt, ERR_NEVER, ERR_MAGIC, + ERR_FALSE, ERR_NEG_INT, ERR_ALWAYS, NO_TRACEBACK_LINE_NO, Environment ) from mypyc.ir.func_ir import FuncIR +from mypyc.ir.rtypes import bool_rprimitive def insert_exception_handling(ir: FuncIR) -> None: @@ -29,7 +30,7 @@ def insert_exception_handling(ir: FuncIR) -> None: error_label = add_handler_block(ir) break if error_label: - ir.blocks = split_blocks_at_errors(ir.blocks, error_label, ir.traceback_name) + ir.blocks = split_blocks_at_errors(ir.blocks, error_label, ir.traceback_name, ir.env) def add_handler_block(ir: FuncIR) -> BasicBlock: @@ -44,7 +45,8 @@ def add_handler_block(ir: FuncIR) -> BasicBlock: def split_blocks_at_errors(blocks: List[BasicBlock], default_error_handler: BasicBlock, - func_name: Optional[str]) -> List[BasicBlock]: + func_name: Optional[str], + env: Environment) -> List[BasicBlock]: new_blocks = [] # type: List[BasicBlock] # First split blocks on ops that may raise. @@ -60,6 +62,7 @@ def split_blocks_at_errors(blocks: List[BasicBlock], block.error_handler = None for op in ops: + target = op cur_block.ops.append(op) if isinstance(op, RegisterOp) and op.error_kind != ERR_NEVER: # Split @@ -77,14 +80,24 @@ def split_blocks_at_errors(blocks: List[BasicBlock], elif op.error_kind == ERR_NEG_INT: variant = Branch.NEG_INT_EXPR negated = False + elif op.error_kind == ERR_ALWAYS: + variant = Branch.BOOL_EXPR + negated = True + # this is a hack to represent the always fail + # semantics, using a temporary bool with value false + tmp = LoadInt(0, rtype=bool_rprimitive) + cur_block.ops.append(tmp) + env.add_op(tmp) + target = tmp else: assert False, 'unknown error kind %d' % op.error_kind # Void ops can't generate errors since error is always # indicated by a special value stored in a register. - assert not op.is_void, "void op generating errors?" + if op.error_kind != ERR_ALWAYS: + assert not op.is_void, "void op generating errors?" - branch = Branch(op, + branch = Branch(target, true_label=error_label, false_label=new_block, op=variant, From a08bbbb2fa37b8d2784dd3b194bcb1cd111bbcac Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 3 Jul 2020 17:11:13 +0100 Subject: [PATCH 050/138] Use [arg-type] code for some one-off argument type error messages (#9090) Fixes #9083. --- mypy/messages.py | 8 +++++--- test-data/unit/check-errorcodes.test | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/mypy/messages.py b/mypy/messages.py index 941c7adc3634..8b689861548f 100644 --- a/mypy/messages.py +++ b/mypy/messages.py @@ -854,7 +854,7 @@ def invalid_keyword_var_arg(self, typ: Type, is_mapping: bool, context: Context) suffix = ', not {}'.format(format_type(typ)) self.fail( 'Argument after ** must be a mapping{}'.format(suffix), - context) + context, code=codes.ARG_TYPE) def undefined_in_superclass(self, member: str, context: Context) -> None: self.fail('"{}" undefined in superclass'.format(member), context) @@ -867,7 +867,8 @@ def first_argument_for_super_must_be_type(self, actual: Type, context: Context) type_str = 'a non-type instance' else: type_str = format_type(actual) - self.fail('Argument 1 for "super" must be a type object; got {}'.format(type_str), context) + self.fail('Argument 1 for "super" must be a type object; got {}'.format(type_str), context, + code=codes.ARG_TYPE) def too_few_string_formatting_arguments(self, context: Context) -> None: self.fail('Not enough arguments for format string', context, @@ -1188,7 +1189,8 @@ def typeddict_setdefault_arguments_inconsistent( expected: Type, context: Context) -> None: msg = 'Argument 2 to "setdefault" of "TypedDict" has incompatible type {}; expected {}' - self.fail(msg.format(format_type(default), format_type(expected)), context) + self.fail(msg.format(format_type(default), format_type(expected)), context, + code=codes.ARG_TYPE) def type_arguments_not_allowed(self, context: Context) -> None: self.fail('Parameterized generics cannot be used with class or instance checks', context) diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 1639be052458..1be5bd507aea 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -748,3 +748,26 @@ def f() -> Optional[C]: f( # type: ignore[operator] ) + C() + +[case testErrorCodeSpecialArgTypeErrors] +from typing import TypedDict + +class C(TypedDict): + x: int + +c: C +c.setdefault('x', '1') # type: ignore[arg-type] + +class A: + pass + +class B(A): + def f(self) -> None: + super(1, self).foo() # type: ignore[arg-type] + +def f(**x: int) -> None: + pass + +f(**1) # type: ignore[arg-type] +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] From 17b1f9c9423321e3e6504c97035148cfd51c8436 Mon Sep 17 00:00:00 2001 From: LiuYuhui Date: Sat, 4 Jul 2020 00:46:47 +0800 Subject: [PATCH 051/138] Fix *expr in rvalue (#8827) Fixes #7779. --- mypy/checker.py | 43 ++++++++++++++++++++++++++++++-- test-data/unit/check-tuples.test | 37 +++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index b01be788aaa4..2558440168e3 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2482,8 +2482,47 @@ def check_assignment_to_multiple_lvalues(self, lvalues: List[Lvalue], rvalue: Ex # using the type of rhs, because this allowed more fine grained # control in cases like: a, b = [int, str] where rhs would get # type List[object] - - rvalues = rvalue.items + rvalues = [] # type: List[Expression] + iterable_type = None # type: Optional[Type] + last_idx = None # type: Optional[int] + for idx_rval, rval in enumerate(rvalue.items): + if isinstance(rval, StarExpr): + typs = get_proper_type(self.expr_checker.visit_star_expr(rval).type) + if isinstance(typs, TupleType): + rvalues.extend([TempNode(typ) for typ in typs.items]) + elif self.type_is_iterable(typs) and isinstance(typs, Instance): + if (iterable_type is not None + and iterable_type != self.iterable_item_type(typs)): + self.fail("Contiguous iterable with same type expected", context) + else: + if last_idx is None or last_idx + 1 == idx_rval: + rvalues.append(rval) + last_idx = idx_rval + iterable_type = self.iterable_item_type(typs) + else: + self.fail("Contiguous iterable with same type expected", context) + else: + self.fail("Invalid type '{}' for *expr (iterable expected)".format(typs), + context) + else: + rvalues.append(rval) + iterable_start = None # type: Optional[int] + iterable_end = None # type: Optional[int] + for i, rval in enumerate(rvalues): + if isinstance(rval, StarExpr): + typs = get_proper_type(self.expr_checker.visit_star_expr(rval).type) + if self.type_is_iterable(typs) and isinstance(typs, Instance): + if iterable_start is None: + iterable_start = i + iterable_end = i + if (iterable_start is not None + and iterable_end is not None + and iterable_type is not None): + iterable_num = iterable_end - iterable_start + 1 + rvalue_needed = len(lvalues) - (len(rvalues) - iterable_num) + if rvalue_needed > 0: + rvalues = rvalues[0: iterable_start] + [TempNode(iterable_type) + for i in range(rvalue_needed)] + rvalues[iterable_end + 1:] if self.check_rvalue_count_in_assignment(lvalues, len(rvalues), context): star_index = next((i for i, lv in enumerate(lvalues) if diff --git a/test-data/unit/check-tuples.test b/test-data/unit/check-tuples.test index 2ae61f885eae..1c4b537325e8 100644 --- a/test-data/unit/check-tuples.test +++ b/test-data/unit/check-tuples.test @@ -1421,3 +1421,40 @@ t6: Tuple[int, int, int, int, int, int, int, int, int, int, int, int] = (1, 2, 3 # E: Incompatible types in assignment (expression has type Tuple[int, int, ... <15 more items>], variable has type Tuple[int, int, ... <10 more items>]) [builtins fixtures/tuple.pyi] + +[case testTupleWithStarExpr] +from typing import Tuple, List +points = (1, "test") # type: Tuple[int, str] +x, y, z = *points, 0 +reveal_type(x) # N: Revealed type is 'builtins.int' +reveal_type(y) # N: Revealed type is 'builtins.str' +reveal_type(z) # N: Revealed type is 'builtins.int' + +points2 = [1,2] +x2, y2, z2= *points2, "test" + +reveal_type(x2) # N: Revealed type is 'builtins.int*' +reveal_type(y2) # N: Revealed type is 'builtins.int*' +reveal_type(z2) # N: Revealed type is 'builtins.str' + +x3, x4, y3, y4, z3 = *points, *points2, "test" + +reveal_type(x3) # N: Revealed type is 'builtins.int' +reveal_type(x4) # N: Revealed type is 'builtins.str' +reveal_type(y3) # N: Revealed type is 'builtins.int*' +reveal_type(y4) # N: Revealed type is 'builtins.int*' +reveal_type(z3) # N: Revealed type is 'builtins.str' + +x5, x6, y5, y6, z4 = *points2, *points2, "test" + +reveal_type(x5) # N: Revealed type is 'builtins.int*' +reveal_type(x6) # N: Revealed type is 'builtins.int*' +reveal_type(y5) # N: Revealed type is 'builtins.int*' +reveal_type(y6) # N: Revealed type is 'builtins.int*' +reveal_type(z4) # N: Revealed type is 'builtins.str' + +points3 = ["test1", "test2"] +x7, x8, y7, y8 = *points2, *points3 # E: Contiguous iterable with same type expected + +x9, y9, x10, y10, z5 = *points2, 1, *points2 # E: Contiguous iterable with same type expected +[builtins fixtures/tuple.pyi] From 259e0cfb65b656ce0847be7c41a3a12f7c8b00a0 Mon Sep 17 00:00:00 2001 From: Ben Wolsieffer Date: Fri, 3 Jul 2020 12:47:44 -0400 Subject: [PATCH 052/138] [mypyc] Add missing packages to setup.py (#9061) --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 2b55b60f7656..5bf74a31711f 100644 --- a/setup.py +++ b/setup.py @@ -180,7 +180,8 @@ def run(self): ext_modules=ext_modules, packages=[ 'mypy', 'mypy.test', 'mypy.server', 'mypy.plugins', 'mypy.dmypy', - 'mypyc', 'mypyc.test', + 'mypyc', 'mypyc.test', 'mypyc.codegen', 'mypyc.ir', 'mypyc.irbuild', + 'mypyc.primitives', 'mypyc.transform' ], package_data={'mypy': package_data}, scripts=['scripts/mypyc'], From b98c47edf204c9abce27aa10a99b943f3fb857dc Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Tue, 7 Jul 2020 20:08:32 +0800 Subject: [PATCH 053/138] [mypyc] Add BinaryIntOp for low-level integer operations (#9108) Related: mypyc/mypyc#741 This PR introduces BinaryIntOp to represent all low-level integer binary operations. The block-like logic described in mypyc/mypyc#743 would be handled differently, BinaryIntOp would be the building block of it. --- mypyc/analysis.py | 5 +- mypyc/codegen/emitfunc.py | 9 +++- mypyc/ir/ops.py | 68 +++++++++++++++++++++++++ mypyc/irbuild/builder.py | 3 ++ mypyc/irbuild/for_helpers.py | 25 +++++---- mypyc/irbuild/ll_builder.py | 5 +- mypyc/test-data/irbuild-basic.test | 8 +-- mypyc/test-data/irbuild-lists.test | 2 +- mypyc/test-data/irbuild-statements.test | 23 +++++---- mypyc/test-data/irbuild-tuple.test | 2 +- 10 files changed, 117 insertions(+), 33 deletions(-) diff --git a/mypyc/analysis.py b/mypyc/analysis.py index e9f0b63f628f..029056a15c38 100644 --- a/mypyc/analysis.py +++ b/mypyc/analysis.py @@ -9,7 +9,7 @@ BasicBlock, OpVisitor, Assign, LoadInt, LoadErrorValue, RegisterOp, Goto, Branch, Return, Call, Environment, Box, Unbox, Cast, Op, Unreachable, TupleGet, TupleSet, GetAttr, SetAttr, LoadStatic, InitStatic, PrimitiveOp, MethodCall, RaiseStandardError, CallC, LoadGlobal, - Truncate + Truncate, BinaryIntOp ) @@ -205,6 +205,9 @@ def visit_truncate(self, op: Truncate) -> GenAndKill: def visit_load_global(self, op: LoadGlobal) -> GenAndKill: return self.visit_register_op(op) + def visit_binary_int_op(self, op: BinaryIntOp) -> GenAndKill: + return self.visit_register_op(op) + class DefinedVisitor(BaseAnalysisVisitor): """Visitor for finding defined registers. diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index 6d6b46b277f5..b94694d8039c 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -11,7 +11,8 @@ OpVisitor, Goto, Branch, Return, Assign, LoadInt, LoadErrorValue, GetAttr, SetAttr, LoadStatic, InitStatic, TupleGet, TupleSet, Call, IncRef, DecRef, Box, Cast, Unbox, BasicBlock, Value, MethodCall, PrimitiveOp, EmitterInterface, Unreachable, NAMESPACE_STATIC, - NAMESPACE_TYPE, NAMESPACE_MODULE, RaiseStandardError, CallC, LoadGlobal, Truncate + NAMESPACE_TYPE, NAMESPACE_MODULE, RaiseStandardError, CallC, LoadGlobal, Truncate, + BinaryIntOp ) from mypyc.ir.rtypes import RType, RTuple, is_int32_rprimitive, is_int64_rprimitive from mypyc.ir.func_ir import FuncIR, FuncDecl, FUNC_STATICMETHOD, FUNC_CLASSMETHOD @@ -436,6 +437,12 @@ def visit_load_global(self, op: LoadGlobal) -> None: ann = ' /* %s */' % s self.emit_line('%s = %s;%s' % (dest, op.identifier, ann)) + def visit_binary_int_op(self, op: BinaryIntOp) -> None: + dest = self.reg(op) + lhs = self.reg(op.lhs) + rhs = self.reg(op.rhs) + self.emit_line('%s = %s %s %s;' % (dest, lhs, op.op_str[op.op], rhs)) + # Helpers def label(self, label: BasicBlock) -> str: diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 0344d49af72a..efcf7b011001 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -1252,6 +1252,70 @@ def accept(self, visitor: 'OpVisitor[T]') -> T: return visitor.visit_load_global(self) +class BinaryIntOp(RegisterOp): + """Binary operations on integer types + + These ops are low-level and will be eventually generated to simple x op y form. + The left and right values should be of low-level integer types that support those ops + """ + error_kind = ERR_NEVER + + # arithmetic + ADD = 0 # type: Final + SUB = 1 # type: Final + MUL = 2 # type: Final + DIV = 3 # type: Final + MOD = 4 # type: Final + # logical + EQ = 100 # type: Final + NEQ = 101 # type: Final + LT = 102 # type: Final + GT = 103 # type: Final + LEQ = 104 # type: Final + GEQ = 105 # type: Final + # bitwise + AND = 200 # type: Final + OR = 201 # type: Final + XOR = 202 # type: Final + LEFT_SHIFT = 203 # type: Final + RIGHT_SHIFT = 204 # type: Final + + op_str = { + ADD: '+', + SUB: '-', + MUL: '*', + DIV: '/', + MOD: '%', + EQ: '==', + NEQ: '!=', + LT: '<', + GT: '>', + LEQ: '<=', + GEQ: '>=', + AND: '&', + OR: '|', + XOR: '^', + LEFT_SHIFT: '<<', + RIGHT_SHIFT: '>>', + } # type: Final + + def __init__(self, type: RType, lhs: Value, rhs: Value, op: int, line: int = -1) -> None: + super().__init__(line) + self.type = type + self.lhs = lhs + self.rhs = rhs + self.op = op + + def sources(self) -> List[Value]: + return [self.lhs, self.rhs] + + def to_str(self, env: Environment) -> str: + return env.format('%r = %r %s %r', self, self.lhs, self.op_str[self.op], self.rhs) + + def accept(self, visitor: 'OpVisitor[T]') -> T: + return visitor.visit_binary_int_op(self) + + @trait class OpVisitor(Generic[T]): """Generic visitor over ops (uses the visitor design pattern).""" @@ -1354,6 +1418,10 @@ def visit_truncate(self, op: Truncate) -> T: def visit_load_global(self, op: LoadGlobal) -> T: raise NotImplementedError + @abstractmethod + def visit_binary_int_op(self, op: BinaryIntOp) -> T: + raise NotImplementedError + # TODO: Should this live somewhere else? LiteralsMap = Dict[Tuple[Type[object], Union[int, float, str, bytes, complex]], str] diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 17559b90bcdf..30699b5a7ac2 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -232,6 +232,9 @@ def load_module(self, name: str) -> Value: def call_c(self, desc: CFunctionDescription, args: List[Value], line: int) -> Value: return self.builder.call_c(desc, args, line) + def binary_int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: int) -> Value: + return self.builder.binary_int_op(type, lhs, rhs, op, line) + @property def environment(self) -> Environment: return self.builder.environment diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 90d5e5403de9..1c65fec05eb3 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -13,17 +13,16 @@ ) from mypyc.ir.ops import ( Value, BasicBlock, LoadInt, Branch, Register, AssignmentTarget, TupleGet, - AssignmentTargetTuple, TupleSet, OpDescription + AssignmentTargetTuple, TupleSet, OpDescription, BinaryIntOp ) from mypyc.ir.rtypes import ( RType, is_short_int_rprimitive, is_list_rprimitive, is_sequence_rprimitive, - RTuple, is_dict_rprimitive + RTuple, is_dict_rprimitive, short_int_rprimitive ) from mypyc.primitives.dict_ops import ( dict_next_key_op, dict_next_value_op, dict_next_item_op, dict_check_size_op, dict_key_iter_op, dict_value_iter_op, dict_item_iter_op ) -from mypyc.primitives.int_ops import unsafe_short_add from mypyc.primitives.list_ops import new_list_op, list_append_op, list_get_item_unsafe_op from mypyc.primitives.generic_ops import iter_op, next_op from mypyc.primitives.exc_ops import no_err_occurred_op @@ -465,10 +464,10 @@ def gen_step(self) -> None: builder = self.builder line = self.line step = 1 if not self.reverse else -1 - builder.assign(self.index_target, builder.primitive_op( - unsafe_short_add, - [builder.read(self.index_target, line), - builder.add(LoadInt(step))], line), line) + add = builder.binary_int_op(short_int_rprimitive, + builder.read(self.index_target, line), + builder.add(LoadInt(step)), BinaryIntOp.ADD, line) + builder.assign(self.index_target, add, line) class ForDictionaryCommon(ForGenerator): @@ -635,9 +634,9 @@ def gen_step(self) -> None: # short ints. if (is_short_int_rprimitive(self.start_reg.type) and is_short_int_rprimitive(self.end_reg.type)): - new_val = builder.primitive_op( - unsafe_short_add, [builder.read(self.index_reg, line), - builder.add(LoadInt(self.step))], line) + new_val = builder.binary_int_op(short_int_rprimitive, + builder.read(self.index_reg, line), + builder.add(LoadInt(self.step)), BinaryIntOp.ADD, line) else: new_val = builder.binary_op( @@ -665,9 +664,9 @@ def gen_step(self) -> None: # We can safely assume that the integer is short, since we are not going to wrap # around a 63-bit integer. # NOTE: This would be questionable if short ints could be 32 bits. - new_val = builder.primitive_op( - unsafe_short_add, [builder.read(self.index_reg, line), - builder.add(LoadInt(1))], line) + new_val = builder.binary_int_op(short_int_rprimitive, + builder.read(self.index_reg, line), + builder.add(LoadInt(1)), BinaryIntOp.ADD, line) builder.assign(self.index_reg, new_val, line) builder.assign(self.index_target, new_val, line) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 44ee62feddf9..ecc7bfccbbb5 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -21,7 +21,7 @@ Assign, Branch, Goto, Call, Box, Unbox, Cast, GetAttr, LoadStatic, MethodCall, PrimitiveOp, OpDescription, RegisterOp, CallC, Truncate, RaiseStandardError, Unreachable, LoadErrorValue, LoadGlobal, - NAMESPACE_TYPE, NAMESPACE_MODULE, NAMESPACE_STATIC, + NAMESPACE_TYPE, NAMESPACE_MODULE, NAMESPACE_STATIC, BinaryIntOp ) from mypyc.ir.rtypes import ( RType, RUnion, RInstance, optional_value_type, int_rprimitive, float_rprimitive, @@ -750,6 +750,9 @@ def matching_call_c(self, return target return None + def binary_int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: int) -> Value: + return self.add(BinaryIntOp(type, lhs, rhs, op, line)) + # Internal helpers def decompose_union_helper(self, diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index b47af32ad533..85c203dbe548 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -1917,7 +1917,7 @@ L6: r20 = r0.append(r19) :: list L7: r21 = 1 - r22 = r9 + r21 :: short_int + r22 = r9 + r21 r9 = r22 goto L1 L8: @@ -1982,7 +1982,7 @@ L6: r21 = r0.__setitem__(r19, r20) :: dict L7: r22 = 1 - r23 = r9 + r22 :: short_int + r23 = r9 + r22 r9 = r23 goto L1 L8: @@ -2032,7 +2032,7 @@ L2: z = r8 L3: r9 = 1 - r10 = r1 + r9 :: short_int + r10 = r1 + r9 r1 = r10 goto L1 L4: @@ -2058,7 +2058,7 @@ L6: r24 = r11.append(r23) :: list L7: r25 = 1 - r26 = r13 + r25 :: short_int + r26 = r13 + r25 r13 = r26 goto L5 L8: diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test index 1455f02ac05e..dcf11aeae641 100644 --- a/mypyc/test-data/irbuild-lists.test +++ b/mypyc/test-data/irbuild-lists.test @@ -195,7 +195,7 @@ L2: r8 = CPyList_SetItem(l, i, r7) L3: r9 = 1 - r10 = r2 + r9 :: short_int + r10 = r2 + r9 r2 = r10 i = r10 goto L1 diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index d9732218f684..b385edfc4f17 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -28,7 +28,7 @@ L2: x = r5 L3: r6 = 1 - r7 = r3 + r6 :: short_int + r7 = r3 + r6 r3 = r7 i = r7 goto L1 @@ -58,7 +58,7 @@ L1: L2: L3: r4 = -1 - r5 = r2 + r4 :: short_int + r5 = r2 + r4 r2 = r5 i = r5 goto L1 @@ -113,7 +113,7 @@ L2: goto L4 L3: r4 = 1 - r5 = r2 + r4 :: short_int + r5 = r2 + r4 r2 = r5 n = r5 goto L1 @@ -202,7 +202,7 @@ L1: L2: L3: r4 = 1 - r5 = r2 + r4 :: short_int + r5 = r2 + r4 r2 = r5 n = r5 goto L1 @@ -281,7 +281,7 @@ L2: y = r7 L3: r8 = 1 - r9 = r2 + r8 :: short_int + r9 = r2 + r8 r2 = r9 goto L1 L4: @@ -868,11 +868,11 @@ L2: r8 = CPyTagged_Add(i, x) L3: r9 = 1 - r10 = r1 + r9 :: short_int + r10 = r1 + r9 r1 = r10 i = r10 r11 = 1 - r12 = r3 + r11 :: short_int + r12 = r3 + r11 r3 = r12 goto L1 L4: @@ -901,7 +901,7 @@ L2: n = r4 L3: r5 = 1 - r6 = r1 + r5 :: short_int + r6 = r1 + r5 r1 = r6 i = r6 goto L1 @@ -961,7 +961,7 @@ L4: L5: L6: r11 = 1 - r12 = r1 + r11 :: short_int + r12 = r1 + r11 r1 = r12 goto L1 L7: @@ -1012,10 +1012,10 @@ L4: x = r13 L5: r14 = 1 - r15 = r2 + r14 :: short_int + r15 = r2 + r14 r2 = r15 r16 = 1 - r17 = r5 + r16 :: short_int + r17 = r5 + r16 r5 = r17 z = r17 goto L1 @@ -1024,3 +1024,4 @@ L6: L7: r19 = None return r19 + diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index e7a8d09d73c3..0d9f255dc606 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -155,7 +155,7 @@ L2: x = r5 L3: r6 = 1 - r7 = r1 + r6 :: short_int + r7 = r1 + r6 r1 = r7 goto L1 L4: From ffdbeb3d47ccb2d9691b36993be0a18e0718d997 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 7 Jul 2020 13:35:28 +0100 Subject: [PATCH 054/138] [mypyc] Various documentation updates (#9098) Includes many changes (this list is not exhaustive): * Document how to pass mypy options. * Document some additional primitives. * Protocols are now supported. * General wordsmithing. --- mypyc/doc/dict_operations.rst | 4 + mypyc/doc/differences_from_python.rst | 6 - mypyc/doc/getting_started.rst | 91 +++++++++----- mypyc/doc/introduction.rst | 166 ++++++++++++-------------- mypyc/doc/str_operations.rst | 1 + 5 files changed, 145 insertions(+), 123 deletions(-) diff --git a/mypyc/doc/dict_operations.rst b/mypyc/doc/dict_operations.rst index 8fccc0d4fc6d..89dd8149a970 100644 --- a/mypyc/doc/dict_operations.rst +++ b/mypyc/doc/dict_operations.rst @@ -33,12 +33,16 @@ Statements ---------- * ``d[key] = value`` +* ``for key in d:`` Methods ------- * ``d.get(key)`` * ``d.get(key, default)`` +* ``d.keys()`` +* ``d.values()`` +* ``d.items()`` * ``d1.update(d2: dict)`` * ``d.update(x: Iterable)`` diff --git a/mypyc/doc/differences_from_python.rst b/mypyc/doc/differences_from_python.rst index cc7136873b6a..3bebf4049e7c 100644 --- a/mypyc/doc/differences_from_python.rst +++ b/mypyc/doc/differences_from_python.rst @@ -262,12 +262,6 @@ Descriptors Native classes can't contain arbitrary descriptors. Properties, static methods and class methods are supported. -Defining protocols -****************** - -Protocols can't be defined in compiled code. You can use protocols -defined elsewhere, however. - Stack introspection ******************* diff --git a/mypyc/doc/getting_started.rst b/mypyc/doc/getting_started.rst index 7860b8390d54..8d3bf5bba662 100644 --- a/mypyc/doc/getting_started.rst +++ b/mypyc/doc/getting_started.rst @@ -50,10 +50,10 @@ On some systems you need to use this instead: $ python -m pip install -U mypy -Compile and run a program -------------------------- +Example program +--------------- -Let's now compile a classic micro-benchmark, recursive fibonacci. Save +Let's start with a classic micro-benchmark, recursive fibonacci. Save this file as ``fib.py``: .. code-block:: python @@ -70,8 +70,8 @@ this file as ``fib.py``: fib(32) print(time.time() - t0) -Note that we gave ``fib`` a type annotation. Without it, performance -won't be as impressive after compilation. +Note that we gave the ``fib`` function a type annotation. Without it, +performance won't be as impressive after compilation. .. note:: @@ -81,6 +81,9 @@ won't be as impressive after compilation. mypy to perform type checking and type inference, so some familiarity with mypy is very useful. +Compiling and running +--------------------- + We can run ``fib.py`` as a regular, interpreted program using CPython: .. code-block:: console @@ -114,9 +117,12 @@ After compilation, the program is about 10x faster. Nice! ``__name__`` in ``fib.py`` would now be ``"fib"``, not ``"__main__"``. +You can also pass most +`mypy command line options `_ +to ``mypyc``. -Delete compiled binary ----------------------- +Deleting compiled binary +------------------------ You can manually delete the C extension to get back to an interpreted version (this example works on Linux): @@ -125,11 +131,11 @@ version (this example works on Linux): $ rm fib.*.so -Compile using setup.py ----------------------- +Using setup.py +-------------- You can also use ``setup.py`` to compile modules using mypyc. Here is an -example:: +example ``setup.py`` file:: from setuptools import setup @@ -154,40 +160,67 @@ Now you can build a wheel (.whl) file for the package:: The wheel is created under ``dist/``. +You can also compile the C extensions in-place, in the current directory (similar +to using ``mypyc`` to compile modules):: + + python3 setup.py build_ext --inplace + +You can include most `mypy command line options +`_ in the +list of arguments passed to ``mypycify()``. For example, here we use +the ``--disallow-untyped-defs`` flag to require that all functions +have type annotations:: + + ... + setup( + name='frobnicate', + packages=['frobnicate'], + ext_modules=mypycify([ + '--disallow-untyped-defs', # Pass a mypy flag + 'frobnicate.py', + ]), + ) + +.. note: + + You may be tempted to use `--check-untyped-defs + `_ + to type check functions without type annotations. Note that this + may reduce performance, due to many transitions between type-checked and unchecked + code. + Recommended workflow -------------------- A simple way to use mypyc is to always compile your code after any -code changes, but this can get tedious. Instead, you may prefer -another workflow, where you compile code less often. The following -development workflow has worked very well for developing mypy and -mypyc, and we recommend that you to try it out: +code changes, but this can get tedious, especially if you have a lot +of code. Instead, you can do most development in interpreted mode. +This development workflow has worked smoothly for developing mypy and +mypyc (often we forget that we aren't working on a vanilla Python +project): -1. During development, use interpreted mode. This allows a very fast - edit-run cycle, since you don't need to wait for mypyc compilation. +1. During development, use interpreted mode. This gives you a fast + edit-run cycle. 2. Use type annotations liberally and use mypy to type check your code during development. Mypy and tests can find most errors that would - break your compiled version, if you have good annotation - coverage. (Running mypy is faster than compiling, and you can run - your code even if there are mypy errors.) + break your compiled code, if you have good type annotation + coverage. (Running mypy is pretty quick.) 3. After you've implemented a feature or a fix, compile your project - and run tests again, now in compiled mode. Almost always, nothing - will break here, if your type annotation coverage is good - enough. This can happen locally or as part of a Continuous - Integration (CI) job. If you have good CI, compiling locally may be - rarely needed. + and run tests again, now in compiled mode. Usually nothing will + break here, assuming your type annotation coverage is good. This + can happen locally or in a Continuous Integration (CI) job. If you + have CI, compiling locally may be rarely needed. 4. Release or deploy a compiled version. Optionally, include a fallback interpreted version for platforms that mypyc doesn't support. -This way of using mypyc has minimal impact on your productivity and -requires only minor adjustments to a typical Python workflow. Most of -development, testing and debugging happens in interpreted -mode. Incremental mypy runs, especially when using mypy daemon, are -very quick (often a few hundred milliseconds). +This mypyc workflow only involves minor tweaks to a typical Python +workflow. Most of development, testing and debugging happens in +interpreted mode. Incremental mypy runs, especially when using the +mypy daemon, are very quick (often a few hundred milliseconds). Next steps ---------- diff --git a/mypyc/doc/introduction.rst b/mypyc/doc/introduction.rst index 364d6e0acce1..035e4bbf2f5b 100644 --- a/mypyc/doc/introduction.rst +++ b/mypyc/doc/introduction.rst @@ -2,79 +2,71 @@ Introduction ============ Mypyc is a compiler for a strict, statically typed Python variant that -creates CPython C extension modules. The goal of mypyc is to speed up -Python modules -- code compiled with mypyc is often much faster than -CPython. Mypyc uses Python type hints to generate fast code, but it -also restricts the use of some dynamic Python features to gain -performance. +generates CPython C extension modules. Code compiled with mypyc is +often much faster than CPython. Mypyc uses Python `type hints +`_ to +generate fast code, and it also restricts the use of some dynamic +Python features to gain performance. Mypyc uses `mypy `_ to perform type checking and type inference. Most type checking features in the stdlib `typing `_ module are supported, including generic types, optional and union types, tuple types, and type variables. Using type hints is not necessary, but type -annotations are often the key to getting impressive performance gains. +annotations are the key to impressive performance gains. Compiled modules can import arbitrary Python modules, including third-party libraries, and compiled modules can be freely used from -other Python modules. Typically you use mypyc to only compile modules -that contain performance bottlenecks. +other Python modules. Often you'd use mypyc to only compile modules +with performance bottlenecks. -You can run compiled modules also as normal, interpreted Python -modules, since mypyc only compiles valid Python code. This means that -all Python developer tools and debuggers can be used (though some only -fully work in interpreted mode). +You can run the modules you compile also as normal, interpreted Python +modules. Mypyc only compiles valid Python code. This means that all +Python developer tools and debuggers can be used, though some only +fully work in interpreted mode. How fast is mypyc ----------------- The speed improvement from compilation depends on many factors. -Certain operations will be a lot faster, while other things will -remain the same. +Certain operations will be a lot faster, while others will get no +speedup. These estimates give a rough idea of what to expect (2x improvement halves the runtime): -* Existing code with type annotations may get **1.5x to 5x** better - performance. +* Typical code with type annotations may get **1.5x to 5x** faster. -* Existing code with *no* type annotations can expect **1.0x to 1.3x** - better performance. +* Typical code with *no* type annotations may get **1.0x to 1.3x** + faster. -* Code optimized for mypyc may see **5x to 10x** performance - improvement. +* Code optimized for mypyc may get **5x to 10x** faster. -Only performance of compiled modules improves. Time spent in libraries -or on I/O will not change (unless you also compile libraries). +Remember that only performance of compiled modules improves. Time +spent in libraries or on I/O will not change (unless you also compile +libraries). -Why does speed matter ---------------------- - -Here are reasons why speed can be important: - -* It can lower hardware costs. If a server application is 2x faster, - it may only need half as much hardware to run. +Why speed matters +----------------- -* It can improve user experience. If a request can be served in half - the time, it can help you attract more users. +Faster code has many benefits, some obvious and others less so: -* It can make your library or tool more popular. If there are two - options, and one of them is 2x faster, many users will pick the - faster one. +* Users prefer efficient and responsive applications, tools and + libraries. -* More efficient code uses less energy to run. +* If your server application is faster, you need less hardware, which + saves money. -* Compiled code may make your tests run faster, or allow batch jobs to - complete quicker, reducing wasted time. (This needs to offset - compilation time.) +* Faster code uses less energy, especially on servers that run 24/7. + This lowers your environmental footprint. -* Python is popular. Even a small efficiency gain across the Python - community can have a big impact (say, in a popular library). +* If tests or batch jobs run faster, you'll be more productive and + save time. How does mypyc work ------------------- -Mypyc can produce fast code through several features: +Mypyc produces fast code via several techniques: * Mypyc uses *ahead-of-time compilation* to native code. This removes CPython interpreter overhead. @@ -91,9 +83,9 @@ Mypyc can produce fast code through several features: attributes declared ``Final`` are immutable (and tries to enforce this). -* Classes are usually compiled to *C extension classes*. They use +* Most classes are compiled to *C extension classes*. They use `vtables `_ for - efficient method calls and attribute accesses. + fast method calls and attribute access. * Mypyc uses efficient (unboxed) representations for some primitive types, such as integers and booleans. @@ -101,14 +93,7 @@ Mypyc can produce fast code through several features: Why mypyc --------- -**High performance and high productivity.** Since code compiled with -mypyc can be run with CPython without compilation, and mypyc supports -most Python features, mypyc improves performance of Python with minor -changes to workflows, and with minimal productivity impact. - -**Migration path for existing Python code.** Existing Python code -often requires only minor changes to compile using mypyc, especially -if it's already using type annotations and mypy for type checking. +Here are some mypyc properties and features that can be useful. **Powerful Python types.** Mypyc leverages most features of standard Python type hint syntax, unlike tools such as Cython, which focus on @@ -118,56 +103,61 @@ such as local type inference, generics, optional types, tuple types and union types. Type hints act as machine-checked documentation, making code easier to understand and modify. -**Static and runtime type safety.** Mypyc aims to protect you from -segfaults and memory corruption. We consider any unexpected runtime -type safety violation as a bug. Mypyc uses mypy for powerful type -checking that will catch many bugs, saving you from a lot of -debugging. - **Fast program startup.** Python implementations using a JIT compiler, such as PyPy, slow down program startup, sometimes significantly. -Mypyc uses ahead-of-time compilation, so compilation does not -happen during program startup. +Mypyc uses ahead-of-time compilation, so compilation does not slow +down program startup. -**Ecosystem compatibility.** Since mypyc uses unmodified CPython as -the runtime, the stdlib and all existing third-party libraries, -including C extensions, continue to work. +**Python ecosystem compatibility.** Since mypyc uses the standard +CPython runtime, you can freely use the stdlib and use pip to install +arbitary third-party libraries, including C extensions. + +**Migration path for existing Python code.** Existing Python code +often requires only minor changes to compile using mypyc. + +**No need to wait for compilation.** Compiled code also runs as normal +Python code. You can use intepreted Python during development, with +familiar workflows. + +**Runtime type safety.** Mypyc aims to protect you from segfaults and +memory corruption. We consider any unexpected runtime type safety +violation as a bug. + +**Find errors statically.** Mypyc uses mypy for powerful static type +checking that will catch many bugs, saving you from a lot of +debugging. -**Easy path to "real" static typing.** Mypyc is an easy way to get -started with a statically typed language, with only a small set of -concepts to learn beyond Python skills. Unlike with a completely new -language, such as Go, Rust, or C++, you can become productive with -mypyc in a matter of hours, since the libraries, the tools and the -Python ecosystem are still there for you. +**Easy path to static typing.** Mypyc lets Python developers easily +dip their toes into modern static typing, without having to learn all +new syntax, libraries and idioms. Use cases for mypyc ------------------- -Here are examples of use cases where mypyc can be effective: +Here are examples of use cases where mypyc can be effective. -* Your project has a particular module that is critical for - performance. Add type annotations and compile it for quick - performance gains. +**Address a performance bottleneck.** Profiling shows that most time +is spent in a certain Python module. Add type annotations and compile +the module for performance gains. -* You've been using mypy to type check your code. Using mypyc is now - easy since your code is already annotated. +**Leverage existing type hints.** You already use mypy to type check +your code. Using mypyc will now be easy, since you already use static +typing. -* You want your entire program to be as fast as possible. You compile - all modules (except tests) for each release. You continue to use - interpreted mode during development, for a faster edit-run cycle. - (This is how mypy achieved a 4x end-to-end performance improvement - through mypyc.) +**Compile everything.** You want your whole application to be fast. +During development you use interpreted mode, for a quick edit-run +cycle, but in your releases all (non-test) code is compiled. This is +how mypy achieved a 4x performance improvement using mypyc. -* You are writing a new module that must be fast. You write the module - in Python, and focus on primitives that mypyc can optimize well. The - module is much faster when compiled, and you've saved a lot of - effort compared to writing an extension in C (and you don't need to - know C). +**Alternative to C.** You are writing a new module that must be fast. +You write the module in Python, and try to use operations that mypyc +can optimize well. The module is much faster when compiled, and you've +saved a lot of effort compared to writing an extension in C (and you +don't need to know C). -* You've written a C extension, but you are unhappy with it, and would - prefer to maintain Python code. In some cases you can switch to - Python and use mypyc to get performance comparable to the - original C. +**Rewriting a C extension.** You've written a C extension, but +maintaining C code is no fun. You might be able to switch to Python +and use mypyc to get performance comparable to the original C. Development status ------------------ diff --git a/mypyc/doc/str_operations.rst b/mypyc/doc/str_operations.rst index 7c03cc4a84cd..1fb9c10aa719 100644 --- a/mypyc/doc/str_operations.rst +++ b/mypyc/doc/str_operations.rst @@ -10,6 +10,7 @@ Construction ------------ * String literal +* ``str(x: int)`` * ``str(x: object)`` Operators From b1f51217437408eefed37ee09322b9cee0b3c401 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Thu, 9 Jul 2020 19:26:03 +0800 Subject: [PATCH 055/138] [mypyc] Merge more primitive ops (#9110) Relates to mypyc/mypyc#734. This PR completes ALL ops of dict, str, list, tuple, set that are supported using current design. The remaining ones would rather need to split into multiple ops (via specializers) or using pointers. --- mypyc/codegen/emit.py | 7 +- mypyc/irbuild/builder.py | 4 +- mypyc/irbuild/classdef.py | 32 +++--- mypyc/irbuild/expression.py | 12 +- mypyc/irbuild/for_helpers.py | 19 +-- mypyc/irbuild/function.py | 8 +- mypyc/irbuild/ll_builder.py | 4 +- mypyc/irbuild/specialize.py | 6 +- mypyc/irbuild/statement.py | 4 +- mypyc/primitives/dict_ops.py | 146 ++++++++++-------------- mypyc/primitives/list_ops.py | 25 ++-- mypyc/primitives/set_ops.py | 44 ++++--- mypyc/test-data/exceptions.test | 6 +- mypyc/test-data/irbuild-basic.test | 110 +++++++++--------- mypyc/test-data/irbuild-classes.test | 40 +++---- mypyc/test-data/irbuild-dict.test | 50 ++++---- mypyc/test-data/irbuild-lists.test | 12 +- mypyc/test-data/irbuild-set.test | 68 +++++------ mypyc/test-data/irbuild-statements.test | 18 +-- mypyc/test-data/irbuild-tuple.test | 8 +- mypyc/test-data/refcount.test | 12 +- mypyc/test/test_emitfunc.py | 22 ++-- 22 files changed, 325 insertions(+), 332 deletions(-) diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index 037182674a11..7b26c3003c52 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -13,7 +13,7 @@ is_float_rprimitive, is_bool_rprimitive, is_int_rprimitive, is_short_int_rprimitive, is_list_rprimitive, is_dict_rprimitive, is_set_rprimitive, is_tuple_rprimitive, is_none_rprimitive, is_object_rprimitive, object_rprimitive, is_str_rprimitive, - int_rprimitive, is_optional_type, optional_value_type + int_rprimitive, is_optional_type, optional_value_type, is_int32_rprimitive, is_int64_rprimitive ) from mypyc.ir.func_ir import FuncDecl from mypyc.ir.class_ir import ClassIR, all_concrete_classes @@ -695,6 +695,11 @@ def emit_box(self, src: str, dest: str, typ: RType, declare_dest: bool = False, self.emit_lines('{}{} = Py_None;'.format(declaration, dest)) if not can_borrow: self.emit_inc_ref(dest, object_rprimitive) + # TODO: This is a hack to handle mypy's false negative on unreachable code. + # All ops returning int32/int64 should not be coerced into object. + # Since their result will not be used elsewhere, it's safe to use NULL here + elif is_int32_rprimitive(typ) or is_int64_rprimitive(typ): + self.emit_lines('{}{} = NULL;'.format(declaration, dest)) elif isinstance(typ, RTuple): self.declare_tuple_struct(typ) self.emit_line('{}{} = PyTuple_New({});'.format(declaration, dest, len(typ.types))) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 30699b5a7ac2..e1d50df98bde 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -245,7 +245,7 @@ def add_to_non_ext_dict(self, non_ext: NonExtClassInfo, key: str, val: Value, line: int) -> None: # Add an attribute entry into the class dict of a non-extension class. key_unicode = self.load_static_unicode(key) - self.primitive_op(dict_set_item_op, [non_ext.dict, key_unicode, val], line) + self.call_c(dict_set_item_op, [non_ext.dict, key_unicode, val], line) def gen_import(self, id: str, line: int) -> None: self.imports[id] = None @@ -884,7 +884,7 @@ def load_global(self, expr: NameExpr) -> Value: def load_global_str(self, name: str, line: int) -> Value: _globals = self.load_globals_dict() reg = self.load_static_unicode(name) - return self.primitive_op(dict_get_item_op, [_globals, reg], line) + return self.call_c(dict_get_item_op, [_globals, reg], line) def load_globals_dict(self) -> Value: return self.add(LoadStatic(dict_rprimitive, 'globals', self.module_name)) diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index 7d9244b23f83..8b5f1da98d26 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -149,12 +149,12 @@ def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None: builder.add(InitStatic(non_ext_class, cdef.name, builder.module_name, NAMESPACE_TYPE)) # Add the non-extension class to the dict - builder.primitive_op(dict_set_item_op, - [ - builder.load_globals_dict(), - builder.load_static_unicode(cdef.name), - non_ext_class - ], cdef.line) + builder.call_c(dict_set_item_op, + [ + builder.load_globals_dict(), + builder.load_static_unicode(cdef.name), + non_ext_class + ], cdef.line) # Cache any cachable class attributes cache_class_attrs(builder, attrs_to_cache, cdef) @@ -191,12 +191,12 @@ def allocate_class(builder: IRBuilder, cdef: ClassDef) -> Value: builder.add(InitStatic(tp, cdef.name, builder.module_name, NAMESPACE_TYPE)) # Add it to the dict - builder.primitive_op(dict_set_item_op, - [ - builder.load_globals_dict(), - builder.load_static_unicode(cdef.name), - tp, - ], cdef.line) + builder.call_c(dict_set_item_op, + [ + builder.load_globals_dict(), + builder.load_static_unicode(cdef.name), + tp, + ], cdef.line) return tp @@ -280,7 +280,7 @@ def add_non_ext_class_attr(builder: IRBuilder, # TODO: Maybe generate more precise types for annotations key = builder.load_static_unicode(lvalue.name) typ = builder.primitive_op(type_object_op, [], stmt.line) - builder.primitive_op(dict_set_item_op, [non_ext.anns, key, typ], stmt.line) + builder.call_c(dict_set_item_op, [non_ext.anns, key, typ], stmt.line) # Only add the attribute to the __dict__ if the assignment is of the form: # x: type = value (don't add attributes of the form 'x: type' to the __dict__). @@ -470,9 +470,9 @@ def create_mypyc_attrs_tuple(builder: IRBuilder, ir: ClassIR, line: int) -> Valu def finish_non_ext_dict(builder: IRBuilder, non_ext: NonExtClassInfo, line: int) -> None: # Add __annotations__ to the class dict. - builder.primitive_op(dict_set_item_op, - [non_ext.dict, builder.load_static_unicode('__annotations__'), - non_ext.anns], -1) + builder.call_c(dict_set_item_op, + [non_ext.dict, builder.load_static_unicode('__annotations__'), + non_ext.anns], -1) # We add a __doc__ attribute so if the non-extension class is decorated with the # dataclass decorator, dataclass will not try to look for __text_signature__. diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 9daf2b302875..a52a7ee83479 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -20,7 +20,7 @@ ) from mypyc.ir.rtypes import RTuple, object_rprimitive, is_none_rprimitive from mypyc.ir.func_ir import FUNC_CLASSMETHOD, FUNC_STATICMETHOD -from mypyc.primitives.registry import name_ref_ops +from mypyc.primitives.registry import name_ref_ops, CFunctionDescription from mypyc.primitives.generic_ops import iter_op from mypyc.primitives.misc_ops import new_slice_op, ellipsis_op, type_op from mypyc.primitives.list_ops import new_list_op, list_append_op, list_extend_op @@ -491,8 +491,8 @@ def transform_set_expr(builder: IRBuilder, expr: SetExpr) -> Value: def _visit_display(builder: IRBuilder, items: List[Expression], constructor_op: OpDescription, - append_op: OpDescription, - extend_op: OpDescription, + append_op: CFunctionDescription, + extend_op: CFunctionDescription, line: int ) -> Value: accepted_items = [] @@ -512,7 +512,7 @@ def _visit_display(builder: IRBuilder, if result is None: result = builder.primitive_op(constructor_op, initial_items, line) - builder.primitive_op(extend_op if starred else append_op, [result, value], line) + builder.call_c(extend_op if starred else append_op, [result, value], line) if result is None: result = builder.primitive_op(constructor_op, initial_items, line) @@ -534,7 +534,7 @@ def transform_set_comprehension(builder: IRBuilder, o: SetComprehension) -> Valu def gen_inner_stmts() -> None: e = builder.accept(gen.left_expr) - builder.primitive_op(set_add_op, [set_ops, e], o.line) + builder.call_c(set_add_op, [set_ops, e], o.line) comprehension_helper(builder, loop_params, gen_inner_stmts, o.line) return set_ops @@ -547,7 +547,7 @@ def transform_dictionary_comprehension(builder: IRBuilder, o: DictionaryComprehe def gen_inner_stmts() -> None: k = builder.accept(o.key) v = builder.accept(o.value) - builder.primitive_op(dict_set_item_op, [d, k, v], o.line) + builder.call_c(dict_set_item_op, [d, k, v], o.line) comprehension_helper(builder, loop_params, gen_inner_stmts, o.line) return d diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 1c65fec05eb3..38e75016b26e 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -13,12 +13,13 @@ ) from mypyc.ir.ops import ( Value, BasicBlock, LoadInt, Branch, Register, AssignmentTarget, TupleGet, - AssignmentTargetTuple, TupleSet, OpDescription, BinaryIntOp + AssignmentTargetTuple, TupleSet, BinaryIntOp ) from mypyc.ir.rtypes import ( RType, is_short_int_rprimitive, is_list_rprimitive, is_sequence_rprimitive, RTuple, is_dict_rprimitive, short_int_rprimitive ) +from mypyc.primitives.registry import CFunctionDescription from mypyc.primitives.dict_ops import ( dict_next_key_op, dict_next_value_op, dict_next_item_op, dict_check_size_op, dict_key_iter_op, dict_value_iter_op, dict_item_iter_op @@ -92,7 +93,7 @@ def translate_list_comprehension(builder: IRBuilder, gen: GeneratorExpr) -> Valu def gen_inner_stmts() -> None: e = builder.accept(gen.left_expr) - builder.primitive_op(list_append_op, [list_ops, e], gen.line) + builder.call_c(list_append_op, [list_ops, e], gen.line) comprehension_helper(builder, loop_params, gen_inner_stmts, gen.line) return list_ops @@ -485,8 +486,8 @@ class ForDictionaryCommon(ForGenerator): since they may override some iteration methods in subtly incompatible manner. The fallback logic is implemented in CPy.h via dynamic type check. """ - dict_next_op = None # type: ClassVar[OpDescription] - dict_iter_op = None # type: ClassVar[OpDescription] + dict_next_op = None # type: ClassVar[CFunctionDescription] + dict_iter_op = None # type: ClassVar[CFunctionDescription] def need_cleanup(self) -> bool: # Technically, a dict subclass can raise an unrelated exception @@ -504,14 +505,14 @@ def init(self, expr_reg: Value, target_type: RType) -> None: self.size = builder.maybe_spill(self.load_len(self.expr_target)) # For dict class (not a subclass) this is the dictionary itself. - iter_reg = builder.primitive_op(self.dict_iter_op, [expr_reg], self.line) + iter_reg = builder.call_c(self.dict_iter_op, [expr_reg], self.line) self.iter_target = builder.maybe_spill(iter_reg) def gen_condition(self) -> None: """Get next key/value pair, set new offset, and check if we should continue.""" builder = self.builder line = self.line - self.next_tuple = self.builder.primitive_op( + self.next_tuple = self.builder.call_c( self.dict_next_op, [builder.read(self.iter_target, line), builder.read(self.offset_target, line)], line) @@ -532,9 +533,9 @@ def gen_step(self) -> None: builder = self.builder line = self.line # Technically, we don't need a new primitive for this, but it is simpler. - builder.primitive_op(dict_check_size_op, - [builder.read(self.expr_target, line), - builder.read(self.size, line)], line) + builder.call_c(dict_check_size_op, + [builder.read(self.expr_target, line), + builder.read(self.size, line)], line) def gen_cleanup(self) -> None: # Same as for generic ForIterable. diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index 8687df6b6487..ad42a12584ee 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -93,10 +93,10 @@ def transform_decorator(builder: IRBuilder, dec: Decorator) -> None: decorated_func = load_decorated_func(builder, dec.func, orig_func) # Set the callable object representing the decorated function as a global. - builder.primitive_op(dict_set_item_op, - [builder.load_globals_dict(), - builder.load_static_unicode(dec.func.name), decorated_func], - decorated_func.line) + builder.call_c(dict_set_item_op, + [builder.load_globals_dict(), + builder.load_static_unicode(dec.func.name), decorated_func], + decorated_func.line) builder.functions.append(func_ir) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index ecc7bfccbbb5..359dea60d068 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -269,7 +269,7 @@ def py_call(self, # don't have an extend method. pos_args_list = self.primitive_op(new_list_op, pos_arg_values, line) for star_arg_value in star_arg_values: - self.primitive_op(list_extend_op, [pos_args_list, star_arg_value], line) + self.call_c(list_extend_op, [pos_args_list, star_arg_value], line) pos_args_tuple = self.call_c(list_tuple_op, [pos_args_list], line) kw_args_dict = self.make_dict(kw_arg_key_value_pairs, line) @@ -591,7 +591,7 @@ def make_dict(self, key_value_pairs: Sequence[DictEntry], line: int) -> Value: if result is None: result = self._create_dict(keys, values, line) - self.primitive_op( + self.call_c( dict_update_in_display_op, [result, value], line=line diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index 137e6abf30e1..d10387c26f6b 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -99,11 +99,11 @@ def dict_methods_fast_path( # Note that it is not safe to use fast methods on dict subclasses, so # the corresponding helpers in CPy.h fallback to (inlined) generic logic. if attr == 'keys': - return builder.primitive_op(dict_keys_op, [obj], expr.line) + return builder.call_c(dict_keys_op, [obj], expr.line) elif attr == 'values': - return builder.primitive_op(dict_values_op, [obj], expr.line) + return builder.call_c(dict_values_op, [obj], expr.line) else: - return builder.primitive_op(dict_items_op, [obj], expr.line) + return builder.call_c(dict_items_op, [obj], expr.line) @specialize_function('builtins.tuple') diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py index 1f669930c634..ac88ecb04858 100644 --- a/mypyc/irbuild/statement.py +++ b/mypyc/irbuild/statement.py @@ -126,8 +126,8 @@ def transform_import(builder: IRBuilder, node: Import) -> None: # Python 3.7 has a nice 'PyImport_GetModule' function that we can't use :( mod_dict = builder.primitive_op(get_module_dict_op, [], node.line) - obj = builder.primitive_op(dict_get_item_op, - [mod_dict, builder.load_static_unicode(base)], node.line) + obj = builder.call_c(dict_get_item_op, + [mod_dict, builder.load_static_unicode(base)], node.line) builder.gen_method_call( globals, '__setitem__', [builder.load_static_unicode(name), obj], result_type=None, line=node.line) diff --git a/mypyc/primitives/dict_ops.py b/mypyc/primitives/dict_ops.py index 1ee2e40920b7..47a2712709c1 100644 --- a/mypyc/primitives/dict_ops.py +++ b/mypyc/primitives/dict_ops.py @@ -10,9 +10,8 @@ ) from mypyc.primitives.registry import ( - name_ref_op, method_op, func_op, custom_op, - simple_emit, call_emit, call_negative_bool_emit, - name_emit, c_custom_op, c_method_op, c_function_op, c_binary_op + name_ref_op, method_op, func_op, + simple_emit, name_emit, c_custom_op, c_method_op, c_function_op, c_binary_op ) @@ -24,20 +23,20 @@ is_borrowed=True) # dict[key] -dict_get_item_op = method_op( +dict_get_item_op = c_method_op( name='__getitem__', arg_types=[dict_rprimitive, object_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyDict_GetItem')) + return_type=object_rprimitive, + c_function_name='CPyDict_GetItem', + error_kind=ERR_MAGIC) # dict[key] = value -dict_set_item_op = method_op( +dict_set_item_op = c_method_op( name='__setitem__', arg_types=[dict_rprimitive, object_rprimitive, object_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_FALSE, - emit=call_negative_bool_emit('CPyDict_SetItem')) + return_type=c_int_rprimitive, + c_function_name='CPyDict_SetItem', + error_kind=ERR_NEG_INT) # key in dict c_binary_op( @@ -50,30 +49,29 @@ ordering=[1, 0]) # dict1.update(dict2) -dict_update_op = method_op( +dict_update_op = c_method_op( name='update', arg_types=[dict_rprimitive, dict_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_FALSE, - emit=call_negative_bool_emit('CPyDict_Update'), + return_type=c_int_rprimitive, + c_function_name='CPyDict_Update', + error_kind=ERR_NEG_INT, priority=2) # Operation used for **value in dict displays. # This is mostly like dict.update(obj), but has customized error handling. -dict_update_in_display_op = custom_op( +dict_update_in_display_op = c_custom_op( arg_types=[dict_rprimitive, dict_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_FALSE, - emit=call_negative_bool_emit('CPyDict_UpdateInDisplay'), - format_str='{dest} = {args[0]}.update({args[1]}) (display) :: dict',) + return_type=c_int_rprimitive, + c_function_name='CPyDict_UpdateInDisplay', + error_kind=ERR_NEG_INT) # dict.update(obj) -method_op( +c_method_op( name='update', arg_types=[dict_rprimitive, object_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_FALSE, - emit=call_negative_bool_emit('CPyDict_UpdateFromAny')) + return_type=c_int_rprimitive, + c_function_name='CPyDict_UpdateFromAny', + error_kind=ERR_NEG_INT) # dict.get(key, default) c_method_op( @@ -150,31 +148,25 @@ error_kind=ERR_MAGIC) # list(dict.keys()) -dict_keys_op = custom_op( - name='keys', +dict_keys_op = c_custom_op( arg_types=[dict_rprimitive], - result_type=list_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyDict_Keys') -) + return_type=list_rprimitive, + c_function_name='CPyDict_Keys', + error_kind=ERR_MAGIC) # list(dict.values()) -dict_values_op = custom_op( - name='values', +dict_values_op = c_custom_op( arg_types=[dict_rprimitive], - result_type=list_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyDict_Values') -) + return_type=list_rprimitive, + c_function_name='CPyDict_Values', + error_kind=ERR_MAGIC) # list(dict.items()) -dict_items_op = custom_op( - name='items', +dict_items_op = c_custom_op( arg_types=[dict_rprimitive], - result_type=list_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyDict_Items') -) + return_type=list_rprimitive, + c_function_name='CPyDict_Items', + error_kind=ERR_MAGIC) def emit_len(emitter: EmitterInterface, args: List[str], dest: str) -> None: @@ -192,59 +184,45 @@ def emit_len(emitter: EmitterInterface, args: List[str], dest: str) -> None: emit=emit_len) # PyDict_Next() fast iteration -dict_key_iter_op = custom_op( - name='key_iter', +dict_key_iter_op = c_custom_op( arg_types=[dict_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyDict_GetKeysIter'), -) + return_type=object_rprimitive, + c_function_name='CPyDict_GetKeysIter', + error_kind=ERR_MAGIC) -dict_value_iter_op = custom_op( - name='value_iter', +dict_value_iter_op = c_custom_op( arg_types=[dict_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyDict_GetValuesIter'), -) + return_type=object_rprimitive, + c_function_name='CPyDict_GetValuesIter', + error_kind=ERR_MAGIC) -dict_item_iter_op = custom_op( - name='item_iter', +dict_item_iter_op = c_custom_op( arg_types=[dict_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyDict_GetItemsIter'), -) + return_type=object_rprimitive, + c_function_name='CPyDict_GetItemsIter', + error_kind=ERR_MAGIC) -dict_next_key_op = custom_op( +dict_next_key_op = c_custom_op( arg_types=[object_rprimitive, int_rprimitive], - result_type=dict_next_rtuple_single, - error_kind=ERR_NEVER, - emit=call_emit('CPyDict_NextKey'), - format_str='{dest} = next_key {args[0]}, offset={args[1]}', -) + return_type=dict_next_rtuple_single, + c_function_name='CPyDict_NextKey', + error_kind=ERR_NEVER) -dict_next_value_op = custom_op( +dict_next_value_op = c_custom_op( arg_types=[object_rprimitive, int_rprimitive], - result_type=dict_next_rtuple_single, - error_kind=ERR_NEVER, - emit=call_emit('CPyDict_NextValue'), - format_str='{dest} = next_value {args[0]}, offset={args[1]}', -) + return_type=dict_next_rtuple_single, + c_function_name='CPyDict_NextValue', + error_kind=ERR_NEVER) -dict_next_item_op = custom_op( +dict_next_item_op = c_custom_op( arg_types=[object_rprimitive, int_rprimitive], - result_type=dict_next_rtuple_pair, - error_kind=ERR_NEVER, - emit=call_emit('CPyDict_NextItem'), - format_str='{dest} = next_item {args[0]}, offset={args[1]}', -) + return_type=dict_next_rtuple_pair, + c_function_name='CPyDict_NextItem', + error_kind=ERR_NEVER) # check that len(dict) == const during iteration -dict_check_size_op = custom_op( +dict_check_size_op = c_custom_op( arg_types=[dict_rprimitive, int_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_FALSE, - emit=call_emit('CPyDict_CheckSize'), - format_str='{dest} = assert size({args[0]}) == {args[1]}', -) + return_type=bool_rprimitive, + c_function_name='CPyDict_CheckSize', + error_kind=ERR_FALSE) diff --git a/mypyc/primitives/list_ops.py b/mypyc/primitives/list_ops.py index ddacc7c9b0a5..24546e9f1914 100644 --- a/mypyc/primitives/list_ops.py +++ b/mypyc/primitives/list_ops.py @@ -2,13 +2,14 @@ from typing import List -from mypyc.ir.ops import ERR_MAGIC, ERR_NEVER, ERR_FALSE, EmitterInterface +from mypyc.ir.ops import ERR_MAGIC, ERR_NEVER, ERR_FALSE, ERR_NEG_INT, EmitterInterface from mypyc.ir.rtypes import ( - int_rprimitive, short_int_rprimitive, list_rprimitive, object_rprimitive, bool_rprimitive + int_rprimitive, short_int_rprimitive, list_rprimitive, object_rprimitive, bool_rprimitive, + c_int_rprimitive ) from mypyc.primitives.registry import ( - name_ref_op, func_op, method_op, custom_op, name_emit, - call_emit, call_negative_bool_emit, c_function_op, c_binary_op, c_method_op + name_ref_op, func_op, custom_op, name_emit, + call_emit, c_function_op, c_binary_op, c_method_op ) @@ -85,20 +86,20 @@ def emit_new(emitter: EmitterInterface, args: List[str], dest: str) -> None: steals=[False, False, True]) # list.append(obj) -list_append_op = method_op( +list_append_op = c_method_op( name='append', arg_types=[list_rprimitive, object_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_FALSE, - emit=call_negative_bool_emit('PyList_Append')) + return_type=c_int_rprimitive, + c_function_name='PyList_Append', + error_kind=ERR_NEG_INT) # list.extend(obj) -list_extend_op = method_op( +list_extend_op = c_method_op( name='extend', arg_types=[list_rprimitive, object_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyList_Extend')) + return_type=object_rprimitive, + c_function_name='CPyList_Extend', + error_kind=ERR_MAGIC) # list.pop() list_pop_last = c_method_op( diff --git a/mypyc/primitives/set_ops.py b/mypyc/primitives/set_ops.py index a81b309b944b..fd929829b2ed 100644 --- a/mypyc/primitives/set_ops.py +++ b/mypyc/primitives/set_ops.py @@ -1,8 +1,7 @@ """Primitive set (and frozenset) ops.""" from mypyc.primitives.registry import ( - func_op, method_op, binary_op, simple_emit, negative_int_emit, - call_negative_bool_emit, c_function_op, c_method_op + func_op, simple_emit, c_function_op, c_method_op, c_binary_op ) from mypyc.ir.ops import ERR_MAGIC, ERR_FALSE, ERR_NEVER, ERR_NEG_INT, EmitterInterface from mypyc.ir.rtypes import ( @@ -54,14 +53,14 @@ def emit_len(emitter: EmitterInterface, args: List[str], dest: str) -> None: ) # item in set -binary_op( - op='in', +c_binary_op( + name='in', arg_types=[object_rprimitive, set_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_MAGIC, - format_str='{dest} = {args[0]} in {args[1]} :: set', - emit=negative_int_emit('{dest} = PySet_Contains({args[1]}, {args[0]});') -) + return_type=c_int_rprimitive, + c_function_name='PySet_Contains', + error_kind=ERR_NEG_INT, + truncated_type=bool_rprimitive, + ordering=[1, 0]) # set.remove(obj) c_method_op( @@ -80,33 +79,30 @@ def emit_len(emitter: EmitterInterface, args: List[str], dest: str) -> None: error_kind=ERR_NEG_INT) # set.add(obj) -set_add_op = method_op( +set_add_op = c_method_op( name='add', arg_types=[set_rprimitive, object_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_FALSE, - emit=call_negative_bool_emit('PySet_Add') -) + return_type=c_int_rprimitive, + c_function_name='PySet_Add', + error_kind=ERR_NEG_INT) # set.update(obj) # # This is not a public API but looks like it should be fine. -set_update_op = method_op( +set_update_op = c_method_op( name='update', arg_types=[set_rprimitive, object_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_FALSE, - emit=call_negative_bool_emit('_PySet_Update') -) + return_type=c_int_rprimitive, + c_function_name='_PySet_Update', + error_kind=ERR_NEG_INT) # set.clear() -method_op( +c_method_op( name='clear', arg_types=[set_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_FALSE, - emit=call_negative_bool_emit('PySet_Clear') -) + return_type=c_int_rprimitive, + c_function_name='PySet_Clear', + error_kind=ERR_NEG_INT) # set.pop() c_method_op( diff --git a/mypyc/test-data/exceptions.test b/mypyc/test-data/exceptions.test index edac4269afc8..0848943f8d4d 100644 --- a/mypyc/test-data/exceptions.test +++ b/mypyc/test-data/exceptions.test @@ -36,7 +36,7 @@ def f(x, y, z): x :: list y, z :: int r0 :: object - r1 :: bool + r1 :: int32 r2 :: None r3 :: object r4 :: bool @@ -44,9 +44,9 @@ def f(x, y, z): L0: inc_ref y :: int r0 = box(int, y) - r1 = x.append(r0) :: list + r1 = PyList_Append(x, r0) dec_ref r0 - if not r1 goto L3 (error at f:3) else goto L1 :: bool + if r1 < 0 goto L3 (error at f:3) else goto L1 L1: r2 = None inc_ref z :: int diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 85c203dbe548..eac963c9adcc 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -594,7 +594,7 @@ def f(x): L0: r0 = __main__.globals :: static r1 = unicode_2 :: static ('g') - r2 = r0[r1] :: dict + r2 = CPyDict_GetItem(r0, r1) r3 = box(int, x) r4 = py_call(r2, r3) r5 = unbox(int, r4) @@ -1058,7 +1058,7 @@ def return_callable_type(): L0: r0 = __main__.globals :: static r1 = unicode_4 :: static ('return_float') - r2 = r0[r1] :: dict + r2 = CPyDict_GetItem(r0, r1) return r2 def call_callable_type(): r0, f, r1 :: object @@ -1359,7 +1359,7 @@ def f(): L0: r0 = __main__.globals :: static r1 = unicode_1 :: static ('x') - r2 = r0[r1] :: dict + r2 = CPyDict_GetItem(r0, r1) r3 = unbox(int, r2) r4 = builtins :: module r5 = unicode_2 :: static ('print') @@ -1377,7 +1377,7 @@ def __top_level__(): r6 :: dict r7 :: str r8 :: object - r9 :: bool + r9 :: int32 r10 :: dict r11 :: str r12 :: object @@ -1400,10 +1400,10 @@ L2: r6 = __main__.globals :: static r7 = unicode_1 :: static ('x') r8 = box(short_int, r5) - r9 = r6.__setitem__(r7, r8) :: dict + r9 = CPyDict_SetItem(r6, r7, r8) r10 = __main__.globals :: static r11 = unicode_1 :: static ('x') - r12 = r10[r11] :: dict + r12 = CPyDict_GetItem(r10, r11) r13 = unbox(int, r12) r14 = builtins :: module r15 = unicode_2 :: static ('print') @@ -1629,10 +1629,10 @@ L0: r3 = (r0, r1, r2) r4 = __main__.globals :: static r5 = unicode_3 :: static ('f') - r6 = r4[r5] :: dict + r6 = CPyDict_GetItem(r4, r5) r7 = [] r8 = box(tuple[int, int, int], r3) - r9 = r7.extend(r8) :: list + r9 = CPyList_Extend(r7, r8) r10 = PyList_AsTuple(r7) r11 = PyDict_New() r12 = py_call_with_kwargs(r6, r10, r11) @@ -1657,11 +1657,11 @@ L0: r3 = (r1, r2) r4 = __main__.globals :: static r5 = unicode_3 :: static ('f') - r6 = r4[r5] :: dict + r6 = CPyDict_GetItem(r4, r5) r7 = box(short_int, r0) r8 = [r7] r9 = box(tuple[int, int], r3) - r10 = r8.extend(r9) :: list + r10 = CPyList_Extend(r8, r9) r11 = PyList_AsTuple(r8) r12 = PyDict_New() r13 = py_call_with_kwargs(r6, r11, r12) @@ -1697,7 +1697,7 @@ def g(): r13 :: object r14 :: tuple r15 :: dict - r16 :: bool + r16 :: int32 r17 :: object r18 :: tuple[int, int, int] L0: @@ -1714,10 +1714,10 @@ L0: r10 = CPyDict_Build(r6, r0, r7, r2, r8, r4, r9) r11 = __main__.globals :: static r12 = unicode_6 :: static ('f') - r13 = r11[r12] :: dict + r13 = CPyDict_GetItem(r11, r12) r14 = () :: tuple r15 = PyDict_New() - r16 = r15.update(r10) (display) :: dict + r16 = CPyDict_UpdateInDisplay(r15, r10) r17 = py_call_with_kwargs(r13, r14, r15) r18 = unbox(tuple[int, int, int], r17) return r18 @@ -1734,7 +1734,7 @@ def h(): r11, r12 :: object r13 :: tuple r14 :: dict - r15 :: bool + r15 :: int32 r16 :: object r17 :: tuple[int, int, int] L0: @@ -1749,11 +1749,11 @@ L0: r8 = CPyDict_Build(r5, r1, r6, r3, r7) r9 = __main__.globals :: static r10 = unicode_6 :: static ('f') - r11 = r9[r10] :: dict + r11 = CPyDict_GetItem(r9, r10) r12 = box(short_int, r0) r13 = (r12) :: tuple r14 = PyDict_New() - r15 = r14.update(r8) (display) :: dict + r15 = CPyDict_UpdateInDisplay(r14, r8) r16 = py_call_with_kwargs(r11, r13, r14) r17 = unbox(tuple[int, int, int], r16) return r17 @@ -1879,7 +1879,7 @@ def f(): r17 :: bool r18 :: int r19 :: object - r20 :: bool + r20 :: int32 r21, r22 :: short_int L0: r0 = [] @@ -1914,7 +1914,7 @@ L5: L6: r18 = CPyTagged_Multiply(x, x) r19 = box(int, r18) - r20 = r0.append(r19) :: list + r20 = PyList_Append(r0, r19) L7: r21 = 1 r22 = r9 + r21 @@ -1943,7 +1943,7 @@ def f(): r17 :: bool r18 :: int r19, r20 :: object - r21 :: bool + r21 :: int32 r22, r23 :: short_int L0: r0 = PyDict_New() @@ -1979,7 +1979,7 @@ L6: r18 = CPyTagged_Multiply(x, x) r19 = box(int, x) r20 = box(int, r18) - r21 = r0.__setitem__(r19, r20) :: dict + r21 = CPyDict_SetItem(r0, r19, r20) L7: r22 = 1 r23 = r9 + r22 @@ -2012,7 +2012,7 @@ def f(l): r17 :: tuple[int, int, int] r18, r19, r20, r21, r22 :: int r23 :: object - r24 :: bool + r24 :: int32 r25, r26 :: short_int L0: r0 = 0 @@ -2055,7 +2055,7 @@ L6: r21 = CPyTagged_Add(x0, y0) r22 = CPyTagged_Add(r21, z0) r23 = box(int, r22) - r24 = r11.append(r23) :: list + r24 = PyList_Append(r11, r23) L7: r25 = 1 r26 = r13 + r25 @@ -2412,15 +2412,15 @@ def __top_level__(): r12 :: str r13 :: object r14 :: str - r15 :: bool + r15 :: int32 r16 :: str r17 :: object r18 :: str - r19 :: bool + r19 :: int32 r20 :: str r21 :: object r22 :: str - r23 :: bool + r23 :: int32 r24, r25 :: str r26 :: object r27 :: tuple[str, object] @@ -2436,7 +2436,7 @@ def __top_level__(): r37, r38 :: object r39 :: dict r40 :: str - r41 :: bool + r41 :: int32 r42 :: short_int r43 :: str r44 :: dict @@ -2445,13 +2445,13 @@ def __top_level__(): r49 :: tuple r50 :: dict r51 :: str - r52 :: bool + r52 :: int32 r53 :: dict r54 :: str r55, r56, r57 :: object r58 :: dict r59 :: str - r60 :: bool + r60 :: int32 r61 :: str r62 :: dict r63 :: str @@ -2461,7 +2461,7 @@ def __top_level__(): r67, r68 :: object r69 :: dict r70 :: str - r71 :: bool + r71 :: int32 r72, r73, r74 :: short_int r75, r76, r77 :: object r78 :: list @@ -2470,7 +2470,7 @@ def __top_level__(): r81, r82 :: object r83 :: dict r84 :: str - r85 :: bool + r85 :: int32 r86 :: None L0: r0 = builtins :: module @@ -2496,15 +2496,15 @@ L4: r12 = unicode_2 :: static ('List') r13 = getattr r10, r12 r14 = unicode_2 :: static ('List') - r15 = r11.__setitem__(r14, r13) :: dict + r15 = CPyDict_SetItem(r11, r14, r13) r16 = unicode_3 :: static ('NewType') r17 = getattr r10, r16 r18 = unicode_3 :: static ('NewType') - r19 = r11.__setitem__(r18, r17) :: dict + r19 = CPyDict_SetItem(r11, r18, r17) r20 = unicode_4 :: static ('NamedTuple') r21 = getattr r10, r20 r22 = unicode_4 :: static ('NamedTuple') - r23 = r11.__setitem__(r22, r21) :: dict + r23 = CPyDict_SetItem(r11, r22, r21) r24 = unicode_5 :: static ('Lol') r25 = unicode_6 :: static ('a') r26 = int @@ -2518,41 +2518,41 @@ L4: r34 = box(tuple[object, object], r33) r35 = __main__.globals :: static r36 = unicode_4 :: static ('NamedTuple') - r37 = r35[r36] :: dict + r37 = CPyDict_GetItem(r35, r36) r38 = py_call(r37, r24, r34) r39 = __main__.globals :: static r40 = unicode_5 :: static ('Lol') - r41 = r39.__setitem__(r40, r38) :: dict + r41 = CPyDict_SetItem(r39, r40, r38) r42 = 1 r43 = unicode_8 :: static r44 = __main__.globals :: static r45 = unicode_5 :: static ('Lol') - r46 = r44[r45] :: dict + r46 = CPyDict_GetItem(r44, r45) r47 = box(short_int, r42) r48 = py_call(r46, r47, r43) r49 = cast(tuple, r48) r50 = __main__.globals :: static r51 = unicode_9 :: static ('x') - r52 = r50.__setitem__(r51, r49) :: dict + r52 = CPyDict_SetItem(r50, r51, r49) r53 = __main__.globals :: static r54 = unicode_2 :: static ('List') - r55 = r53[r54] :: dict + r55 = CPyDict_GetItem(r53, r54) r56 = int r57 = r55[r56] :: object r58 = __main__.globals :: static r59 = unicode_10 :: static ('Foo') - r60 = r58.__setitem__(r59, r57) :: dict + r60 = CPyDict_SetItem(r58, r59, r57) r61 = unicode_11 :: static ('Bar') r62 = __main__.globals :: static r63 = unicode_10 :: static ('Foo') - r64 = r62[r63] :: dict + r64 = CPyDict_GetItem(r62, r63) r65 = __main__.globals :: static r66 = unicode_3 :: static ('NewType') - r67 = r65[r66] :: dict + r67 = CPyDict_GetItem(r65, r66) r68 = py_call(r67, r61, r64) r69 = __main__.globals :: static r70 = unicode_11 :: static ('Bar') - r71 = r69.__setitem__(r70, r68) :: dict + r71 = CPyDict_SetItem(r69, r70, r68) r72 = 1 r73 = 2 r74 = 3 @@ -2562,11 +2562,11 @@ L4: r78 = [r75, r76, r77] r79 = __main__.globals :: static r80 = unicode_11 :: static ('Bar') - r81 = r79[r80] :: dict + r81 = CPyDict_GetItem(r79, r80) r82 = py_call(r81, r78) r83 = __main__.globals :: static r84 = unicode_12 :: static ('y') - r85 = r83.__setitem__(r84, r82) :: dict + r85 = CPyDict_SetItem(r83, r84, r82) r86 = None return r86 @@ -2829,11 +2829,11 @@ L0: r1.__mypyc_env__ = r0; r2 = is_error r3 = __main__.globals :: static r4 = unicode_8 :: static ('b') - r5 = r3[r4] :: dict + r5 = CPyDict_GetItem(r3, r4) r6 = py_call(r5, r1) r7 = __main__.globals :: static r8 = unicode_9 :: static ('a') - r9 = r7[r8] :: dict + r9 = CPyDict_GetItem(r7, r8) r10 = py_call(r9, r6) r0.d = r10; r11 = is_error r12 = unicode_10 :: static ('c') @@ -2857,7 +2857,7 @@ def __top_level__(): r12 :: str r13 :: object r14 :: str - r15 :: bool + r15 :: int32 r16 :: dict r17 :: str r18 :: object @@ -2869,7 +2869,7 @@ def __top_level__(): r25, r26 :: object r27 :: dict r28 :: str - r29 :: bool + r29 :: int32 r30 :: None L0: r0 = builtins :: module @@ -2895,21 +2895,21 @@ L4: r12 = unicode_2 :: static ('Callable') r13 = getattr r10, r12 r14 = unicode_2 :: static ('Callable') - r15 = r11.__setitem__(r14, r13) :: dict + r15 = CPyDict_SetItem(r11, r14, r13) r16 = __main__.globals :: static r17 = unicode_11 :: static ('__mypyc_c_decorator_helper__') - r18 = r16[r17] :: dict + r18 = CPyDict_GetItem(r16, r17) r19 = __main__.globals :: static r20 = unicode_8 :: static ('b') - r21 = r19[r20] :: dict + r21 = CPyDict_GetItem(r19, r20) r22 = py_call(r21, r18) r23 = __main__.globals :: static r24 = unicode_9 :: static ('a') - r25 = r23[r24] :: dict + r25 = CPyDict_GetItem(r23, r24) r26 = py_call(r25, r22) r27 = __main__.globals :: static r28 = unicode_10 :: static ('c') - r29 = r27.__setitem__(r28, r26) :: dict + r29 = CPyDict_SetItem(r27, r28, r26) r30 = None return r30 @@ -2995,7 +2995,7 @@ def __top_level__(): r12 :: str r13 :: object r14 :: str - r15 :: bool + r15 :: int32 r16 :: None L0: r0 = builtins :: module @@ -3021,7 +3021,7 @@ L4: r12 = unicode_2 :: static ('Callable') r13 = getattr r10, r12 r14 = unicode_2 :: static ('Callable') - r15 = r11.__setitem__(r14, r13) :: dict + r15 = CPyDict_SetItem(r11, r14, r13) r16 = None return r16 diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index 99880d75de17..eabe0c3f45ea 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -333,11 +333,11 @@ def __top_level__(): r12 :: str r13 :: object r14 :: str - r15 :: bool + r15 :: int32 r16 :: str r17 :: object r18 :: str - r19 :: bool + r19 :: int32 r20, r21 :: object r22 :: bool r23 :: str @@ -346,14 +346,14 @@ def __top_level__(): r27 :: str r28 :: object r29 :: str - r30 :: bool + r30 :: int32 r31 :: str r32 :: dict r33 :: str r34, r35 :: object r36 :: dict r37 :: str - r38 :: bool + r38 :: int32 r39 :: object r40 :: str r41, r42 :: object @@ -363,7 +363,7 @@ def __top_level__(): r46 :: bool r47 :: dict r48 :: str - r49 :: bool + r49 :: int32 r50 :: object r51 :: str r52, r53 :: object @@ -372,7 +372,7 @@ def __top_level__(): r56 :: bool r57 :: dict r58 :: str - r59 :: bool + r59 :: int32 r60, r61 :: object r62 :: dict r63 :: str @@ -389,7 +389,7 @@ def __top_level__(): r77 :: bool r78 :: dict r79 :: str - r80 :: bool + r80 :: int32 r81 :: None L0: r0 = builtins :: module @@ -415,11 +415,11 @@ L4: r12 = unicode_2 :: static ('TypeVar') r13 = getattr r10, r12 r14 = unicode_2 :: static ('TypeVar') - r15 = r11.__setitem__(r14, r13) :: dict + r15 = CPyDict_SetItem(r11, r14, r13) r16 = unicode_3 :: static ('Generic') r17 = getattr r10, r16 r18 = unicode_3 :: static ('Generic') - r19 = r11.__setitem__(r18, r17) :: dict + r19 = CPyDict_SetItem(r11, r18, r17) r20 = mypy_extensions :: module r21 = builtins.None :: object r22 = r20 is not r21 @@ -434,15 +434,15 @@ L6: r27 = unicode_5 :: static ('trait') r28 = getattr r25, r27 r29 = unicode_5 :: static ('trait') - r30 = r26.__setitem__(r29, r28) :: dict + r30 = CPyDict_SetItem(r26, r29, r28) r31 = unicode_6 :: static ('T') r32 = __main__.globals :: static r33 = unicode_2 :: static ('TypeVar') - r34 = r32[r33] :: dict + r34 = CPyDict_GetItem(r32, r33) r35 = py_call(r34, r31) r36 = __main__.globals :: static r37 = unicode_6 :: static ('T') - r38 = r36.__setitem__(r37, r35) :: dict + r38 = CPyDict_SetItem(r36, r37, r35) r39 = :: object r40 = unicode_7 :: static ('__main__') r41 = __main__.C_template :: type @@ -454,7 +454,7 @@ L6: __main__.C = r42 :: type r47 = __main__.globals :: static r48 = unicode_9 :: static ('C') - r49 = r47.__setitem__(r48, r42) :: dict + r49 = CPyDict_SetItem(r47, r48, r42) r50 = :: object r51 = unicode_7 :: static ('__main__') r52 = __main__.S_template :: type @@ -465,15 +465,15 @@ L6: __main__.S = r53 :: type r57 = __main__.globals :: static r58 = unicode_10 :: static ('S') - r59 = r57.__setitem__(r58, r53) :: dict + r59 = CPyDict_SetItem(r57, r58, r53) r60 = __main__.C :: type r61 = __main__.S :: type r62 = __main__.globals :: static r63 = unicode_3 :: static ('Generic') - r64 = r62[r63] :: dict + r64 = CPyDict_GetItem(r62, r63) r65 = __main__.globals :: static r66 = unicode_6 :: static ('T') - r67 = r65[r66] :: dict + r67 = CPyDict_GetItem(r65, r66) r68 = r64[r67] :: object r69 = (r60, r61, r68) :: tuple r70 = unicode_7 :: static ('__main__') @@ -487,7 +487,7 @@ L6: __main__.D = r72 :: type r78 = __main__.globals :: static r79 = unicode_12 :: static ('D') - r80 = r78.__setitem__(r79, r72) :: dict + r80 = CPyDict_SetItem(r78, r79, r72) r81 = None return r81 @@ -1047,7 +1047,7 @@ L0: __mypyc_self__.x = r0; r1 = is_error r2 = __main__.globals :: static r3 = unicode_9 :: static ('LOL') - r4 = r2[r3] :: dict + r4 = CPyDict_GetItem(r2, r3) r5 = cast(str, r4) __mypyc_self__.y = r5; r6 = is_error r7 = None @@ -1068,10 +1068,10 @@ def foo(x: WelpDict) -> None: [out] def foo(x): x :: dict - r0 :: bool + r0 :: int32 r1, r2 :: None L0: - r0 = x.update(x) :: dict + r0 = CPyDict_Update(x, x) r1 = None r2 = None return r2 diff --git a/mypyc/test-data/irbuild-dict.test b/mypyc/test-data/irbuild-dict.test index 5844d12ed095..a5a47e0fd3bb 100644 --- a/mypyc/test-data/irbuild-dict.test +++ b/mypyc/test-data/irbuild-dict.test @@ -11,7 +11,7 @@ def f(d): L0: r0 = 0 r1 = box(short_int, r0) - r2 = d[r1] :: dict + r2 = CPyDict_GetItem(d, r1) r3 = unbox(bool, r2) return r3 @@ -25,14 +25,14 @@ def f(d): r0 :: bool r1 :: short_int r2, r3 :: object - r4 :: bool + r4 :: int32 r5 :: None L0: r0 = False r1 = 0 r2 = box(short_int, r1) r3 = box(bool, r0) - r4 = d.__setitem__(r2, r3) :: dict + r4 = CPyDict_SetItem(d, r2, r3) r5 = None return r5 @@ -140,10 +140,10 @@ def f(a: Dict[int, int], b: Dict[int, int]) -> None: [out] def f(a, b): a, b :: dict - r0 :: bool + r0 :: int32 r1, r2 :: None L0: - r0 = a.update(b) :: dict + r0 = CPyDict_Update(a, b) r1 = None r2 = None return r2 @@ -168,14 +168,15 @@ def increment(d): r9 :: object r10 :: short_int r11, r12 :: object - r13, r14, r15 :: bool + r13 :: int32 + r14, r15 :: bool L0: r0 = 0 r1 = r0 r2 = len d :: dict - r3 = key_iter d :: dict + r3 = CPyDict_GetKeysIter(d) L1: - r4 = next_key r3, offset=r1 + r4 = CPyDict_NextKey(r3, r1) r5 = r4[1] r1 = r5 r6 = r4[0] @@ -184,13 +185,13 @@ L2: r7 = r4[2] r8 = cast(str, r7) k = r8 - r9 = d[k] :: dict + r9 = CPyDict_GetItem(d, k) r10 = 1 r11 = box(short_int, r10) r12 = r9 += r11 - r13 = d.__setitem__(k, r12) :: dict + r13 = CPyDict_SetItem(d, k, r12) L3: - r14 = assert size(d) == r2 + r14 = CPyDict_CheckSize(d, r2) goto L1 L4: r15 = no_err_occurred @@ -211,9 +212,9 @@ def f(x, y): r3 :: native_int r4 :: object r5 :: dict - r6 :: bool + r6 :: int32 r7 :: object - r8 :: bool + r8 :: int32 L0: r0 = 2 r1 = unicode_3 :: static ('z') @@ -221,9 +222,9 @@ L0: r3 = 1 r4 = box(short_int, r0) r5 = CPyDict_Build(r3, x, r4) - r6 = r5.update(y) (display) :: dict + r6 = CPyDict_UpdateInDisplay(r5, y) r7 = box(short_int, r2) - r8 = r5.__setitem__(r1, r7) :: dict + r8 = CPyDict_SetItem(r5, r1, r7) return r5 [case testDictIterationMethods] @@ -259,15 +260,16 @@ def print_dict_methods(d1, d2): r22, r23 :: object r24, r25, k :: int r26, r27, r28, r29, r30 :: object - r31, r32, r33 :: bool + r31 :: int32 + r32, r33 :: bool r34 :: None L0: r0 = 0 r1 = r0 r2 = len d1 :: dict - r3 = value_iter d1 :: dict + r3 = CPyDict_GetValuesIter(d1) L1: - r4 = next_value r3, offset=r1 + r4 = CPyDict_NextValue(r3, r1) r5 = r4[1] r1 = r5 r6 = r4[0] @@ -285,7 +287,7 @@ L3: return r12 L4: L5: - r13 = assert size(d1) == r2 + r13 = CPyDict_CheckSize(d1, r2) goto L1 L6: r14 = no_err_occurred @@ -293,9 +295,9 @@ L7: r15 = 0 r16 = r15 r17 = len d2 :: dict - r18 = item_iter d2 :: dict + r18 = CPyDict_GetItemsIter(d2) L8: - r19 = next_item r18, offset=r16 + r19 = CPyDict_NextItem(r18, r16) r20 = r19[1] r16 = r20 r21 = r19[0] @@ -308,13 +310,13 @@ L9: k = r24 v = r25 r26 = box(int, k) - r27 = d2[r26] :: dict + r27 = CPyDict_GetItem(d2, r26) r28 = box(int, v) r29 = r27 += r28 r30 = box(int, k) - r31 = d2.__setitem__(r30, r29) :: dict + r31 = CPyDict_SetItem(d2, r30, r29) L10: - r32 = assert size(d2) == r17 + r32 = CPyDict_CheckSize(d2, r17) goto L8 L11: r33 = no_err_occurred diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test index dcf11aeae641..2db12c6a4975 100644 --- a/mypyc/test-data/irbuild-lists.test +++ b/mypyc/test-data/irbuild-lists.test @@ -153,11 +153,11 @@ def f(a, x): a :: list x :: int r0 :: object - r1 :: bool + r1 :: int32 r2, r3 :: None L0: r0 = box(int, x) - r1 = a.append(r0) :: list + r1 = PyList_Append(a, r0) r2 = None r3 = None return r3 @@ -213,7 +213,7 @@ def f(x, y): r3, r4 :: object r5 :: list r6, r7, r8 :: object - r9 :: bool + r9 :: int32 L0: r0 = 1 r1 = 2 @@ -221,9 +221,9 @@ L0: r3 = box(short_int, r0) r4 = box(short_int, r1) r5 = [r3, r4] - r6 = r5.extend(x) :: list - r7 = r5.extend(y) :: list + r6 = CPyList_Extend(r5, x) + r7 = CPyList_Extend(r5, y) r8 = box(short_int, r2) - r9 = r5.append(r8) :: list + r9 = PyList_Append(r5, r8) return r5 diff --git a/mypyc/test-data/irbuild-set.test b/mypyc/test-data/irbuild-set.test index 5a3babb2967f..9ffee68de5b2 100644 --- a/mypyc/test-data/irbuild-set.test +++ b/mypyc/test-data/irbuild-set.test @@ -7,22 +7,22 @@ def f(): r0, r1, r2 :: short_int r3 :: set r4 :: object - r5 :: bool + r5 :: int32 r6 :: object - r7 :: bool + r7 :: int32 r8 :: object - r9 :: bool + r9 :: int32 L0: r0 = 1 r1 = 2 r2 = 3 r3 = set r4 = box(short_int, r0) - r5 = r3.add(r4) :: set + r5 = PySet_Add(r3, r4) r6 = box(short_int, r1) - r7 = r3.add(r6) :: set + r7 = PySet_Add(r3, r6) r8 = box(short_int, r2) - r9 = r3.add(r8) :: set + r9 = PySet_Add(r3, r8) return r3 [case testNewEmptySet] @@ -57,11 +57,11 @@ def f(): r0, r1, r2 :: short_int r3 :: set r4 :: object - r5 :: bool + r5 :: int32 r6 :: object - r7 :: bool + r7 :: int32 r8 :: object - r9 :: bool + r9 :: int32 r10 :: int L0: r0 = 1 @@ -69,11 +69,11 @@ L0: r2 = 3 r3 = set r4 = box(short_int, r0) - r5 = r3.add(r4) :: set + r5 = PySet_Add(r3, r4) r6 = box(short_int, r1) - r7 = r3.add(r6) :: set + r7 = PySet_Add(r3, r6) r8 = box(short_int, r2) - r9 = r3.add(r8) :: set + r9 = PySet_Add(r3, r8) r10 = len r3 :: set return r10 @@ -87,26 +87,28 @@ def f(): r0, r1 :: short_int r2 :: set r3 :: object - r4 :: bool + r4 :: int32 r5 :: object - r6 :: bool + r6 :: int32 x :: set r7 :: short_int r8 :: object - r9 :: bool + r9 :: int32 + r10 :: bool L0: r0 = 3 r1 = 4 r2 = set r3 = box(short_int, r0) - r4 = r2.add(r3) :: set + r4 = PySet_Add(r2, r3) r5 = box(short_int, r1) - r6 = r2.add(r5) :: set + r6 = PySet_Add(r2, r5) x = r2 r7 = 5 r8 = box(short_int, r7) - r9 = r8 in x :: set - return r9 + r9 = PySet_Contains(x, r8) + r10 = truncate r9: int32 to builtins.bool + return r10 [case testSetRemove] from typing import Set @@ -163,14 +165,14 @@ def f(): r0, x :: set r1 :: short_int r2 :: object - r3 :: bool + r3 :: int32 r4 :: None L0: r0 = set x = r0 r1 = 1 r2 = box(short_int, r1) - r3 = x.add(r2) :: set + r3 = PySet_Add(x, r2) r4 = None return x @@ -183,12 +185,12 @@ def f() -> Set[int]: [out] def f(): r0, x :: set - r1 :: bool + r1 :: int32 r2 :: None L0: r0 = set x = r0 - r1 = x.clear() :: set + r1 = PySet_Clear(x) r2 = None return x @@ -214,10 +216,10 @@ def update(s: Set[int], x: List[int]) -> None: def update(s, x): s :: set x :: list - r0 :: bool + r0 :: int32 r1, r2 :: None L0: - r0 = s.update(x) :: set + r0 = _PySet_Update(s, x) r1 = None r2 = None return r2 @@ -232,23 +234,23 @@ def f(x, y): r0, r1, r2 :: short_int r3 :: set r4 :: object - r5 :: bool + r5 :: int32 r6 :: object - r7, r8, r9 :: bool + r7, r8, r9 :: int32 r10 :: object - r11 :: bool + r11 :: int32 L0: r0 = 1 r1 = 2 r2 = 3 r3 = set r4 = box(short_int, r0) - r5 = r3.add(r4) :: set + r5 = PySet_Add(r3, r4) r6 = box(short_int, r1) - r7 = r3.add(r6) :: set - r8 = r3.update(x) :: set - r9 = r3.update(y) :: set + r7 = PySet_Add(r3, r6) + r8 = _PySet_Update(r3, x) + r9 = _PySet_Update(r3, y) r10 = box(short_int, r2) - r11 = r3.add(r10) :: set + r11 = PySet_Add(r3, r10) return r3 diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index b385edfc4f17..596e6671da5e 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -312,9 +312,9 @@ L0: r0 = 0 r1 = r0 r2 = len d :: dict - r3 = key_iter d :: dict + r3 = CPyDict_GetKeysIter(d) L1: - r4 = next_key r3, offset=r1 + r4 = CPyDict_NextKey(r3, r1) r5 = r4[1] r1 = r5 r6 = r4[0] @@ -324,10 +324,10 @@ L2: r8 = unbox(int, r7) key = r8 r9 = box(int, key) - r10 = d[r9] :: dict + r10 = CPyDict_GetItem(d, r9) r11 = unbox(int, r10) L3: - r12 = assert size(d) == r2 + r12 = CPyDict_CheckSize(d, r2) goto L1 L4: r13 = no_err_occurred @@ -373,9 +373,9 @@ L0: r1 = 0 r2 = r1 r3 = len d :: dict - r4 = key_iter d :: dict + r4 = CPyDict_GetKeysIter(d) L1: - r5 = next_key r4, offset=r2 + r5 = CPyDict_NextKey(r4, r2) r6 = r5[1] r2 = r6 r7 = r5[0] @@ -385,7 +385,7 @@ L2: r9 = unbox(int, r8) key = r9 r10 = box(int, key) - r11 = d[r10] :: dict + r11 = CPyDict_GetItem(d, r10) r12 = unbox(int, r11) r13 = 2 r14 = CPyTagged_Remainder(r12, r13) @@ -396,12 +396,12 @@ L3: goto L5 L4: r17 = box(int, key) - r18 = d[r17] :: dict + r18 = CPyDict_GetItem(d, r17) r19 = unbox(int, r18) r20 = CPyTagged_Add(s, r19) s = r20 L5: - r21 = assert size(d) == r3 + r21 = CPyDict_CheckSize(d, r3) goto L1 L6: r22 = no_err_occurred diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index 0d9f255dc606..477d387a369b 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -111,7 +111,7 @@ def f(x, y): r3, r4 :: object r5 :: list r6, r7, r8 :: object - r9 :: bool + r9 :: int32 r10 :: tuple L0: r0 = 1 @@ -120,10 +120,10 @@ L0: r3 = box(short_int, r0) r4 = box(short_int, r1) r5 = [r3, r4] - r6 = r5.extend(x) :: list - r7 = r5.extend(y) :: list + r6 = CPyList_Extend(r5, x) + r7 = CPyList_Extend(r5, y) r8 = box(short_int, r2) - r9 = r5.append(r8) :: list + r9 = PyList_Append(r5, r8) r10 = PyList_AsTuple(r5) return r10 diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index c356d8fa23a5..3729a6fe207b 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -770,12 +770,12 @@ def f(a, x): a :: list x :: int r0 :: object - r1 :: bool + r1 :: int32 r2, r3 :: None L0: inc_ref x :: int r0 = box(int, x) - r1 = a.append(r0) :: list + r1 = PyList_Append(a, r0) dec_ref r0 r2 = None r3 = None @@ -806,9 +806,9 @@ L0: r0 = 0 r1 = r0 r2 = len d :: dict - r3 = key_iter d :: dict + r3 = CPyDict_GetKeysIter(d) L1: - r4 = next_key r3, offset=r1 + r4 = CPyDict_NextKey(r3, r1) r5 = r4[1] r1 = r5 r6 = r4[0] @@ -820,13 +820,13 @@ L2: dec_ref r7 key = r8 r9 = box(int, key) - r10 = d[r9] :: dict + r10 = CPyDict_GetItem(d, r9) dec_ref r9 r11 = unbox(int, r10) dec_ref r10 dec_ref r11 :: int L3: - r12 = assert size(d) == r2 + r12 = CPyDict_CheckSize(d, r2) goto L1 L4: r13 = no_err_occurred diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index 5277427a95b2..e182275ae070 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -191,8 +191,10 @@ def test_new_list(self) -> None: """) def test_list_append(self) -> None: - self.assert_emit(PrimitiveOp([self.l, self.o], list_append_op, 1), - """cpy_r_r0 = PyList_Append(cpy_r_l, cpy_r_o) >= 0;""") + self.assert_emit(CallC(list_append_op.c_function_name, [self.l, self.o], + list_append_op.return_type, list_append_op.steals, + list_append_op.error_kind, 1), + """cpy_r_r0 = PyList_Append(cpy_r_l, cpy_r_o);""") def test_get_attr(self) -> None: self.assert_emit( @@ -216,16 +218,22 @@ def test_set_attr(self) -> None: """) def test_dict_get_item(self) -> None: - self.assert_emit(PrimitiveOp([self.d, self.o2], dict_get_item_op, 1), + self.assert_emit(CallC(dict_get_item_op.c_function_name, [self.d, self.o2], + dict_get_item_op.return_type, dict_get_item_op.steals, + dict_get_item_op.error_kind, 1), """cpy_r_r0 = CPyDict_GetItem(cpy_r_d, cpy_r_o2);""") def test_dict_set_item(self) -> None: - self.assert_emit(PrimitiveOp([self.d, self.o, self.o2], dict_set_item_op, 1), - """cpy_r_r0 = CPyDict_SetItem(cpy_r_d, cpy_r_o, cpy_r_o2) >= 0;""") + self.assert_emit(CallC(dict_set_item_op.c_function_name, [self.d, self.o, self.o2], + dict_set_item_op.return_type, dict_set_item_op.steals, + dict_set_item_op.error_kind, 1), + """cpy_r_r0 = CPyDict_SetItem(cpy_r_d, cpy_r_o, cpy_r_o2);""") def test_dict_update(self) -> None: - self.assert_emit(PrimitiveOp([self.d, self.o], dict_update_op, 1), - """cpy_r_r0 = CPyDict_Update(cpy_r_d, cpy_r_o) >= 0;""") + self.assert_emit(CallC(dict_update_op.c_function_name, [self.d, self.o], + dict_update_op.return_type, dict_update_op.steals, + dict_update_op.error_kind, 1), + """cpy_r_r0 = CPyDict_Update(cpy_r_d, cpy_r_o);""") def test_new_dict(self) -> None: self.assert_emit(CallC(dict_new_op.c_function_name, [], dict_new_op.return_type, From 82634ed3008a3cbcd4eea466f58e4d65b4c78f3c Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Fri, 10 Jul 2020 19:03:35 +0800 Subject: [PATCH 056/138] [mypyc] Properly box int32/int64 (#9119) Follows #9110's comment. Using CPython API to box int32/int64 into PyObject*. --- mypyc/codegen/emit.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/mypyc/codegen/emit.py b/mypyc/codegen/emit.py index 7b26c3003c52..266a3131cdca 100644 --- a/mypyc/codegen/emit.py +++ b/mypyc/codegen/emit.py @@ -695,11 +695,10 @@ def emit_box(self, src: str, dest: str, typ: RType, declare_dest: bool = False, self.emit_lines('{}{} = Py_None;'.format(declaration, dest)) if not can_borrow: self.emit_inc_ref(dest, object_rprimitive) - # TODO: This is a hack to handle mypy's false negative on unreachable code. - # All ops returning int32/int64 should not be coerced into object. - # Since their result will not be used elsewhere, it's safe to use NULL here - elif is_int32_rprimitive(typ) or is_int64_rprimitive(typ): - self.emit_lines('{}{} = NULL;'.format(declaration, dest)) + elif is_int32_rprimitive(typ): + self.emit_line('{}{} = PyLong_FromLong({});'.format(declaration, dest, src)) + elif is_int64_rprimitive(typ): + self.emit_line('{}{} = PyLong_FromLongLong({});'.format(declaration, dest, src)) elif isinstance(typ, RTuple): self.declare_tuple_struct(typ) self.emit_line('{}{} = PyTuple_New({});'.format(declaration, dest, len(typ.types))) From f73834ef3590d45750feb2d9fd244eedf2672c37 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Fri, 10 Jul 2020 19:07:21 +0800 Subject: [PATCH 057/138] [mypyc] Low-level integer operations: integer equals (#9116) Relates to mypyc/mypyc#741, mypyc/mypyc#743. --- mypyc/irbuild/builder.py | 3 + mypyc/irbuild/expression.py | 6 +- mypyc/irbuild/ll_builder.py | 29 ++++++ mypyc/primitives/int_ops.py | 20 ++++- mypyc/test-data/analysis.test | 108 +++++++++++++++------- mypyc/test-data/refcount.test | 164 +++++++++++++++++++++++----------- mypyc/test/test_analysis.py | 5 +- 7 files changed, 247 insertions(+), 88 deletions(-) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index e1d50df98bde..1a465fa65e91 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -235,6 +235,9 @@ def call_c(self, desc: CFunctionDescription, args: List[Value], line: int) -> Va def binary_int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: int) -> Value: return self.builder.binary_int_op(type, lhs, rhs, op, line) + def compare_tagged(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: + return self.builder.compare_tagged(lhs, rhs, op, line) + @property def environment(self) -> Environment: return self.builder.environment diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index a52a7ee83479..c8db606eb52a 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -18,7 +18,7 @@ from mypyc.ir.ops import ( Value, TupleGet, TupleSet, PrimitiveOp, BasicBlock, OpDescription, Assign ) -from mypyc.ir.rtypes import RTuple, object_rprimitive, is_none_rprimitive +from mypyc.ir.rtypes import RTuple, object_rprimitive, is_none_rprimitive, is_int_rprimitive from mypyc.ir.func_ir import FUNC_CLASSMETHOD, FUNC_STATICMETHOD from mypyc.primitives.registry import name_ref_ops, CFunctionDescription from mypyc.primitives.generic_ops import iter_op @@ -27,6 +27,7 @@ from mypyc.primitives.tuple_ops import list_tuple_op from mypyc.primitives.dict_ops import dict_new_op, dict_set_item_op from mypyc.primitives.set_ops import new_set_op, set_add_op, set_update_op +from mypyc.primitives.int_ops import int_logical_op_mapping from mypyc.irbuild.specialize import specializers from mypyc.irbuild.builder import IRBuilder from mypyc.irbuild.for_helpers import translate_list_comprehension, comprehension_helper @@ -382,6 +383,9 @@ def transform_basic_comparison(builder: IRBuilder, left: Value, right: Value, line: int) -> Value: + if (is_int_rprimitive(left.type) and is_int_rprimitive(right.type) + and op in int_logical_op_mapping.keys()): + return builder.compare_tagged(left, right, op, line) negate = False if op == 'is not': op, negate = 'is', True diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 359dea60d068..bf9b71f83937 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -52,6 +52,7 @@ from mypyc.primitives.misc_ops import ( none_op, none_object_op, false_op, fast_isinstance_op, bool_op, type_is_op ) +from mypyc.primitives.int_ops import int_logical_op_mapping from mypyc.rt_subtype import is_runtime_subtype from mypyc.subtype import is_subtype from mypyc.sametype import is_same_type @@ -555,6 +556,34 @@ def binary_op(self, assert target, 'Unsupported binary operation: %s' % expr_op return target + def check_tagged_short_int(self, val: Value, line: int) -> Value: + """Check if a tagged integer is a short integer""" + int_tag = self.add(LoadInt(1, line, rtype=c_pyssize_t_rprimitive)) + bitwise_and = self.binary_int_op(c_pyssize_t_rprimitive, val, + int_tag, BinaryIntOp.AND, line) + zero = self.add(LoadInt(0, line, rtype=c_pyssize_t_rprimitive)) + check = self.binary_int_op(bool_rprimitive, bitwise_and, zero, BinaryIntOp.EQ, line) + return check + + def compare_tagged(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: + """Compare two tagged integers using given op""" + op_type, c_func_desc = int_logical_op_mapping[op] + result = self.alloc_temp(bool_rprimitive) + short_int_block, int_block, out = BasicBlock(), BasicBlock(), BasicBlock() + check = self.check_tagged_short_int(lhs, line) + branch = Branch(check, short_int_block, int_block, Branch.BOOL_EXPR) + branch.negated = False + self.add(branch) + self.activate_block(short_int_block) + eq = self.binary_int_op(bool_rprimitive, lhs, rhs, op_type, line) + self.add(Assign(result, eq, line)) + self.goto(out) + self.activate_block(int_block) + call = self.call_c(c_func_desc, [lhs, rhs], line) + self.add(Assign(result, call, line)) + self.goto_and_activate(out) + return result + def unary_op(self, lreg: Value, expr_op: str, diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index acd54ea38fb9..b0efd642890a 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -6,14 +6,15 @@ See also the documentation for mypyc.rtypes.int_rprimitive. """ -from mypyc.ir.ops import ERR_NEVER, ERR_MAGIC +from typing import Dict, Tuple +from mypyc.ir.ops import ERR_NEVER, ERR_MAGIC, BinaryIntOp from mypyc.ir.rtypes import ( int_rprimitive, bool_rprimitive, float_rprimitive, object_rprimitive, short_int_rprimitive, str_rprimitive, RType ) from mypyc.primitives.registry import ( name_ref_op, binary_op, custom_op, simple_emit, name_emit, - c_unary_op, CFunctionDescription, c_function_op, c_binary_op + c_unary_op, CFunctionDescription, c_function_op, c_binary_op, c_custom_op ) # These int constructors produce object_rprimitives that then need to be unboxed @@ -139,3 +140,18 @@ def int_unary_op(name: str, c_function_name: str) -> CFunctionDescription: int_neg_op = int_unary_op('-', 'CPyTagged_Negate') + +# integer comparsion operation implementation related: + +# description for equal operation on two boxed tagged integers +int_equal_ = c_custom_op( + arg_types=[int_rprimitive, int_rprimitive], + return_type=bool_rprimitive, + c_function_name='CPyTagged_IsEq_', + error_kind=ERR_NEVER) + +# provide mapping from textual op to short int's op variant and boxed int's description +# note these are not complete implementations +int_logical_op_mapping = { + '==': (BinaryIntOp.EQ, int_equal_) +} # type: Dict[str, Tuple[int, CFunctionDescription]] diff --git a/mypyc/test-data/analysis.test b/mypyc/test-data/analysis.test index 76c98e9fba40..db20f704a875 100644 --- a/mypyc/test-data/analysis.test +++ b/mypyc/test-data/analysis.test @@ -13,38 +13,62 @@ def f(a): r0 :: short_int x :: int r1 :: bool - r2 :: short_int + r2, r3, r4 :: native_int + r5, r6, r7 :: bool + r8 :: short_int y :: int - r3 :: short_int + r9 :: short_int z :: int - r4 :: None + r10 :: None L0: r0 = 1 x = r0 - r1 = CPyTagged_IsEq(x, a) - if r1 goto L1 else goto L2 :: bool -L1: r2 = 1 - y = r2 + r3 = x & r2 + r4 = 0 + r5 = r3 == r4 + if r5 goto L1 else goto L2 :: bool +L1: + r6 = x == a + r1 = r6 goto L3 L2: - r3 = 1 - z = r3 + r7 = CPyTagged_IsEq_(x, a) + r1 = r7 L3: - r4 = None - return r4 + if r1 goto L4 else goto L5 :: bool +L4: + r8 = 1 + y = r8 + goto L6 +L5: + r9 = 1 + z = r9 +L6: + r10 = None + return r10 (0, 0) {a} {a} (0, 1) {a} {a, x} (0, 2) {a, x} {a, x} (0, 3) {a, x} {a, x} +(0, 4) {a, x} {a, x} +(0, 5) {a, x} {a, x} +(0, 6) {a, x} {a, x} (1, 0) {a, x} {a, x} -(1, 1) {a, x} {a, x, y} -(1, 2) {a, x, y} {a, x, y} +(1, 1) {a, x} {a, r1, x} +(1, 2) {a, r1, x} {a, r1, x} (2, 0) {a, x} {a, x} -(2, 1) {a, x} {a, x, z} -(2, 2) {a, x, z} {a, x, z} -(3, 0) {a, x, y, z} {a, x, y, z} -(3, 1) {a, x, y, z} {a, x, y, z} +(2, 1) {a, x} {a, r1, x} +(2, 2) {a, r1, x} {a, r1, x} +(3, 0) {a, r1, x} {a, r1, x} +(4, 0) {a, r1, x} {a, r1, x} +(4, 1) {a, r1, x} {a, r1, x, y} +(4, 2) {a, r1, x, y} {a, r1, x, y} +(5, 0) {a, r1, x} {a, r1, x} +(5, 1) {a, r1, x} {a, r1, x, z} +(5, 2) {a, r1, x, z} {a, r1, x, z} +(6, 0) {a, r1, x, y, z} {a, r1, x, y, z} +(6, 1) {a, r1, x, y, z} {a, r1, x, y, z} [case testSimple_Liveness] def f(a: int) -> int: @@ -418,34 +442,58 @@ def f(a: int) -> int: def f(a): a :: int r0 :: bool - r1 :: short_int + r1, r2, r3 :: native_int + r4, r5, r6 :: bool + r7 :: short_int x :: int - r2, r3 :: short_int + r8, r9 :: short_int L0: - r0 = CPyTagged_IsEq(a, a) - if r0 goto L1 else goto L2 :: bool + r1 = 1 + r2 = a & r1 + r3 = 0 + r4 = r2 == r3 + if r4 goto L1 else goto L2 :: bool L1: - r1 = 2 - x = r1 - r2 = 1 - a = r2 + r5 = a == a + r0 = r5 goto L3 L2: - r3 = 1 - x = r3 + r6 = CPyTagged_IsEq_(a, a) + r0 = r6 L3: + if r0 goto L4 else goto L5 :: bool +L4: + r7 = 2 + x = r7 + r8 = 1 + a = r8 + goto L6 +L5: + r9 = 1 + x = r9 +L6: return x (0, 0) {a} {a} (0, 1) {a} {a} +(0, 2) {a} {a} +(0, 3) {a} {a} +(0, 4) {a} {a} (1, 0) {a} {a} (1, 1) {a} {a} (1, 2) {a} {a} -(1, 3) {a} {} -(1, 4) {} {} (2, 0) {a} {a} (2, 1) {a} {a} (2, 2) {a} {a} -(3, 0) {} {} +(3, 0) {a} {a} +(4, 0) {a} {a} +(4, 1) {a} {a} +(4, 2) {a} {a} +(4, 3) {a} {} +(4, 4) {} {} +(5, 0) {a} {a} +(5, 1) {a} {a} +(5, 2) {a} {a} +(6, 0) {} {} [case testLoop_BorrowedArgument] def f(a: int) -> int: diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index 3729a6fe207b..a63474b02766 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -217,31 +217,45 @@ def f(a: int) -> int: def f(a): a :: int r0 :: bool - r1, r2 :: short_int + r1, r2, r3 :: native_int + r4, r5, r6 :: bool + r7, r8 :: short_int x :: int - r3 :: short_int - r4, y :: int + r9 :: short_int + r10, y :: int L0: - r0 = CPyTagged_IsEq(a, a) - if r0 goto L1 else goto L2 :: bool -L1: r1 = 1 - a = r1 + r2 = a & r1 + r3 = 0 + r4 = r2 == r3 + if r4 goto L1 else goto L2 :: bool +L1: + r5 = a == a + r0 = r5 goto L3 L2: - r2 = 2 - x = r2 - dec_ref x :: int - goto L4 + r6 = CPyTagged_IsEq_(a, a) + r0 = r6 L3: - r3 = 1 - r4 = CPyTagged_Add(a, r3) + if r0 goto L4 else goto L5 :: bool +L4: + r7 = 1 + a = r7 + goto L6 +L5: + r8 = 2 + x = r8 + dec_ref x :: int + goto L7 +L6: + r9 = 1 + r10 = CPyTagged_Add(a, r9) dec_ref a :: int - y = r4 + y = r10 return y -L4: +L7: inc_ref a :: int - goto L3 + goto L6 [case testConditionalAssignToArgument2] def f(a: int) -> int: @@ -255,30 +269,44 @@ def f(a: int) -> int: def f(a): a :: int r0 :: bool - r1 :: short_int + r1, r2, r3 :: native_int + r4, r5, r6 :: bool + r7 :: short_int x :: int - r2, r3 :: short_int - r4, y :: int + r8, r9 :: short_int + r10, y :: int L0: - r0 = CPyTagged_IsEq(a, a) - if r0 goto L1 else goto L2 :: bool + r1 = 1 + r2 = a & r1 + r3 = 0 + r4 = r2 == r3 + if r4 goto L1 else goto L2 :: bool L1: - r1 = 2 - x = r1 - dec_ref x :: int - goto L4 + r5 = a == a + r0 = r5 + goto L3 L2: - r2 = 1 - a = r2 + r6 = CPyTagged_IsEq_(a, a) + r0 = r6 L3: - r3 = 1 - r4 = CPyTagged_Add(a, r3) + if r0 goto L4 else goto L5 :: bool +L4: + r7 = 2 + x = r7 + dec_ref x :: int + goto L7 +L5: + r8 = 1 + a = r8 +L6: + r9 = 1 + r10 = CPyTagged_Add(a, r9) dec_ref a :: int - y = r4 + y = r10 return y -L4: +L7: inc_ref a :: int - goto L3 + goto L6 [case testConditionalAssignToArgument3] def f(a: int) -> int: @@ -289,18 +317,32 @@ def f(a: int) -> int: def f(a): a :: int r0 :: bool - r1 :: short_int + r1, r2, r3 :: native_int + r4, r5, r6 :: bool + r7 :: short_int L0: - r0 = CPyTagged_IsEq(a, a) - if r0 goto L1 else goto L3 :: bool -L1: r1 = 1 - a = r1 + r2 = a & r1 + r3 = 0 + r4 = r2 == r3 + if r4 goto L1 else goto L2 :: bool +L1: + r5 = a == a + r0 = r5 + goto L3 L2: - return a + r6 = CPyTagged_IsEq_(a, a) + r0 = r6 L3: + if r0 goto L4 else goto L6 :: bool +L4: + r7 = 1 + a = r7 +L5: + return a +L6: inc_ref a :: int - goto L2 + goto L5 [case testAssignRegisterToItself] def f(a: int) -> int: @@ -501,8 +543,10 @@ def f(): r2 :: short_int z :: int r3 :: bool - r4 :: short_int - a, r5, r6 :: int + r4, r5, r6 :: native_int + r7, r8, r9 :: bool + r10 :: short_int + a, r11, r12 :: int L0: r0 = 1 x = r0 @@ -510,27 +554,39 @@ L0: y = r1 r2 = 3 z = r2 - r3 = CPyTagged_IsEq(z, z) - if r3 goto L3 else goto L4 :: bool + r4 = 1 + r5 = z & r4 + r6 = 0 + r7 = r5 == r6 + if r7 goto L1 else goto L2 :: bool L1: - return z + r8 = z == z + r3 = r8 + goto L3 L2: - r4 = 1 - a = r4 - r5 = CPyTagged_Add(x, y) + r9 = CPyTagged_IsEq_(z, z) + r3 = r9 +L3: + if r3 goto L6 else goto L7 :: bool +L4: + return z +L5: + r10 = 1 + a = r10 + r11 = CPyTagged_Add(x, y) dec_ref x :: int dec_ref y :: int - r6 = CPyTagged_Subtract(r5, a) - dec_ref r5 :: int + r12 = CPyTagged_Subtract(r11, a) + dec_ref r11 :: int dec_ref a :: int - return r6 -L3: + return r12 +L6: dec_ref x :: int dec_ref y :: int - goto L1 -L4: + goto L4 +L7: dec_ref z :: int - goto L2 + goto L5 [case testLoop] def f(a: int) -> int: diff --git a/mypyc/test/test_analysis.py b/mypyc/test/test_analysis.py index 02b20839f428..61c8356299d4 100644 --- a/mypyc/test/test_analysis.py +++ b/mypyc/test/test_analysis.py @@ -6,7 +6,7 @@ from mypy.test.config import test_temp_dir from mypy.errors import CompileError -from mypyc.common import TOP_LEVEL_NAME +from mypyc.common import TOP_LEVEL_NAME, IS_32_BIT_PLATFORM from mypyc import analysis from mypyc.transform import exceptions from mypyc.ir.func_ir import format_func @@ -29,6 +29,9 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: """Perform a data-flow analysis test case.""" with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase): + # replace native_int with platform specific ints + int_format_str = 'int32' if IS_32_BIT_PLATFORM else 'int64' + testcase.output = [s.replace('native_int', int_format_str) for s in testcase.output] try: ir = build_ir_for_single_file(testcase.input) except CompileError as e: From a5b72909e8ccc0eae63093905e647ce2fd338787 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Fri, 10 Jul 2020 19:29:59 +0800 Subject: [PATCH 058/138] [mypyc] Merge exception ops (#9121) Relates to mypyc/mypyc#734. --- mypyc/irbuild/for_helpers.py | 4 +- mypyc/irbuild/generator.py | 2 +- mypyc/irbuild/nonlocalcontrol.py | 6 +- mypyc/irbuild/statement.py | 22 ++-- mypyc/primitives/exc_ops.py | 83 ++++++------- mypyc/test-data/analysis.test | 55 +++++---- mypyc/test-data/exceptions.test | 36 +++--- mypyc/test-data/irbuild-basic.test | 4 +- mypyc/test-data/irbuild-dict.test | 6 +- mypyc/test-data/irbuild-statements.test | 10 +- mypyc/test-data/irbuild-try.test | 150 ++++++++++++------------ mypyc/test-data/refcount.test | 2 +- 12 files changed, 188 insertions(+), 192 deletions(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 38e75016b26e..65e6f97a7e89 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -386,7 +386,7 @@ def gen_cleanup(self) -> None: # an exception was raised during the loop, then err_reg wil be set to # True. If no_err_occurred_op returns False, then the exception will be # propagated using the ERR_FALSE flag. - self.builder.primitive_op(no_err_occurred_op, [], self.line) + self.builder.call_c(no_err_occurred_op, [], self.line) def unsafe_index( @@ -539,7 +539,7 @@ def gen_step(self) -> None: def gen_cleanup(self) -> None: # Same as for generic ForIterable. - self.builder.primitive_op(no_err_occurred_op, [], self.line) + self.builder.call_c(no_err_occurred_op, [], self.line) class ForDictionaryKeys(ForDictionaryCommon): diff --git a/mypyc/irbuild/generator.py b/mypyc/irbuild/generator.py index e09711b7e1f7..18443c1af3fe 100644 --- a/mypyc/irbuild/generator.py +++ b/mypyc/irbuild/generator.py @@ -114,7 +114,7 @@ def add_raise_exception_blocks_to_generator_class(builder: IRBuilder, line: int) builder.add_bool_branch(comparison, error_block, ok_block) builder.activate_block(error_block) - builder.primitive_op(raise_exception_with_tb_op, [exc_type, exc_val, exc_tb], line) + builder.call_c(raise_exception_with_tb_op, [exc_type, exc_val, exc_tb], line) builder.add(Unreachable()) builder.goto_and_activate(ok_block) diff --git a/mypyc/irbuild/nonlocalcontrol.py b/mypyc/irbuild/nonlocalcontrol.py index 2baacd6f372a..f19c376da4bc 100644 --- a/mypyc/irbuild/nonlocalcontrol.py +++ b/mypyc/irbuild/nonlocalcontrol.py @@ -99,7 +99,7 @@ def gen_return(self, builder: 'IRBuilder', value: Value, line: int) -> None: # StopIteration instead of using RaiseStandardError because # the obvious thing doesn't work if the value is a tuple # (???). - builder.primitive_op(set_stop_iteration_value, [value], NO_TRACEBACK_LINE_NO) + builder.call_c(set_stop_iteration_value, [value], NO_TRACEBACK_LINE_NO) builder.add(Unreachable()) builder.builder.pop_error_handler() @@ -159,7 +159,7 @@ def __init__(self, outer: NonlocalControl, saved: Union[Value, AssignmentTarget] self.saved = saved def gen_cleanup(self, builder: 'IRBuilder', line: int) -> None: - builder.primitive_op(restore_exc_info_op, [builder.read(self.saved)], line) + builder.call_c(restore_exc_info_op, [builder.read(self.saved)], line) class FinallyNonlocalControl(CleanupNonlocalControl): @@ -187,5 +187,5 @@ def gen_cleanup(self, builder: 'IRBuilder', line: int) -> None: target, cleanup = BasicBlock(), BasicBlock() builder.add(Branch(self.saved, target, cleanup, Branch.IS_ERROR)) builder.activate_block(cleanup) - builder.primitive_op(restore_exc_info_op, [self.saved], line) + builder.call_c(restore_exc_info_op, [self.saved], line) builder.goto_and_activate(target) diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py index ac88ecb04858..184458b85f31 100644 --- a/mypyc/irbuild/statement.py +++ b/mypyc/irbuild/statement.py @@ -238,7 +238,7 @@ def transform_continue_stmt(builder: IRBuilder, node: ContinueStmt) -> None: def transform_raise_stmt(builder: IRBuilder, s: RaiseStmt) -> None: if s.expr is None: - builder.primitive_op(reraise_exception_op, [], NO_TRACEBACK_LINE_NO) + builder.call_c(reraise_exception_op, [], NO_TRACEBACK_LINE_NO) builder.add(Unreachable()) return @@ -278,7 +278,7 @@ def transform_try_except(builder: IRBuilder, # exception is raised, based on the exception in exc_info. builder.builder.push_error_handler(double_except_block) builder.activate_block(except_entry) - old_exc = builder.maybe_spill(builder.primitive_op(error_catch_op, [], line)) + old_exc = builder.maybe_spill(builder.call_c(error_catch_op, [], line)) # Compile the except blocks with the nonlocal control flow overridden to clear exc_info builder.nonlocal_control.append( ExceptNonlocalControl(builder.nonlocal_control[-1], old_exc)) @@ -288,7 +288,7 @@ def transform_try_except(builder: IRBuilder, next_block = None if type: next_block, body_block = BasicBlock(), BasicBlock() - matches = builder.primitive_op( + matches = builder.call_c( exc_matches_op, [builder.accept(type)], type.line ) builder.add(Branch(matches, body_block, next_block, Branch.BOOL_EXPR)) @@ -297,7 +297,7 @@ def transform_try_except(builder: IRBuilder, target = builder.get_assignment_target(var) builder.assign( target, - builder.primitive_op(get_exc_value_op, [], var.line), + builder.call_c(get_exc_value_op, [], var.line), var.line ) handler_body() @@ -307,7 +307,7 @@ def transform_try_except(builder: IRBuilder, # Reraise the exception if needed if next_block: - builder.primitive_op(reraise_exception_op, [], NO_TRACEBACK_LINE_NO) + builder.call_c(reraise_exception_op, [], NO_TRACEBACK_LINE_NO) builder.add(Unreachable()) builder.nonlocal_control.pop() @@ -317,14 +317,14 @@ def transform_try_except(builder: IRBuilder, # restore the saved exc_info information and continue propagating # the exception if it exists. builder.activate_block(cleanup_block) - builder.primitive_op(restore_exc_info_op, [builder.read(old_exc)], line) + builder.call_c(restore_exc_info_op, [builder.read(old_exc)], line) builder.goto(exit_block) # Cleanup for if we leave except through a raised exception: # restore the saved exc_info information and continue propagating # the exception. builder.activate_block(double_except_block) - builder.primitive_op(restore_exc_info_op, [builder.read(old_exc)], line) + builder.call_c(restore_exc_info_op, [builder.read(old_exc)], line) builder.primitive_op(keep_propagating_op, [], NO_TRACEBACK_LINE_NO) builder.add(Unreachable()) @@ -402,7 +402,7 @@ def try_finally_entry_blocks(builder: IRBuilder, builder.add(LoadErrorValue(builder.ret_types[-1])) ) ) - builder.add(Assign(old_exc, builder.primitive_op(error_catch_op, [], -1))) + builder.add(Assign(old_exc, builder.call_c(error_catch_op, [], -1))) builder.goto(finally_block) return old_exc @@ -442,7 +442,7 @@ def try_finally_resolve_control(builder: IRBuilder, # Reraise the exception if there was one builder.activate_block(reraise) - builder.primitive_op(reraise_exception_op, [], NO_TRACEBACK_LINE_NO) + builder.call_c(reraise_exception_op, [], NO_TRACEBACK_LINE_NO) builder.add(Unreachable()) builder.builder.pop_error_handler() @@ -520,7 +520,7 @@ def transform_try_body() -> None: def get_sys_exc_info(builder: IRBuilder) -> List[Value]: - exc_info = builder.primitive_op(get_exc_info_op, [], -1) + exc_info = builder.call_c(get_exc_info_op, [], -1) return [builder.add(TupleGet(exc_info, i, -1)) for i in range(3)] @@ -557,7 +557,7 @@ def except_body() -> None: reraise_block ) builder.activate_block(reraise_block) - builder.primitive_op(reraise_exception_op, [], NO_TRACEBACK_LINE_NO) + builder.call_c(reraise_exception_op, [], NO_TRACEBACK_LINE_NO) builder.add(Unreachable()) builder.activate_block(out_block) diff --git a/mypyc/primitives/exc_ops.py b/mypyc/primitives/exc_ops.py index a42f8d3c0aa4..5b48b5bb8752 100644 --- a/mypyc/primitives/exc_ops.py +++ b/mypyc/primitives/exc_ops.py @@ -3,7 +3,7 @@ from mypyc.ir.ops import ERR_NEVER, ERR_FALSE, ERR_ALWAYS from mypyc.ir.rtypes import bool_rprimitive, object_rprimitive, void_rtype, exc_rtuple from mypyc.primitives.registry import ( - simple_emit, call_emit, call_void_emit, call_and_fail_emit, custom_op, c_custom_op + simple_emit, custom_op, c_custom_op ) # If the argument is a class, raise an instance of the class. Otherwise, assume @@ -15,37 +15,33 @@ error_kind=ERR_ALWAYS) # Raise StopIteration exception with the specified value (which can be NULL). -set_stop_iteration_value = custom_op( +set_stop_iteration_value = c_custom_op( arg_types=[object_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_FALSE, - format_str='set_stop_iteration_value({args[0]}); {dest} = 0', - emit=call_and_fail_emit('CPyGen_SetStopIterationValue')) + return_type=void_rtype, + c_function_name='CPyGen_SetStopIterationValue', + error_kind=ERR_ALWAYS) # Raise exception with traceback. # Arguments are (exception type, exception value, traceback). -raise_exception_with_tb_op = custom_op( +raise_exception_with_tb_op = c_custom_op( arg_types=[object_rprimitive, object_rprimitive, object_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_FALSE, - format_str='raise_exception_with_tb({args[0]}, {args[1]}, {args[2]}); {dest} = 0', - emit=call_and_fail_emit('CPyErr_SetObjectAndTraceback')) + return_type=void_rtype, + c_function_name='CPyErr_SetObjectAndTraceback', + error_kind=ERR_ALWAYS) # Reraise the currently raised exception. -reraise_exception_op = custom_op( +reraise_exception_op = c_custom_op( arg_types=[], - result_type=bool_rprimitive, - error_kind=ERR_FALSE, - format_str='reraise_exc; {dest} = 0', - emit=call_and_fail_emit('CPy_Reraise')) + return_type=void_rtype, + c_function_name='CPy_Reraise', + error_kind=ERR_ALWAYS) # Propagate exception if the CPython error indicator is set (an exception was raised). -no_err_occurred_op = custom_op( +no_err_occurred_op = c_custom_op( arg_types=[], - result_type=bool_rprimitive, - error_kind=ERR_FALSE, - format_str='{dest} = no_err_occurred', - emit=call_emit('CPy_NoErrOccured')) + return_type=bool_rprimitive, + c_function_name='CPy_NoErrOccured', + error_kind=ERR_FALSE) # Assert that the error indicator has been set. assert_err_occured_op = custom_op( @@ -68,42 +64,37 @@ # handled exception" (by sticking it into sys.exc_info()). Returns the # exception that was previously being handled, which must be restored # later. -error_catch_op = custom_op( +error_catch_op = c_custom_op( arg_types=[], - result_type=exc_rtuple, - error_kind=ERR_NEVER, - format_str='{dest} = error_catch', - emit=call_emit('CPy_CatchError')) + return_type=exc_rtuple, + c_function_name='CPy_CatchError', + error_kind=ERR_NEVER) # Restore an old "currently handled exception" returned from. # error_catch (by sticking it into sys.exc_info()) -restore_exc_info_op = custom_op( +restore_exc_info_op = c_custom_op( arg_types=[exc_rtuple], - result_type=void_rtype, - error_kind=ERR_NEVER, - format_str='restore_exc_info {args[0]}', - emit=call_void_emit('CPy_RestoreExcInfo')) + return_type=void_rtype, + c_function_name='CPy_RestoreExcInfo', + error_kind=ERR_NEVER) # Checks whether the exception currently being handled matches a particular type. -exc_matches_op = custom_op( +exc_matches_op = c_custom_op( arg_types=[object_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_NEVER, - format_str='{dest} = exc_matches {args[0]}', - emit=call_emit('CPy_ExceptionMatches')) + return_type=bool_rprimitive, + c_function_name='CPy_ExceptionMatches', + error_kind=ERR_NEVER) # Get the value of the exception currently being handled. -get_exc_value_op = custom_op( +get_exc_value_op = c_custom_op( arg_types=[], - result_type=object_rprimitive, - error_kind=ERR_NEVER, - format_str='{dest} = get_exc_value', - emit=call_emit('CPy_GetExcValue')) + return_type=object_rprimitive, + c_function_name='CPy_GetExcValue', + error_kind=ERR_NEVER) # Get exception info (exception type, exception instance, traceback object). -get_exc_info_op = custom_op( +get_exc_info_op = c_custom_op( arg_types=[], - result_type=exc_rtuple, - error_kind=ERR_NEVER, - format_str='{dest} = get_exc_info', - emit=call_emit('CPy_GetExcInfo')) + return_type=exc_rtuple, + c_function_name='CPy_GetExcInfo', + error_kind=ERR_NEVER) diff --git a/mypyc/test-data/analysis.test b/mypyc/test-data/analysis.test index db20f704a875..dde8430f18eb 100644 --- a/mypyc/test-data/analysis.test +++ b/mypyc/test-data/analysis.test @@ -568,49 +568,51 @@ def lol(x): r5 :: bool r6 :: short_int r7 :: int - r8, r9 :: bool - r10 :: short_int - r11, r12 :: int + r8 :: bool + r9 :: short_int + r10, r11 :: int + r12 :: bool L0: L1: r0 = CPyTagged_Id(x) st = r0 goto L10 L2: - r1 = error_catch + r1 = CPy_CatchError() r2 = builtins :: module r3 = unicode_1 :: static ('Exception') r4 = getattr r2, r3 if is_error(r4) goto L8 (error at lol:4) else goto L3 L3: - r5 = exc_matches r4 + r5 = CPy_ExceptionMatches(r4) if r5 goto L4 else goto L5 :: bool L4: r6 = 1 r7 = CPyTagged_Negate(r6) - restore_exc_info r1 + CPy_RestoreExcInfo(r1) return r7 L5: - reraise_exc; r8 = 0 - if not r8 goto L8 else goto L6 :: bool + CPy_Reraise() + r12 = 0 + if not r12 goto L8 else goto L6 :: bool L6: unreachable L7: - restore_exc_info r1 + CPy_RestoreExcInfo(r1) goto L10 L8: - restore_exc_info r1 - r9 = keep_propagating - if not r9 goto L11 else goto L9 :: bool + CPy_RestoreExcInfo(r1) + r8 = keep_propagating + if not r8 goto L11 else goto L9 :: bool L9: unreachable L10: - r10 = 1 - r11 = CPyTagged_Add(st, r10) - return r11 + r9 = 1 + r10 = CPyTagged_Add(st, r9) + return r10 L11: - r12 = :: int - return r12 + r11 = :: int + return r11 (0, 0) {x} {x} (1, 0) {x} {r0} (1, 1) {r0} {st} @@ -626,18 +628,19 @@ L11: (4, 1) {r1, r6} {r1, r7} (4, 2) {r1, r7} {r7} (4, 3) {r7} {} -(5, 0) {r1} {r1, r8} -(5, 1) {r1, r8} {r1} +(5, 0) {r1} {r1} +(5, 1) {r1} {r1, r12} +(5, 2) {r1, r12} {r1} (6, 0) {} {} (7, 0) {r1, st} {st} (7, 1) {st} {st} (8, 0) {r1} {} -(8, 1) {} {r9} -(8, 2) {r9} {} +(8, 1) {} {r8} +(8, 2) {r8} {} (9, 0) {} {} -(10, 0) {st} {r10, st} -(10, 1) {r10, st} {r11} -(10, 2) {r11} {} -(11, 0) {} {r12} -(11, 1) {r12} {} +(10, 0) {st} {r9, st} +(10, 1) {r9, st} {r10} +(10, 2) {r10} {} +(11, 0) {} {r11} +(11, 1) {r11} {} diff --git a/mypyc/test-data/exceptions.test b/mypyc/test-data/exceptions.test index 0848943f8d4d..dd18356e22c4 100644 --- a/mypyc/test-data/exceptions.test +++ b/mypyc/test-data/exceptions.test @@ -202,7 +202,7 @@ L2: dec_ref r2 if is_error(r3) goto L3 (error at g:3) else goto L10 L3: - r4 = error_catch + r4 = CPy_CatchError() r5 = unicode_2 :: static ('weeee') r6 = builtins :: module r7 = unicode_3 :: static ('print') @@ -213,11 +213,11 @@ L4: dec_ref r8 if is_error(r9) goto L6 (error at g:5) else goto L11 L5: - restore_exc_info r4 + CPy_RestoreExcInfo(r4) dec_ref r4 goto L8 L6: - restore_exc_info r4 + CPy_RestoreExcInfo(r4) dec_ref r4 r10 = keep_propagating if not r10 goto L9 else goto L7 :: bool @@ -258,8 +258,9 @@ def a(): r12 :: object r13 :: str r14, r15 :: object - r16, r17 :: bool - r18 :: str + r16 :: bool + r17 :: str + r18 :: bool L0: L1: r0 = builtins :: module @@ -281,7 +282,7 @@ L4: L5: r9 = :: str r5 = r9 - r10 = error_catch + r10 = CPy_CatchError() r6 = r10 L6: r11 = unicode_3 :: static ('goodbye!') @@ -296,8 +297,9 @@ L7: L8: if is_error(r6) goto L11 else goto L9 L9: - reraise_exc; r16 = 0 - if not r16 goto L13 else goto L22 :: bool + CPy_Reraise() + r18 = 0 + if not r18 goto L13 else goto L22 :: bool L10: unreachable L11: @@ -309,18 +311,18 @@ L13: L14: if is_error(r6) goto L16 else goto L15 L15: - restore_exc_info r6 + CPy_RestoreExcInfo(r6) dec_ref r6 L16: - r17 = keep_propagating - if not r17 goto L19 else goto L17 :: bool + r16 = keep_propagating + if not r16 goto L19 else goto L17 :: bool L17: unreachable L18: unreachable L19: - r18 = :: str - return r18 + r17 = :: str + return r17 L20: dec_ref r3 goto L3 @@ -373,9 +375,9 @@ L2: st = r1 goto L4 L3: - r2 = error_catch + r2 = CPy_CatchError() r3 = unicode_4 :: static - restore_exc_info r2 + CPy_RestoreExcInfo(r2) dec_ref r2 inc_ref r3 return r3 @@ -418,9 +420,9 @@ L3: b = r3 goto L6 L4: - r4 = error_catch + r4 = CPy_CatchError() L5: - restore_exc_info r4 + CPy_RestoreExcInfo(r4) dec_ref r4 L6: if is_error(a) goto L17 else goto L9 diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index eac963c9adcc..5eb54b6e3383 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -3063,7 +3063,7 @@ L4: L5: goto L1 L6: - r8 = no_err_occurred + r8 = CPy_NoErrOccured() L7: L8: return r0 @@ -3096,7 +3096,7 @@ L4: L5: goto L1 L6: - r9 = no_err_occurred + r9 = CPy_NoErrOccured() L7: L8: return r0 diff --git a/mypyc/test-data/irbuild-dict.test b/mypyc/test-data/irbuild-dict.test index a5a47e0fd3bb..c4e5bfe185c5 100644 --- a/mypyc/test-data/irbuild-dict.test +++ b/mypyc/test-data/irbuild-dict.test @@ -194,7 +194,7 @@ L3: r14 = CPyDict_CheckSize(d, r2) goto L1 L4: - r15 = no_err_occurred + r15 = CPy_NoErrOccured() L5: return d @@ -290,7 +290,7 @@ L5: r13 = CPyDict_CheckSize(d1, r2) goto L1 L6: - r14 = no_err_occurred + r14 = CPy_NoErrOccured() L7: r15 = 0 r16 = r15 @@ -319,7 +319,7 @@ L10: r32 = CPyDict_CheckSize(d2, r17) goto L8 L11: - r33 = no_err_occurred + r33 = CPy_NoErrOccured() L12: r34 = None return r34 diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index 596e6671da5e..16660928f465 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -330,7 +330,7 @@ L3: r12 = CPyDict_CheckSize(d, r2) goto L1 L4: - r13 = no_err_occurred + r13 = CPy_NoErrOccured() L5: r14 = None return r14 @@ -404,7 +404,7 @@ L5: r21 = CPyDict_CheckSize(d, r3) goto L1 L6: - r22 = no_err_occurred + r22 = CPy_NoErrOccured() L7: return s @@ -906,7 +906,7 @@ L3: i = r6 goto L1 L4: - r7 = no_err_occurred + r7 = CPy_NoErrOccured() L5: r8 = None return r8 @@ -965,7 +965,7 @@ L6: r1 = r12 goto L1 L7: - r13 = no_err_occurred + r13 = CPy_NoErrOccured() L8: r14 = None return r14 @@ -1020,7 +1020,7 @@ L5: z = r17 goto L1 L6: - r18 = no_err_occurred + r18 = CPy_NoErrOccured() L7: r19 = None return r19 diff --git a/mypyc/test-data/irbuild-try.test b/mypyc/test-data/irbuild-try.test index f5cee4864957..6183ab9de1da 100644 --- a/mypyc/test-data/irbuild-try.test +++ b/mypyc/test-data/irbuild-try.test @@ -24,17 +24,17 @@ L1: r3 = py_call(r2) goto L5 L2: (handler for L1) - r4 = error_catch + r4 = CPy_CatchError() r5 = unicode_2 :: static ('weeee') r6 = builtins :: module r7 = unicode_3 :: static ('print') r8 = getattr r6, r7 r9 = py_call(r8, r5) L3: - restore_exc_info r4 + CPy_RestoreExcInfo(r4) goto L5 L4: (handler for L2) - restore_exc_info r4 + CPy_RestoreExcInfo(r4) r10 = keep_propagating unreachable L5: @@ -79,17 +79,17 @@ L3: L4: goto L8 L5: (handler for L1, L2, L3, L4) - r6 = error_catch + r6 = CPy_CatchError() r7 = unicode_3 :: static ('weeee') r8 = builtins :: module r9 = unicode_4 :: static ('print') r10 = getattr r8, r9 r11 = py_call(r10, r7) L6: - restore_exc_info r6 + CPy_RestoreExcInfo(r6) goto L8 L7: (handler for L5) - restore_exc_info r6 + CPy_RestoreExcInfo(r6) r12 = keep_propagating unreachable L8: @@ -124,14 +124,14 @@ def g(): r16 :: object r17 :: str r18, r19 :: object - r20, r21 :: bool - r22 :: tuple[object, object, object] - r23 :: str - r24 :: object - r25 :: str - r26, r27 :: object - r28 :: bool - r29 :: None + r20 :: bool + r21 :: tuple[object, object, object] + r22 :: str + r23 :: object + r24 :: str + r25, r26 :: object + r27 :: bool + r28 :: None L0: L1: r0 = unicode_1 :: static ('a') @@ -146,14 +146,14 @@ L2: r8 = py_call(r7) goto L8 L3: (handler for L2) - r9 = error_catch + r9 = CPy_CatchError() r10 = builtins :: module r11 = unicode_4 :: static ('AttributeError') r12 = getattr r10, r11 - r13 = exc_matches r12 + r13 = CPy_ExceptionMatches(r12) if r13 goto L4 else goto L5 :: bool L4: - r14 = get_exc_value + r14 = CPy_GetExcValue() e = r14 r15 = unicode_5 :: static ('b') r16 = builtins :: module @@ -162,34 +162,34 @@ L4: r19 = py_call(r18, r15, e) goto L6 L5: - reraise_exc; r20 = 0 + CPy_Reraise() unreachable L6: - restore_exc_info r9 + CPy_RestoreExcInfo(r9) goto L8 L7: (handler for L3, L4, L5) - restore_exc_info r9 - r21 = keep_propagating + CPy_RestoreExcInfo(r9) + r20 = keep_propagating unreachable L8: goto L12 L9: (handler for L1, L6, L7, L8) - r22 = error_catch - r23 = unicode_6 :: static ('weeee') - r24 = builtins :: module - r25 = unicode_2 :: static ('print') - r26 = getattr r24, r25 - r27 = py_call(r26, r23) + r21 = CPy_CatchError() + r22 = unicode_6 :: static ('weeee') + r23 = builtins :: module + r24 = unicode_2 :: static ('print') + r25 = getattr r23, r24 + r26 = py_call(r25, r22) L10: - restore_exc_info r22 + CPy_RestoreExcInfo(r21) goto L12 L11: (handler for L9) - restore_exc_info r22 - r28 = keep_propagating + CPy_RestoreExcInfo(r21) + r27 = keep_propagating unreachable L12: - r29 = None - return r29 + r28 = None + return r28 [case testTryExcept4] def g() -> None: @@ -217,17 +217,17 @@ def g(): r15 :: object r16 :: str r17, r18 :: object - r19, r20 :: bool - r21 :: None + r19 :: bool + r20 :: None L0: L1: goto L9 L2: (handler for L1) - r0 = error_catch + r0 = CPy_CatchError() r1 = builtins :: module r2 = unicode_1 :: static ('KeyError') r3 = getattr r1, r2 - r4 = exc_matches r3 + r4 = CPy_ExceptionMatches(r3) if r4 goto L3 else goto L4 :: bool L3: r5 = unicode_2 :: static ('weeee') @@ -240,7 +240,7 @@ L4: r10 = builtins :: module r11 = unicode_4 :: static ('IndexError') r12 = getattr r10, r11 - r13 = exc_matches r12 + r13 = CPy_ExceptionMatches(r12) if r13 goto L5 else goto L6 :: bool L5: r14 = unicode_5 :: static ('yo') @@ -250,18 +250,18 @@ L5: r18 = py_call(r17, r14) goto L7 L6: - reraise_exc; r19 = 0 + CPy_Reraise() unreachable L7: - restore_exc_info r0 + CPy_RestoreExcInfo(r0) goto L9 L8: (handler for L2, L3, L4, L5, L6) - restore_exc_info r0 - r20 = keep_propagating + CPy_RestoreExcInfo(r0) + r19 = keep_propagating unreachable L9: - r21 = None - return r21 + r20 = None + return r20 [case testTryFinally] def a(b: bool) -> None: @@ -282,8 +282,8 @@ def a(b): r9 :: object r10 :: str r11, r12 :: object - r13, r14 :: bool - r15 :: None + r13 :: bool + r14 :: None L0: L1: if b goto L2 else goto L3 :: bool @@ -302,7 +302,7 @@ L5: r5 = r6 goto L7 L6: (handler for L1, L2, L3) - r7 = error_catch + r7 = CPy_CatchError() r5 = r7 L7: r8 = unicode_3 :: static ('finally') @@ -312,20 +312,20 @@ L7: r12 = py_call(r11, r8) if is_error(r5) goto L9 else goto L8 L8: - reraise_exc; r13 = 0 + CPy_Reraise() unreachable L9: goto L13 L10: (handler for L7, L8) if is_error(r5) goto L12 else goto L11 L11: - restore_exc_info r5 + CPy_RestoreExcInfo(r5) L12: - r14 = keep_propagating + r13 = keep_propagating unreachable L13: - r15 = None - return r15 + r14 = None + return r14 [case testWith] from typing import Any @@ -349,11 +349,11 @@ def foo(x): r15 :: bool r16 :: tuple[object, object, object] r17, r18, r19, r20 :: object - r21, r22, r23 :: bool - r24, r25, r26 :: tuple[object, object, object] - r27, r28 :: object - r29, r30 :: bool - r31 :: None + r21, r22 :: bool + r23, r24, r25 :: tuple[object, object, object] + r26, r27 :: object + r28 :: bool + r29 :: None L0: r0 = py_call(x) r1 = type r0 :: object @@ -374,10 +374,10 @@ L2: r13 = py_call(r12, r9) goto L8 L3: (handler for L2) - r14 = error_catch + r14 = CPy_CatchError() r15 = False r8 = r15 - r16 = get_exc_info + r16 = CPy_GetExcInfo() r17 = r16[0] r18 = r16[1] r19 = r16[2] @@ -385,45 +385,45 @@ L3: (handler for L2) r21 = bool r20 :: object if r21 goto L5 else goto L4 :: bool L4: - reraise_exc; r22 = 0 + CPy_Reraise() unreachable L5: L6: - restore_exc_info r14 + CPy_RestoreExcInfo(r14) goto L8 L7: (handler for L3, L4, L5) - restore_exc_info r14 - r23 = keep_propagating + CPy_RestoreExcInfo(r14) + r22 = keep_propagating unreachable L8: L9: L10: - r25 = :: tuple[object, object, object] - r24 = r25 + r24 = :: tuple[object, object, object] + r23 = r24 goto L12 L11: (handler for L1, L6, L7, L8) - r26 = error_catch - r24 = r26 + r25 = CPy_CatchError() + r23 = r25 L12: if r8 goto L13 else goto L14 :: bool L13: - r27 = builtins.None :: object - r28 = py_call(r3, r0, r27, r27, r27) + r26 = builtins.None :: object + r27 = py_call(r3, r0, r26, r26, r26) L14: - if is_error(r24) goto L16 else goto L15 + if is_error(r23) goto L16 else goto L15 L15: - reraise_exc; r29 = 0 + CPy_Reraise() unreachable L16: goto L20 L17: (handler for L12, L13, L14, L15) - if is_error(r24) goto L19 else goto L18 + if is_error(r23) goto L19 else goto L18 L18: - restore_exc_info r24 + CPy_RestoreExcInfo(r23) L19: - r30 = keep_propagating + r28 = keep_propagating unreachable L20: - r31 = None - return r31 + r29 = None + return r29 diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index a63474b02766..216454e6d92c 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -885,7 +885,7 @@ L3: r12 = CPyDict_CheckSize(d, r2) goto L1 L4: - r13 = no_err_occurred + r13 = CPy_NoErrOccured() L5: r14 = None return r14 From 4026fcfbd3c17e1a534ac12d63e8cb1ec9cc4248 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 10 Jul 2020 19:51:03 +0100 Subject: [PATCH 059/138] [mypyc] Add default run test driver that calls all test_* functions (#9094) If a test case doesn't define driver.py, use a default driver that calls all functions named test_* in the compiled test case and reports any exceptions as failures. This has a few benefits over an explicit driver: 1. Writing test cases is easier, as a test (sub) case can be defined as one logical unit. 2. It's easy to define many sub test cases per compilation unit. By having larger test cases, tests can run faster. Each compilation unit has significant fixed overhead. Also point some errors to the relevant line number in the `.test` file to make figuring out errors easier, when we have long test case descriptions. Document how to write these tests. Migrate a few test cases as examples. I'll create follow-up PRs that merges various existing test cases into larger ones. It probably won't be worth it to try to migrate most existing test cases, though, since it would be a lot of effort. Work towards mypyc/mypyc#72. --- mypyc/doc/dev-intro.md | 71 ++++++++++++++--- mypyc/test-data/driver/driver.py | 41 ++++++++++ mypyc/test-data/fixtures/ir.py | 2 + mypyc/test-data/run-strings.test | 119 ++++++++++++++++++++++++++++ mypyc/test-data/run.test | 132 +------------------------------ mypyc/test/test_run.py | 46 ++++++++++- mypyc/test/testutil.py | 6 +- 7 files changed, 272 insertions(+), 145 deletions(-) create mode 100644 mypyc/test-data/driver/driver.py create mode 100644 mypyc/test-data/run-strings.test diff --git a/mypyc/doc/dev-intro.md b/mypyc/doc/dev-intro.md index 09f390b8252b..1e14d00645db 100644 --- a/mypyc/doc/dev-intro.md +++ b/mypyc/doc/dev-intro.md @@ -193,14 +193,14 @@ information. See the test cases in `mypyc/test-data/irbuild-basic.test` for examples of what the IR looks like in a pretty-printed form. -## Tests +## Testing overview -Mypyc test cases are defined in the same format (`.test`) as used for -test cases for mypy. Look at mypy developer documentation for a general -overview of how things work. Test cases live under `mypyc/test-data/`, -and you can run all mypyc tests via `pytest mypyc`. If you don't make -changes to code under `mypy/`, it's not important to regularly run mypy -tests during development. +Most mypyc test cases are defined in the same format (`.test`) as used +for test cases for mypy. Look at mypy developer documentation for a +general overview of how things work. Test cases live under +`mypyc/test-data/`, and you can run all mypyc tests via `pytest +mypyc`. If you don't make changes to code under `mypy/`, it's not +important to regularly run mypy tests during development. When you create a PR, we have Continuous Integration jobs set up that compile mypy using mypyc and run the mypy test suite using the @@ -208,6 +208,8 @@ compiled mypy. This will sometimes catch additional issues not caught by the mypyc test suite. It's okay to not do this in your local development environment. +We discuss writing tests in more detail later in this document. + ## Inspecting Generated IR It's often useful to look at the generated IR when debugging issues or @@ -290,10 +292,61 @@ what to do to implement specific kinds of mypyc features. Our bread-and-butter testing strategy is compiling code with mypyc and running it. There are downsides to this (kind of slow, tests a huge number of components at once, insensitive to the particular details of -the IR), but there really is no substitute for running code. +the IR), but there really is no substitute for running code. You can +also write tests that test the generated IR, however. + +### Tests that compile and run code Test cases that compile and run code are located in -`test-data/run*.test` and the test driver is in `mypyc.test.test_run`. +`test-data/run*.test` and the test runner is in `mypyc.test.test_run`. +The code to compile comes after `[case test]`. The code gets +saved into the file `native.py`, and it gets compiled into the module +`native`. + +Each test case uses a non-compiled Python driver that imports the +`native` module and typically calls some compiled functions. Some +tests also perform assertions and print messages in the driver. + +If you don't provide a driver, a default driver is used. The default +driver just calls each module-level function that is prefixed with +`test_` and reports any uncaught exceptions as failures. (Failure to +build or a segfault also count as failures.) `testStringOps` in +`mypyc/test-data/run-strings.test` is an example of a test that uses +the default driver. You should usually use the default driver. It's +the simplest way to write most tests. + +Here's an example test case that uses the default driver: + +``` +[case testConcatenateLists] +def test_concat_lists() -> None: + assert [1, 2] + [5, 6] == [1, 2, 5, 6] + +def test_concat_empty_lists() -> None: + assert [] + [] == [] +``` + +There is one test case, `testConcatenateLists`. It has two sub-cases, +`test_concat_lists` and `test_concat_empty_lists`. Note that you can +use the pytest -k argument to only run `testConcetanateLists`, but you +can't filter tests at the sub-case level. + +It's recommended to have multiple sub-cases per test case, since each +test case has significant fixed overhead. Each test case is run in a +fresh Python subprocess. + +Many of the existing test cases provide a custom driver by having +`[file driver.py]`, followed by the driver implementation. Here the +driver is not compiled, which is useful if you want to test +interactions between compiled and non-compiled code. However, many of +the tests don't have a good reason to use a custom driver -- when they +were written, the default driver wasn't available. + +Test cases can also have a `[out]` section, which specifies the +expected contents of stdout the test case should produce. New test +cases should prefer assert statements to `[out]` sections. + +### IR tests If the specifics of the generated IR of a change is important (because, for example, you want to make sure a particular optimization diff --git a/mypyc/test-data/driver/driver.py b/mypyc/test-data/driver/driver.py new file mode 100644 index 000000000000..4db9843358f1 --- /dev/null +++ b/mypyc/test-data/driver/driver.py @@ -0,0 +1,41 @@ +"""Default driver for run tests (run-*.test). + +This imports the 'native' module (containing the compiled test cases) +and calls each function starting with test_, and reports any +exceptions as failures. + +Test cases can provide a custom driver.py that overrides this file. +""" + +import sys +import native + +failures = [] + +for name in dir(native): + if name.startswith('test_'): + test_func = getattr(native, name) + try: + test_func() + except Exception as e: + failures.append(sys.exc_info()) + +if failures: + from traceback import print_exception, format_tb + import re + + def extract_line(tb): + formatted = '\n'.join(format_tb(tb)) + m = re.search('File "native.py", line ([0-9]+), in test_', formatted) + return m.group(1) + + # Sort failures by line number of test function. + failures = sorted(failures, key=lambda e: extract_line(e[2])) + + # If there are multiple failures, print stack traces of all but the final failure. + for e in failures[:-1]: + print_exception(*e) + print() + + # Raise exception for the last failure. Test runner will show the traceback. + raise failures[-1][1] diff --git a/mypyc/test-data/fixtures/ir.py b/mypyc/test-data/fixtures/ir.py index 7faca81dff40..e80ee29c29da 100644 --- a/mypyc/test-data/fixtures/ir.py +++ b/mypyc/test-data/fixtures/ir.py @@ -181,6 +181,8 @@ class UserWarning(Warning): pass class TypeError(Exception): pass +class ValueError(Exception): pass + class AttributeError(Exception): pass class NameError(Exception): pass diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test new file mode 100644 index 000000000000..0208b534c0a2 --- /dev/null +++ b/mypyc/test-data/run-strings.test @@ -0,0 +1,119 @@ +# Test cases for strings (compile and run) + +[case testStr] +def f() -> str: + return 'some string' +def g() -> str: + return 'some\a \v \t \x7f " \n \0string 🐍' +def tostr(x: int) -> str: + return str(x) +def booltostr(x: bool) -> str: + return str(x) +def concat(x: str, y: str) -> str: + return x + y +def eq(x: str) -> int: + if x == 'foo': + return 0 + elif x != 'bar': + return 1 + return 2 + +[file driver.py] +from native import f, g, tostr, booltostr, concat, eq +assert f() == 'some string' +assert g() == 'some\a \v \t \x7f " \n \0string 🐍' +assert tostr(57) == '57' +assert concat('foo', 'bar') == 'foobar' +assert booltostr(True) == 'True' +assert booltostr(False) == 'False' +assert eq('foo') == 0 +assert eq('zar') == 1 +assert eq('bar') == 2 + +assert int(tostr(0)) == 0 +assert int(tostr(20)) == 20 + +[case testStringOps] +from typing import List, Optional + +var = 'mypyc' + +num = 20 + +def test_fstring_simple() -> None: + f1 = f'Hello {var}, this is a test' + assert f1 == "Hello mypyc, this is a test" + +def test_fstring_conversion() -> None: + f2 = f'Hello {var!r}' + assert f2 == "Hello 'mypyc'" + f3 = f'Hello {var!a}' + assert f3 == "Hello 'mypyc'" + f4 = f'Hello {var!s}' + assert f4 == "Hello mypyc" + +def test_fstring_align() -> None: + f5 = f'Hello {var:>20}' + assert f5 == "Hello mypyc" + f6 = f'Hello {var!r:>20}' + assert f6 == "Hello 'mypyc'" + f7 = f'Hello {var:>{num}}' + assert f7 == "Hello mypyc" + f8 = f'Hello {var!r:>{num}}' + assert f8 == "Hello 'mypyc'" + +def test_fstring_multi() -> None: + f9 = f'Hello {var}, hello again {var}' + assert f9 == "Hello mypyc, hello again mypyc" + +def do_split(s: str, sep: Optional[str] = None, max_split: Optional[int] = None) -> List[str]: + if sep is not None: + if max_split is not None: + return s.split(sep, max_split) + else: + return s.split(sep) + return s.split() + +ss = "abc abcd abcde abcdef" + +def test_split() -> None: + assert do_split(ss) == ["abc", "abcd", "abcde", "abcdef"] + assert do_split(ss, " ") == ["abc", "abcd", "abcde", "abcdef"] + assert do_split(ss, "-") == ["abc abcd abcde abcdef"] + assert do_split(ss, " ", -1) == ["abc", "abcd", "abcde", "abcdef"] + assert do_split(ss, " ", 0) == ["abc abcd abcde abcdef"] + assert do_split(ss, " ", 1) == ["abc", "abcd abcde abcdef"] + assert do_split(ss, " ", 2) == ["abc", "abcd", "abcde abcdef"] + +def getitem(s: str, index: int) -> str: + return s[index] + +from testutil import assertRaises + +s = "abc" + +def test_getitem() -> None: + assert getitem(s, 0) == "a" + assert getitem(s, 1) == "b" + assert getitem(s, 2) == "c" + assert getitem(s, -3) == "a" + assert getitem(s, -2) == "b" + assert getitem(s, -1) == "c" + with assertRaises(IndexError, "string index out of range"): + getitem(s, 4) + with assertRaises(IndexError, "string index out of range"): + getitem(s, -4) + +def str_to_int(s: str, base: Optional[int] = None) -> int: + if base: + return int(s, base) + else: + return int(s) + +def test_str_to_int() -> None: + assert str_to_int("1") == 1 + assert str_to_int("10") == 10 + assert str_to_int("a", 16) == 10 + assert str_to_int("1a", 16) == 26 + with assertRaises(ValueError, "invalid literal for int() with base 10: 'xyz'"): + str_to_int("xyz") diff --git a/mypyc/test-data/run.test b/mypyc/test-data/run.test index 8c03352d22d6..ea0ab0405348 100644 --- a/mypyc/test-data/run.test +++ b/mypyc/test-data/run.test @@ -1,3 +1,4 @@ +# Misc test cases (compile and run) [case testCallTrivialFunction] def f(x: int) -> int: @@ -818,77 +819,6 @@ def g(x: int) -> int: from native import f assert f(1) == 2 -[case testStr] -def f() -> str: - return 'some string' -def g() -> str: - return 'some\a \v \t \x7f " \n \0string 🐍' -def tostr(x: int) -> str: - return str(x) -def booltostr(x: bool) -> str: - return str(x) -def concat(x: str, y: str) -> str: - return x + y -def eq(x: str) -> int: - if x == 'foo': - return 0 - elif x != 'bar': - return 1 - return 2 - -[file driver.py] -from native import f, g, tostr, booltostr, concat, eq -assert f() == 'some string' -assert g() == 'some\a \v \t \x7f " \n \0string 🐍' -assert tostr(57) == '57' -assert concat('foo', 'bar') == 'foobar' -assert booltostr(True) == 'True' -assert booltostr(False) == 'False' -assert eq('foo') == 0 -assert eq('zar') == 1 -assert eq('bar') == 2 - -assert int(tostr(0)) == 0 -assert int(tostr(20)) == 20 - -[case testFstring] - -var = 'mypyc' - -num = 20 - -f1 = f'Hello {var}, this is a test' - -f2 = f'Hello {var!r}' - -f3 = f'Hello {var!a}' - -f4 = f'Hello {var!s}' - -f5 = f'Hello {var:>20}' - -f6 = f'Hello {var!r:>20}' - -f7 = f'Hello {var:>{num}}' - -f8 = f'Hello {var!r:>{num}}' - -f9 = f'Hello {var}, hello again {var}' - -[file driver.py] -from native import f1, f2, f3, f4, f5, f6, f7, f8, f9 -assert f1 == "Hello mypyc, this is a test" -assert f2 == "Hello 'mypyc'" -assert f3 == "Hello 'mypyc'" -assert f4 == "Hello mypyc" -assert f5 == "Hello mypyc" -assert f6 == "Hello 'mypyc'" -assert f7 == "Hello mypyc" -assert f8 == "Hello 'mypyc'" -assert f9 == "Hello mypyc, hello again mypyc" - -[out] - [case testSets] from typing import Set, List def instantiateLiteral() -> Set[int]: @@ -4838,66 +4768,6 @@ import b assert f(20) == 61 assert isinstance(whatever, b.A) -[case testStrSplit] -from typing import List, Optional -def f(s: str, sep: Optional[str] = None, max_split: Optional[int] = None) -> List[str]: - if sep is not None: - if max_split is not None: - return s.split(sep, max_split) - else: - return s.split(sep) - return s.split() - -[file driver.py] -from native import f -s = "abc abcd abcde abcdef" - -assert f(s) == ["abc", "abcd", "abcde", "abcdef"] -assert f(s, " ") == ["abc", "abcd", "abcde", "abcdef"] -assert f(s, "-") == ["abc abcd abcde abcdef"] -assert f(s, " ", -1) == ["abc", "abcd", "abcde", "abcdef"] -assert f(s, " ", 0) == ["abc abcd abcde abcdef"] -assert f(s, " ", 1) == ["abc", "abcd abcde abcdef"] -assert f(s, " ", 2) == ["abc", "abcd", "abcde abcdef"] - -[case testStrGetItem] -def f(s: str, index: int) -> str: - return s[index] - -[file driver.py] -from testutil import assertRaises -from native import f -s = "abc" - -assert f(s, 0) == "a" -assert f(s, 1) == "b" -assert f(s, 2) == "c" -assert f(s, -3) == "a" -assert f(s, -2) == "b" -assert f(s, -1) == "c" -with assertRaises(IndexError, "string index out of range"): - f(s, 4) -with assertRaises(IndexError, "string index out of range"): - f(s, -4) - -[case testIntCastOnStr] -from typing import Optional -def f(s: str, base: Optional[int] = None) -> int: - if base: - return int(s, base) - else: - return int(s) - -[file driver.py] -from testutil import assertRaises -from native import f -assert f("1") == 1 -assert f("10") == 10 -assert f("a", 16) == 10 -assert f("1a", 16) == 26 -with assertRaises(ValueError, "invalid literal for int() with base 10: 'xyz'"): - f("xyz") - [case testNamedTupleAttributeRun] from typing import NamedTuple diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index f57ea5b94166..1f0510b474df 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -32,6 +32,7 @@ files = [ 'run-functions.test', 'run.test', + 'run-strings.test', 'run-classes.test', 'run-traits.test', 'run-multimodule.test', @@ -221,7 +222,7 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> assert False, "Compile error" except CompileError as e: for line in e.messages: - print(line) + print(fix_native_line_number(line, testcase.file, testcase.line)) assert False, 'Compile error' # Check that serialization works on this IR. (Only on the first @@ -244,6 +245,13 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> assert glob.glob('native.*.{}'.format(suffix)) driver_path = 'driver.py' + if not os.path.isfile(driver_path): + # No driver.py provided by test case. Use the default one + # (mypyc/test-data/driver/driver.py) that calls each + # function named test_*. + default_driver = os.path.join( + os.path.dirname(__file__), '..', 'test-data', 'driver', 'driver.py') + shutil.copy(default_driver, driver_path) env = os.environ.copy() env['MYPYC_RUN_BENCH'] = '1' if bench else '0' @@ -283,6 +291,11 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> msg = 'Invalid output (step {})'.format(incremental_step) expected = testcase.output2.get(incremental_step, []) + if not expected: + # Tweak some line numbers, but only if the expected output is empty, + # as tweaked output might not match expected output. + outlines = [fix_native_line_number(line, testcase.file, testcase.line) + for line in outlines] assert_test_output(testcase, outlines, msg, expected) if incremental_step > 1 and options.incremental: @@ -312,8 +325,9 @@ def get_separate(self, program_text: str, return True -# Run the main multi-module tests in multi-file compilation mode class TestRunMultiFile(TestRun): + """Run the main multi-module tests in multi-file compilation mode.""" + multi_file = True test_name_suffix = '_multi' files = [ @@ -322,11 +336,37 @@ class TestRunMultiFile(TestRun): ] -# Run the main multi-module tests in separate compilation mode class TestRunSeparate(TestRun): + """Run the main multi-module tests in separate compilation mode.""" + separate = True test_name_suffix = '_separate' files = [ 'run-multimodule.test', 'run-mypy-sim.test', ] + + +def fix_native_line_number(message: str, fnam: str, delta: int) -> str: + """Update code locations in test case output to point to the .test file. + + The description of the test case is written to native.py, and line numbers + in test case output often are relative to native.py. This translates the + line numbers to be relative to the .test file that contains the test case + description, and also updates the file name to the .test file name. + + Args: + message: message to update + fnam: path of the .test file + delta: line number of the beginning of the test case in the .test file + + Returns updated message (or original message if we couldn't find anything). + """ + fnam = os.path.basename(fnam) + message = re.sub(r'native\.py:([0-9]+):', + lambda m: '%s:%d:' % (fnam, int(m.group(1)) + delta), + message) + message = re.sub(r'"native.py", line ([0-9]+),', + lambda m: '"%s", line %d,' % (fnam, int(m.group(1)) + delta), + message) + return message diff --git a/mypyc/test/testutil.py b/mypyc/test/testutil.py index 8bedbf32509e..141472bb30a6 100644 --- a/mypyc/test/testutil.py +++ b/mypyc/test/testutil.py @@ -148,9 +148,11 @@ def update_testcase_output(testcase: DataDrivenTestCase, output: List[str]) -> N print(data, file=f) -def assert_test_output(testcase: DataDrivenTestCase, actual: List[str], +def assert_test_output(testcase: DataDrivenTestCase, + actual: List[str], message: str, - expected: Optional[List[str]] = None) -> None: + expected: Optional[List[str]] = None, + formatted: Optional[List[str]] = None) -> None: expected_output = expected if expected is not None else testcase.output if expected_output != actual and testcase.config.getoption('--update-data', False): update_testcase_output(testcase, actual) From ac8ae2f386968b393b7b75699b2af2a7e3df3363 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 11 Jul 2020 12:18:33 +0100 Subject: [PATCH 060/138] [mypyc] Migrate some tuple test cases to the new representation (#9129) Also move tuple related tests to a new test file. Work towards mypyc/mypyc#72. --- mypyc/test-data/run-tuples.test | 158 ++++++++++++++++++++++++++ mypyc/test-data/run.test | 190 -------------------------------- mypyc/test/test_run.py | 1 + 3 files changed, 159 insertions(+), 190 deletions(-) create mode 100644 mypyc/test-data/run-tuples.test diff --git a/mypyc/test-data/run-tuples.test b/mypyc/test-data/run-tuples.test new file mode 100644 index 000000000000..adfe3063b9da --- /dev/null +++ b/mypyc/test-data/run-tuples.test @@ -0,0 +1,158 @@ +# Test cases for tuples (compile and run) + +[case testTuple] +from typing import List, Optional, Tuple +from typing import Tuple +def f(x: Tuple[int, int]) -> Tuple[int,int]: + return x + +def lurr(x: List[Optional[Tuple[int, str]]]) -> object: + return x[0] + +def asdf(x: Tuple[int, str]) -> None: + pass +[file driver.py] +from testutil import assertRaises +from native import f, lurr, asdf + +assert f((1,2)) == (1, 2) +assert lurr([(1, '2')]) == (1, '2') + +with assertRaises(TypeError): + print(lurr([(1, 2)])) + +with assertRaises(TypeError): + asdf((1, 2)) + +[case testTupleGet] +from typing import Tuple +def f(x: Tuple[Tuple[int, bool], int]) -> int: + return x[0][0] +[file driver.py] +from native import f +print(f(((1,True),2))) +big_number = pow(2, 80) +print(f(((big_number,True),2))) +[out] +1 +1208925819614629174706176 + +[case testSequenceTupleArg] +from typing import Tuple +def f(x: Tuple[int, ...]) -> int: + return x[1] +[file driver.py] +from native import f +print(f((1,2,3,4))) +[out] +2 + +[case testTupleAttr] +from typing import Tuple +class C: + b: Tuple[Tuple[Tuple[int, int], int], int, str, object] + c: Tuple[()] +def f() -> None: + c = C() + c.b = (((1, 2), 2), 1, 'hi', 'hi2') + print(c.b) + +def g() -> None: + try: + h() + except Exception: + print('caught the exception') + +def h() -> Tuple[Tuple[Tuple[int, int], int], int, str, object]: + raise Exception('Intentional exception') +[file driver.py] +from native import f, g, C +f() +g() +assert not hasattr(C(), 'c') +[out] +(((1, 2), 2), 1, 'hi', 'hi2') +caught the exception + +[case testNamedTupleAttributeRun] +from typing import NamedTuple + +NT = NamedTuple('NT', [('x', int), ('y', int)]) + +def f(nt: NT) -> int: + if nt.x > nt.y: + return nt.x + return nt.y + +nt = NT(1, 2) +[file driver.py] +from native import NT, nt, f + +assert f(nt) == 2 +assert f(NT(3, 2)) == 3 + +class Sub(NT): + pass +assert f(Sub(3, 2)) == 3 + +[case testTupleOps] +from typing import Tuple, List, Any, Optional + +def f() -> Tuple[()]: + return () + +def test_empty_tuple() -> None: + assert f() == () + +def f2() -> Any: + return () + +def test_empty_tuple_with_any_type(): + assert f2() == () + +def f3() -> int: + x = (False, 1) + return x[1] + +def test_new_tuple() -> None: + assert f3() == 1 + +def f4(y: int) -> int: + x = (False, y) + return x[1] + +def test_new_tuple_boxed_int() -> None: + big_number = 1208925819614629174706176 + assert f4(big_number) == big_number + +def f5(x: List[int]) -> int: + return tuple(x)[1] + +def test_sequence_tuple() -> None: + assert f5([1,2,3,4]) == 2 + +def f6(x: List[int]) -> int: + return len(tuple(x)) + +def test_sequence_tuple_len() -> None: + assert f6([1,2,3,4]) == 4 + +def f7(x: List[Tuple[int, int]]) -> int: + a, b = x[0] + return a + b + +def test_unbox_tuple() -> None: + assert f7([(5, 6)]) == 11 + +# Test that order is irrelevant to unions. Really I only care that this builds. + +class A: + pass + +def lol() -> A: + return A() + +def foo(x: bool, y: bool) -> Tuple[Optional[A], bool]: + z = lol() + + return None if y else z, x diff --git a/mypyc/test-data/run.test b/mypyc/test-data/run.test index ea0ab0405348..8db18e19ef7d 100644 --- a/mypyc/test-data/run.test +++ b/mypyc/test-data/run.test @@ -492,119 +492,6 @@ print(f(False)) False True -[case testTuple] -from typing import List, Optional, Tuple -from typing import Tuple -def f(x: Tuple[int, int]) -> Tuple[int,int]: - return x - -def lurr(x: List[Optional[Tuple[int, str]]]) -> object: - return x[0] - -def asdf(x: Tuple[int, str]) -> None: - pass - -[file driver.py] -from testutil import assertRaises -from native import f, lurr, asdf - -assert f((1,2)) == (1, 2) -assert lurr([(1, '2')]) == (1, '2') - -with assertRaises(TypeError): - print(lurr([(1, 2)])) - -with assertRaises(TypeError): - asdf((1, 2)) - -[case testEmptyTupleFunctionWithTupleType] -from typing import Tuple -def f() -> Tuple[()]: - return () -[file driver.py] -from native import f -assert f() == () - -[case testEmptyTupleFunctionWithAnyType] -from typing import Any -def f() -> Any: - return () -[file driver.py] -from native import f -assert f() == () - -[case testTupleGet] -from typing import Tuple -def f(x: Tuple[Tuple[int, bool], int]) -> int: - return x[0][0] -[file driver.py] -from native import f -print(f(((1,True),2))) -[out] -1 - -[case testTupleGetBoxedInt] -from typing import Tuple -def f(x: Tuple[Tuple[int, bool], int]) -> int: - return x[0][0] -[file driver.py] -from native import f -big_number = pow(2, 80) -print(f(((big_number,True),2))) -[out] -1208925819614629174706176 - -[case testNewTuple] -def f() -> int: - x = (False, 1) - return x[1] -[file driver.py] -from native import f -print(f()) -[out] -1 - -[case testNewTupleBoxedInt] -def f(y: int) -> int: - x = (False, y) - return x[1] -[file driver.py] -from native import f -big_number = pow(2, 80) -print(f(big_number)) -[out] -1208925819614629174706176 - -[case testSequenceTuple] -from typing import List -def f(x: List[int]) -> int: - return tuple(x)[1] -[file driver.py] -from native import f -print(f([1,2,3,4])) -[out] -2 - -[case testSequenceTupleLen] -from typing import List -def f(x: List[int]) -> int: - return len(tuple(x)) -[file driver.py] -from native import f -print(f([1,2,3,4])) -[out] -4 - -[case testSequenceTupleArg] -from typing import Tuple -def f(x: Tuple[int, ...]) -> int: - return x[1] -[file driver.py] -from native import f -print(f((1,2,3,4))) -[out] -2 - [case testMaybeUninitVar] class C: def __init__(self, x: int) -> None: @@ -2670,16 +2557,6 @@ assert from_tuple((1, 'x')) == ['x', 1] assert from_list([3, 4]) == [4, 3] assert from_any('xy') == ['y', 'x'] -[case testUnboxTuple] -from typing import List, Tuple - -def f(x: List[Tuple[int, int]]) -> int: - a, b = x[0] - return a + b -[file driver.py] -from native import f -assert f([(5, 6)]) == 11 - [case testUnpack] from typing import List @@ -3492,33 +3369,6 @@ TypeError TypeError 10 -[case testTupleAttr] -from typing import Tuple -class C: - b: Tuple[Tuple[Tuple[int, int], int], int, str, object] - c: Tuple[()] -def f() -> None: - c = C() - c.b = (((1, 2), 2), 1, 'hi', 'hi2') - print(c.b) - -def g() -> None: - try: - h() - except Exception: - print('caught the exception') - -def h() -> Tuple[Tuple[Tuple[int, int], int], int, str, object]: - raise Exception('Intentional exception') -[file driver.py] -from native import f, g, C -f() -g() -assert not hasattr(C(), 'c') -[out] -(((1, 2), 2), 1, 'hi', 'hi2') -caught the exception - [case testUnion] from typing import Union @@ -4486,25 +4336,6 @@ class E: [file driver.py] # really I only care it builds -[case testTupleUnionFail] -# Test that order is irrelevant to unions - -from typing import Optional, Tuple - -class A: - pass - -def lol() -> A: - return A() - -def foo(x: bool, y: bool) -> Tuple[Optional[A], bool]: - z = lol() - - return None if y else z, x - -[file driver.py] -# really I only care it builds - [case testIterTypeTrickiness] # Test inferring the type of a for loop body doesn't cause us grief # Extracted from somethings that broke in mypy @@ -4767,24 +4598,3 @@ import b assert f(20) == 61 assert isinstance(whatever, b.A) - -[case testNamedTupleAttributeRun] -from typing import NamedTuple - -NT = NamedTuple('NT', [('x', int), ('y', int)]) - -def f(nt: NT) -> int: - if nt.x > nt.y: - return nt.x - return nt.y - -nt = NT(1, 2) -[file driver.py] -from native import NT, nt, f - -assert f(nt) == 2 -assert f(NT(3, 2)) == 3 - -class Sub(NT): - pass -assert f(Sub(3, 2)) == 3 diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index 1f0510b474df..bbe4d31a8dbc 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -33,6 +33,7 @@ 'run-functions.test', 'run.test', 'run-strings.test', + 'run-tuples.test', 'run-classes.test', 'run-traits.test', 'run-multimodule.test', From 19f4fb96ad86dd62c28cb4e582af58d36eec085b Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 11 Jul 2020 12:25:08 +0100 Subject: [PATCH 061/138] [mypyc] Group related tests together in separate test files (#9130) This primarily splits the big run.test file into several smaller files and renames that file to run-misc.test. In the future, I hope that most new test cases will be added outside the misc file. I haven't changed the test cases, and I've verified that the total number of test cases remains the same. --- mypyc/test-data/run-classes.test | 348 +- mypyc/test-data/run-dicts.test | 194 ++ mypyc/test-data/run-exceptions.test | 448 +++ mypyc/test-data/run-functions.test | 845 +++++ mypyc/test-data/run-generators.test | 518 +++ mypyc/test-data/run-imports.test | 89 + mypyc/test-data/run-integers.test | 132 + mypyc/test-data/run-lists.test | 128 + mypyc/test-data/run-loops.test | 454 +++ mypyc/test-data/run-misc.test | 997 ++++++ mypyc/test-data/run-primitives.test | 399 +++ mypyc/test-data/run-sets.test | 107 + mypyc/test-data/run.test | 4600 --------------------------- mypyc/test/test_run.py | 11 +- 14 files changed, 4646 insertions(+), 4624 deletions(-) create mode 100644 mypyc/test-data/run-dicts.test create mode 100644 mypyc/test-data/run-exceptions.test create mode 100644 mypyc/test-data/run-generators.test create mode 100644 mypyc/test-data/run-imports.test create mode 100644 mypyc/test-data/run-integers.test create mode 100644 mypyc/test-data/run-lists.test create mode 100644 mypyc/test-data/run-loops.test create mode 100644 mypyc/test-data/run-misc.test create mode 100644 mypyc/test-data/run-primitives.test create mode 100644 mypyc/test-data/run-sets.test delete mode 100644 mypyc/test-data/run.test diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index db910bacf055..66b527bb507a 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -390,29 +390,6 @@ assert c.a == 13 assert type(c) == C assert not hasattr(c, 'b') -[case testListOfUserDefinedClass] -class C: - x: int - -def f() -> int: - c = C() - c.x = 5 - a = [c] - d = a[0] - return d.x + 1 - -def g() -> int: - a = [C()] - a[0].x = 3 - return a[0].x + 4 -[file driver.py] -from native import f, g -print(f()) -print(g()) -[out] -6 -7 - [case testCastUserClass] from typing import List @@ -1466,3 +1443,328 @@ with patch("interp.Bar.spam", lambda self: 20): with assertRaises(TypeError, "int object expected; got str"): y.x = "test" + +[case testProperty] +from typing import Callable +from mypy_extensions import trait +class Temperature: + @property + def celsius(self) -> float: + return 5.0 * (self.farenheit - 32.0) / 9.0 + + def __init__(self, farenheit: float) -> None: + self.farenheit = farenheit + + def print_temp(self) -> None: + print("F:", self.farenheit, "C:", self.celsius) + + @property + def rankine(self) -> float: + raise NotImplementedError + +class Access: + @property + def number_of_accesses(self) -> int: + self._count += 1 + return self._count + def __init__(self) -> None: + self._count = 0 + +from typing import Callable +class BaseProperty: + @property + def doc(self) -> str: + return "Represents a sequence of values. Updates itself by next, which is a new value." + + @property + def value(self) -> object: + return self._incrementer + + @property + def bad_value(self) -> object: + return self._incrementer + + @property + def next(self) -> BaseProperty: + return BaseProperty(self._incrementer + 1) + + def __init__(self, value: int) -> None: + self._incrementer = value + +class DerivedProperty(BaseProperty): + @property + def value(self) -> int: + return self._incrementer + + @property + def bad_value(self) -> object: + return self._incrementer + + def __init__(self, incr_func: Callable[[int], int], value: int) -> None: + BaseProperty.__init__(self, value) + self._incr_func = incr_func + + @property + def next(self) -> DerivedProperty: + return DerivedProperty(self._incr_func, self._incr_func(self.value)) + +class AgainProperty(DerivedProperty): + @property + def next(self) -> AgainProperty: + return AgainProperty(self._incr_func, self._incr_func(self._incr_func(self.value))) + + @property + def bad_value(self) -> int: + return self._incrementer + +def print_first_n(n: int, thing: BaseProperty) -> None: + vals = [] + cur_thing = thing + for _ in range(n): + vals.append(cur_thing.value) + cur_thing = cur_thing.next + print ('', vals) + +@trait +class Trait: + @property + def value(self) -> int: + return 3 + +class Printer(Trait): + def print_value(self) -> None: + print(self.value) + +[file driver.py] +from native import Temperature, Access +import traceback +x = Temperature(32.0) +try: + print (x.rankine) +except NotImplementedError as e: + traceback.print_exc() +print (x.celsius) +x.print_temp() + +y = Temperature(212.0) +print (y.celsius) +y.print_temp() + +z = Access() +print (z.number_of_accesses) +print (z.number_of_accesses) +print (z.number_of_accesses) +print (z.number_of_accesses) + +from native import BaseProperty, DerivedProperty, AgainProperty, print_first_n +a = BaseProperty(7) +b = DerivedProperty((lambda x: x // 2 if (x % 2 == 0) else 3 * x + 1), 7) +c = AgainProperty((lambda x: x // 2 if (x % 2 == 0) else 3 * x + 1), 7) + +def py_print_first_n(n: int, thing: BaseProperty) -> None: + vals = [] + cur_thing = thing + for _ in range(n): + vals.append(cur_thing.value) + cur_thing = cur_thing.next + print ('', vals) + +py_print_first_n(20, a) +py_print_first_n(20, b) +py_print_first_n(20, c) + +print(a.next.next.next.bad_value) +print(b.next.next.next.bad_value) +print(c.next.next.next.bad_value) + +print_first_n(20, a) +print_first_n(20, b) +print_first_n(20, c) + +print (a.doc) +print (b.doc) +print (c.doc) + +from native import Printer +Printer().print_value() +print (Printer().value) +[out] +Traceback (most recent call last): + File "driver.py", line 5, in + print (x.rankine) + File "native.py", line 16, in rankine + raise NotImplementedError +NotImplementedError +0.0 +F: 32.0 C: 0.0 +100.0 +F: 212.0 C: 100.0 +1 +2 +3 +4 + [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26] + [7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1, 4, 2, 1] + [7, 11, 17, 26, 40, 10, 16, 4, 1, 2, 4, 1, 2, 4, 1, 2, 4, 1, 2, 4] +10 +34 +26 + [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26] + [7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1, 4, 2, 1] + [7, 11, 17, 26, 40, 10, 16, 4, 1, 2, 4, 1, 2, 4, 1, 2, 4, 1, 2, 4] +Represents a sequence of values. Updates itself by next, which is a new value. +Represents a sequence of values. Updates itself by next, which is a new value. +Represents a sequence of values. Updates itself by next, which is a new value. +3 +3 + +[case testPropertySetters] + +from mypy_extensions import trait + +class Foo(): + def __init__(self) -> None: + self.attr = "unmodified" + +class A: + def __init__(self) -> None: + self._x = 0 + self._foo = Foo() + + @property + def x(self) -> int: + return self._x + + @x.setter + def x(self, val : int) -> None: + self._x = val + + @property + def foo(self) -> Foo: + return self._foo + + @foo.setter + def foo(self, val : Foo) -> None: + self._foo = val + +# Overrides base property setters and getters +class B(A): + def __init__(self) -> None: + self._x = 10 + + @property + def x(self) -> int: + return self._x + 1 + + @x.setter + def x(self, val : int) -> None: + self._x = val + 1 + +#Inerits base property setters and getters +class C(A): + def __init__(self) -> None: + A.__init__(self) + +@trait +class D(): + def __init__(self) -> None: + self._x = 0 + + @property + def x(self) -> int: + return self._x + + @x.setter + def x(self, val : int) -> None: + self._x = val + +#Inherits trait property setters and getters +class E(D): + def __init__(self) -> None: + D.__init__(self) + +#Overrides trait property setters and getters +class F(D): + def __init__(self) -> None: + self._x = 10 + + @property + def x(self) -> int: + return self._x + 10 + + @x.setter + def x(self, val : int) -> None: + self._x = val + 10 + +# # Property setter and getter are subtypes of base property setters and getters +# # class G(A): +# # def __init__(self) -> None: +# # A.__init__(self) + +# # @property +# # def y(self) -> int: +# # return self._y + +# # @y.setter +# # def y(self, val : object) -> None: +# # self._y = val + +[file other.py] +# Run in both interpreted and compiled mode + +from native import A, B, C, D, E, F + +a = A() +assert a.x == 0 +assert a._x == 0 +a.x = 1 +assert a.x == 1 +assert a._x == 1 +a._x = 0 +assert a.x == 0 +assert a._x == 0 +b = B() +assert b.x == 11 +assert b._x == 10 +b.x = 11 +assert b.x == 13 +b._x = 11 +assert b.x == 12 +c = C() +assert c.x == 0 +c.x = 1000 +assert c.x == 1000 +e = E() +assert e.x == 0 +e.x = 1000 +assert e.x == 1000 +f = F() +assert f.x == 20 +f.x = 30 +assert f.x == 50 + +[file driver.py] +# Run the tests in both interpreted and compiled mode +import other +import other_interpreted + +[out] + +[case testSubclassAttributeAccess] +from mypy_extensions import trait + +class A: + v = 0 + +class B(A): + v = 1 + +class C(B): + v = 2 + +[file driver.py] +from native import A, B, C + +a = A() +b = B() +c = C() diff --git a/mypyc/test-data/run-dicts.test b/mypyc/test-data/run-dicts.test new file mode 100644 index 000000000000..cac68b9af060 --- /dev/null +++ b/mypyc/test-data/run-dicts.test @@ -0,0 +1,194 @@ +# Dict test cases (compile and run) + +[case testDictStuff] +from typing import Dict, Any, List, Set, Tuple +from defaultdictwrap import make_dict + +def f(x: int) -> int: + dict1 = {} # type: Dict[int, int] + dict1[1] = 1 + dict2 = {} # type: Dict[int, int] + dict2[x] = 2 + dict1.update(dict2) + + l = [(5, 2)] # type: Any + dict1.update(l) + d2 = {6: 4} # type: Any + dict1.update(d2) + + return dict1[1] + +def g() -> int: + d = make_dict() + d['a'] = 10 + d['a'] += 10 + d['b'] += 10 + l = [('c', 2)] # type: Any + d.update(l) + d2 = {'d': 4} # type: Any + d.update(d2) + return d['a'] + d['b'] + +def h() -> None: + d = {} # type: Dict[Any, Any] + d[{}] + +def update_dict(x: Dict[Any, Any], y: Any): + x.update(y) + +def make_dict1(x: Any) -> Dict[Any, Any]: + return dict(x) + +def make_dict2(x: Dict[Any, Any]) -> Dict[Any, Any]: + return dict(x) + +def u(x: int) -> int: + d = {} # type: Dict[str, int] + d.update(x=x) + return d['x'] + +def get_content(d: Dict[int, int]) -> Tuple[List[int], List[int], List[Tuple[int, int]]]: + return list(d.keys()), list(d.values()), list(d.items()) + +def get_content_set(d: Dict[int, int]) -> Tuple[Set[int], Set[int], Set[Tuple[int, int]]]: + return set(d.keys()), set(d.values()), set(d.items()) +[file defaultdictwrap.py] +from typing import Dict +from collections import defaultdict # type: ignore +def make_dict() -> Dict[str, int]: + return defaultdict(int) + +[file driver.py] +from collections import OrderedDict +from native import ( + f, g, h, u, make_dict1, make_dict2, update_dict, get_content, get_content_set +) +assert f(1) == 2 +assert f(2) == 1 +assert g() == 30 +# Make sure we get a TypeError from indexing with unhashable and not KeyError +try: + h() +except TypeError: + pass +else: + assert False +d = {'a': 1, 'b': 2} +assert make_dict1(d) == d +assert make_dict1(d.items()) == d +assert make_dict2(d) == d +# object.__dict__ is a "mappingproxy" and not a dict +assert make_dict1(object.__dict__) == dict(object.__dict__) +d = {} +update_dict(d, object.__dict__) +assert d == dict(object.__dict__) + +assert u(10) == 10 +assert get_content({1: 2}) == ([1], [2], [(1, 2)]) +od = OrderedDict([(1, 2), (3, 4)]) +assert get_content(od) == ([1, 3], [2, 4], [(1, 2), (3, 4)]) +od.move_to_end(1) +assert get_content(od) == ([3, 1], [4, 2], [(3, 4), (1, 2)]) +assert get_content_set({1: 2}) == ({1}, {2}, {(1, 2)}) +assert get_content_set(od) == ({1, 3}, {2, 4}, {(1, 2), (3, 4)}) +[typing fixtures/typing-full.pyi] + +[case testDictIterationMethodsRun] +from typing import Dict +def print_dict_methods(d1: Dict[int, int], + d2: Dict[int, int], + d3: Dict[int, int]) -> None: + for k in d1.keys(): + print(k) + for k, v in d2.items(): + print(k) + print(v) + for v in d3.values(): + print(v) + +def clear_during_iter(d: Dict[int, int]) -> None: + for k in d: + d.clear() + +class Custom(Dict[int, int]): pass +[file driver.py] +from native import print_dict_methods, Custom, clear_during_iter +from collections import OrderedDict +print_dict_methods({}, {}, {}) +print_dict_methods({1: 2}, {3: 4, 5: 6}, {7: 8}) +print('==') +c = Custom({0: 1}) +print_dict_methods(c, c, c) +print('==') +d = OrderedDict([(1, 2), (3, 4)]) +print_dict_methods(d, d, d) +print('==') +d.move_to_end(1) +print_dict_methods(d, d, d) +clear_during_iter({}) # OK +try: + clear_during_iter({1: 2, 3: 4}) +except RuntimeError as e: + assert str(e) == "dictionary changed size during iteration" +else: + assert False +try: + clear_during_iter(d) +except RuntimeError as e: + assert str(e) == "OrderedDict changed size during iteration" +else: + assert False + +class CustomMad(dict): + def __iter__(self): + return self + def __next__(self): + raise ValueError +m = CustomMad() +try: + clear_during_iter(m) +except ValueError: + pass +else: + assert False + +class CustomBad(dict): + def items(self): + return [(1, 2, 3)] # Oops +b = CustomBad() +try: + print_dict_methods(b, b, b) +except TypeError as e: + assert str(e) == "a tuple of length 2 expected" +else: + assert False +[out] +1 +3 +4 +5 +6 +8 +== +0 +0 +1 +1 +== +1 +3 +1 +2 +3 +4 +2 +4 +== +3 +1 +3 +4 +1 +2 +4 +2 diff --git a/mypyc/test-data/run-exceptions.test b/mypyc/test-data/run-exceptions.test new file mode 100644 index 000000000000..c591fc1d8c15 --- /dev/null +++ b/mypyc/test-data/run-exceptions.test @@ -0,0 +1,448 @@ +# Test cases for exceptions (compile and run) + +[case testException] +from typing import List +def f(x: List[int]) -> None: + g(x) + +def g(x: List[int]) -> bool: + x[5] = 2 + return True + +def r1() -> None: + q1() + +def q1() -> None: + raise Exception("test") + +def r2() -> None: + q2() + +def q2() -> None: + raise Exception + +class A: + def __init__(self) -> None: + raise Exception + +def hey() -> None: + A() + +[file driver.py] +from native import f, r1, r2, hey +import traceback +try: + f([]) +except IndexError: + traceback.print_exc() +try: + r1() +except Exception: + traceback.print_exc() +try: + r2() +except Exception: + traceback.print_exc() +try: + hey() +except Exception: + traceback.print_exc() +[out] +Traceback (most recent call last): + File "driver.py", line 4, in + f([]) + File "native.py", line 3, in f + g(x) + File "native.py", line 6, in g + x[5] = 2 +IndexError: list assignment index out of range +Traceback (most recent call last): + File "driver.py", line 8, in + r1() + File "native.py", line 10, in r1 + q1() + File "native.py", line 13, in q1 + raise Exception("test") +Exception: test +Traceback (most recent call last): + File "driver.py", line 12, in + r2() + File "native.py", line 16, in r2 + q2() + File "native.py", line 19, in q2 + raise Exception +Exception +Traceback (most recent call last): + File "driver.py", line 16, in + hey() + File "native.py", line 26, in hey + A() + File "native.py", line 23, in __init__ + raise Exception +Exception + +[case testTryExcept] +from typing import Any, Iterator +import wrapsys +def g(b: bool) -> None: + try: + if b: + x = [0] + x[1] + else: + raise Exception('hi') + except: + print("caught!") + +def r(x: int) -> None: + if x == 0: + [0][1] + elif x == 1: + raise Exception('hi') + elif x == 2: + {1: 1}[0] + elif x == 3: + a = object() # type: Any + a.lol + +def f(b: bool) -> None: + try: + r(int(b)) + except AttributeError: + print('no') + except: + print(str(wrapsys.exc_info()[1])) + print(str(wrapsys.exc_info()[1])) + +def h() -> None: + while True: + try: + raise Exception('gonna break') + except: + print(str(wrapsys.exc_info()[1])) + break + print(str(wrapsys.exc_info()[1])) + +def i() -> None: + try: + r(0) + except: + print(type(wrapsys.exc_info()[1])) + raise + +def j(n: int) -> None: + try: + r(n) + except (IndexError, KeyError): + print("lookup!") + except AttributeError as e: + print("attr! --", e) + +def k() -> None: + try: + r(1) + except: + r(0) + +def l() -> None: + try: + r(0) + except IndexError: + try: + r(2) + except KeyError as e: + print("key! --", e) + +def m(x: object) -> int: + try: + st = id(x) + except Exception: + return -1 + return st + 1 + +def iter_exception() -> Iterator[str]: + try: + r(0) + except KeyError as e: + yield 'lol' + +[file wrapsys.py] +# This is a gross hack around some limitations of the test system/mypyc. +from typing import Any +import sys +def exc_info() -> Any: + return sys.exc_info() # type: ignore + +[file driver.py] +import sys, traceback +from native import g, f, h, i, j, k, l, m, iter_exception +from testutil import assertRaises +print("== i ==") +try: + i() +except: + traceback.print_exc(file=sys.stdout) + +print("== k ==") +try: + k() +except: + traceback.print_exc(file=sys.stdout) + +print("== g ==") +g(True) +g(False) + +print("== f ==") +f(True) +f(False) + +print("== h ==") +h() + +print("== j ==") +j(0) +j(2) +j(3) +try: + j(1) +except: + print("out!") + +print("== l ==") +l() + +m('lol') + +with assertRaises(IndexError): + list(iter_exception()) + +[out] +== i == + +Traceback (most recent call last): + File "driver.py", line 6, in + i() + File "native.py", line 44, in i + r(0) + File "native.py", line 15, in r + [0][1] +IndexError: list index out of range +== k == +Traceback (most recent call last): + File "native.py", line 59, in k + r(1) + File "native.py", line 17, in r + raise Exception('hi') +Exception: hi + +During handling of the above exception, another exception occurred: + +Traceback (most recent call last): + File "driver.py", line 12, in + k() + File "native.py", line 61, in k + r(0) + File "native.py", line 15, in r + [0][1] +IndexError: list index out of range +== g == +caught! +caught! +== f == +hi +None +list index out of range +None +== h == +gonna break +None +== j == +lookup! +lookup! +attr! -- 'object' object has no attribute 'lol' +out! +== l == +key! -- 0 + +[case testTryFinally] +from typing import Any +import wrapsys + +def a(b1: bool, b2: int) -> None: + try: + if b1: + raise Exception('hi') + finally: + print('finally:', str(wrapsys.exc_info()[1])) + if b2 == 2: + return + if b2 == 1: + raise Exception('again!') + +def b(b1: int, b2: int) -> str: + try: + if b1 == 1: + raise Exception('hi') + elif b1 == 2: + [0][1] + elif b1 == 3: + return 'try' + except IndexError: + print('except') + finally: + print('finally:', str(wrapsys.exc_info()[1])) + if b2 == 2: + return 'finally' + if b2 == 1: + raise Exception('again!') + return 'outer' + +def c() -> str: + try: + try: + return 'wee' + finally: + print("out a") + finally: + print("out b") + + +[file wrapsys.py] +# This is a gross hack around some limitations of the test system/mypyc. +from typing import Any +import sys +def exc_info() -> Any: + return sys.exc_info() # type: ignore + +[file driver.py] +import traceback +import sys +from native import a, b, c + +def run(f): + try: + x = f() + if x: + print("returned:", x) + except Exception as e: + print("caught:", type(e).__name__ + ": " + str(e)) + +print("== a ==") +for i in range(3): + for b1 in [False, True]: + run(lambda: a(b1, i)) + +print("== b ==") +for i in range(4): + for j in range(3): + run(lambda: b(i, j)) + +print("== b ==") +print(c()) + +[out] +== a == +finally: None +finally: hi +caught: Exception: hi +finally: None +caught: Exception: again! +finally: hi +caught: Exception: again! +finally: None +finally: hi +== b == +finally: None +returned: outer +finally: None +caught: Exception: again! +finally: None +returned: finally +finally: hi +caught: Exception: hi +finally: hi +caught: Exception: again! +finally: hi +returned: finally +except +finally: None +returned: outer +except +finally: None +caught: Exception: again! +except +finally: None +returned: finally +finally: None +returned: try +finally: None +caught: Exception: again! +finally: None +returned: finally +== b == +out a +out b +wee + +[case testCustomException] +from typing import List + +class ListOutOfBounds(IndexError): + pass + +class UserListWarning(UserWarning): + pass + +def f(l: List[int], k: int) -> int: + try: + return l[k] + except IndexError: + raise ListOutOfBounds("Ruh-roh from f!") + +def g(l: List[int], k: int) -> int: + try: + return f([1,2,3], 3) + except ListOutOfBounds: + raise ListOutOfBounds("Ruh-roh from g!") + +def k(l: List[int], k: int) -> int: + try: + return g([1,2,3], 3) + except IndexError: + raise UserListWarning("Ruh-roh from k!") + +def h() -> int: + try: + return k([1,2,3], 3) + except UserWarning: + return -1 + +[file driver.py] +from native import h +assert h() == -1 + +[case testExceptionAtModuleTopLevel] +from typing import Any + +def f(x: int) -> None: pass + +y: Any = '' +f(y) + +[file driver.py] +import traceback +try: + import native +except TypeError: + traceback.print_exc() +else: + assert False + +[out] +Traceback (most recent call last): + File "driver.py", line 3, in + import native + File "native.py", line 6, in + f(y) +TypeError: int object expected; got str diff --git a/mypyc/test-data/run-functions.test b/mypyc/test-data/run-functions.test index dfc9ff7c875c..5dd5face6b0e 100644 --- a/mypyc/test-data/run-functions.test +++ b/mypyc/test-data/run-functions.test @@ -1,3 +1,39 @@ +# Test cases for functions and calls (compile and run) + +[case testCallTrivialFunction] +def f(x: int) -> int: + return x +[file driver.py] +from native import f +print(f(3)) +print(f(-157)) +print(f(10**20)) +print(f(-10**20)) +[out] +3 +-157 +100000000000000000000 +-100000000000000000000 + +[case testRecursiveFibonacci] +def fib(n: int) -> int: + if n <= 1: + return 1 + else: + return fib(n - 1) + fib(n - 2) + return 0 # TODO: This should be unnecessary +[file driver.py] +from native import fib +print(fib(0)) +print(fib(1)) +print(fib(2)) +print(fib(6)) +[out] +1 +1 +2 +13 + [case testNestedFunctions] from typing import Callable, List @@ -243,3 +279,812 @@ assert inner() == 'inner: normal function' assert A(3).outer(4) == 12 assert toplevel_lambda(5) == 35 + +[case testNestedFunctions2] +from typing import Callable + +def outer() -> Callable[[], object]: + def inner() -> object: + return None + return inner + +def first() -> Callable[[], Callable[[], str]]: + def second() -> Callable[[], str]: + def third() -> str: + return 'third: nested function' + return third + return second + +def f1() -> int: + x = 1 + def f2() -> int: + y = 2 + def f3() -> int: + z = 3 + return y + return f3() + return f2() + +def outer_func() -> int: + def inner_func() -> int: + return x + x = 1 + return inner_func() + +def mutual_recursion(start : int) -> int: + def f1(k : int) -> int: + if k <= 0: + return 0 + k -= 1 + return f2(k) + + def f2(k : int) -> int: + if k <= 0: + return 0 + k -= 1 + return f1(k) + return f1(start) + +def topLayer() -> int: + def middleLayer() -> int: + def bottomLayer() -> int: + return x + + return bottomLayer() + + x = 1 + return middleLayer() + +def nest1() -> str: + def nest2() -> str: + def nest3() -> str: + def mut1(val: int) -> str: + if val <= 0: + return "bottomed" + val -= 1 + return mut2(val) + def mut2(val: int) -> str: + if val <= 0: + return "bottomed" + val -= 1 + return mut1(val) + return mut1(start) + return nest3() + start = 3 + return nest2() + +def uno(num: float) -> Callable[[str], str]: + def dos(s: str) -> str: + return s + '!' + return dos + +def eins(num: float) -> str: + def zwei(s: str) -> str: + return s + '?' + a = zwei('eins') + b = zwei('zwei') + return a + +def call_other_inner_func(a: int) -> int: + def foo() -> int: + return a + 1 + + def bar() -> int: + return foo() + + def baz(n: int) -> int: + if n == 0: + return 0 + return n + baz(n - 1) + + return bar() + baz(a) + +def inner() -> str: + return 'inner: normal function' + +def second() -> str: + return 'second: normal function' + +def third() -> str: + return 'third: normal function' + +[file driver.py] +from native import (outer, inner, first, uno, eins, call_other_inner_func, +second, third, f1, outer_func, mutual_recursion, topLayer, nest1) + +assert outer()() == None +assert inner() == 'inner: normal function' +assert first()()() == 'third: nested function' +assert uno(5.0)('uno') == 'uno!' +assert eins(4.0) == 'eins?' +assert call_other_inner_func(5) == 21 +assert second() == 'second: normal function' +assert third() == 'third: normal function' +assert f1() == 2 +assert outer_func() == 1 +assert mutual_recursion(5) == 0 +assert topLayer() == 1 +assert nest1() == "bottomed" + +[case testFunctionCallWithDefaultArgs] +from typing import Tuple, List, Optional, Callable, Any +def f(x: int, y: int = 3, s: str = "test", z: object = 5) -> Tuple[int, str]: + def inner() -> int: + return x + y + return inner(), s +def g() -> None: + assert f(2) == (5, "test") + assert f(s = "123", x = -2) == (1, "123") +def h(a: Optional[object] = None, b: Optional[str] = None) -> Tuple[object, Optional[str]]: + return (a, b) + +def same(x: object = object()) -> object: + return x + +a_lambda: Callable[..., Any] = lambda n=20: n + +def nested_funcs(n: int) -> List[Callable[..., Any]]: + ls: List[Callable[..., Any]] = [] + for i in range(n): + def f(i: int = i) -> int: + return i + ls.append(f) + return ls + + +[file driver.py] +from native import f, g, h, same, nested_funcs, a_lambda +g() +assert f(2) == (5, "test") +assert f(s = "123", x = -2) == (1, "123") +assert h() == (None, None) +assert h(10) == (10, None) +assert h(b='a') == (None, 'a') +assert h(10, 'a') == (10, 'a') +assert same() == same() + +assert [f() for f in nested_funcs(10)] == list(range(10)) + +assert a_lambda(10) == 10 +assert a_lambda() == 20 + +[case testMethodCallWithDefaultArgs] +from typing import Tuple, List +class A: + def f(self, x: int, y: int = 3, s: str = "test") -> Tuple[int, str]: + def inner() -> int: + return x + y + return inner(), s +def g() -> None: + a = A() + assert a.f(2) == (5, "test") + assert a.f(s = "123", x = -2) == (1, "123") +[file driver.py] +from native import A, g +g() +a = A() +assert a.f(2) == (5, "test") +assert a.f(s = "123", x = -2) == (1, "123") + +[case testMethodCallOrdering] +class A: + def __init__(self, s: str) -> None: + print(s) + def f(self, x: 'A', y: 'A') -> None: + pass + +def g() -> None: + A('A!').f(A('hello'), A('world')) +[file driver.py] +from native import g +g() +[out] +A! +hello +world + +[case testPyMethodCall] +from typing import List +def f(x: List[int]) -> int: + return x.pop() +def g(x: List[int], y: List[int]) -> None: + x.extend(y) +[file driver.py] +from native import f, g +l = [1, 2] +assert f(l) == 2 +g(l, [10]) +assert l == [1, 10] +assert f(l) == 10 +assert f(l) == 1 +g(l, [11, 12]) +assert l == [11, 12] + +[case testMethodCallWithKeywordArgs] +from typing import Tuple +import testmodule +class A: + def echo(self, a: int, b: int, c: int) -> Tuple[int, int, int]: + return a, b, c +def test_native_method_call_with_kwargs() -> None: + a = A() + assert a.echo(1, c=3, b=2) == (1, 2, 3) + assert a.echo(c = 3, a = 1, b = 2) == (1, 2, 3) +def test_module_method_call_with_kwargs() -> None: + a = testmodule.A() + assert a.echo(1, c=3, b=2) == (1, 2, 3) + assert a.echo(c = 3, a = 1, b = 2) == (1, 2, 3) +[file testmodule.py] +from typing import Tuple +class A: + def echo(self, a: int, b: int, c: int) -> Tuple[int, int, int]: + return a, b, c +[file driver.py] +import native +native.test_native_method_call_with_kwargs() +native.test_module_method_call_with_kwargs() + +[case testAnyCall] +from typing import Any +def call(f: Any) -> Any: + return f(1, 'x') +[file driver.py] +from native import call +def f(x, y): + return (x, y) +def g(x): pass + +assert call(f) == (1, 'x') +for bad in g, 1: + try: + call(bad) + except TypeError: + pass + else: + assert False, bad + +[case testCallableTypes] +from typing import Callable +def absolute_value(x: int) -> int: + return x if x > 0 else -x + +def call_native_function(x: int) -> int: + return absolute_value(x) + +def call_python_function(x: int) -> int: + return int(x) + +def return_float() -> float: + return 5.0 + +def return_callable_type() -> Callable[[], float]: + return return_float + +def call_callable_type() -> float: + f = return_callable_type() + return f() + +def return_passed_in_callable_type(f: Callable[[], float]) -> Callable[[], float]: + return f + +def call_passed_in_callable_type(f: Callable[[], float]) -> float: + return f() + +[file driver.py] +from native import call_native_function, call_python_function, return_float, return_callable_type, call_callable_type, return_passed_in_callable_type, call_passed_in_callable_type +a = call_native_function(1) +b = call_python_function(1) +c = return_callable_type() +d = call_callable_type() +e = return_passed_in_callable_type(return_float) +f = call_passed_in_callable_type(return_float) +assert a == 1 +assert b == 1 +assert c() == 5.0 +assert d == 5.0 +assert e() == 5.0 +assert f == 5.0 + +[case testKeywordArgs] +from typing import Tuple +import testmodule + +def g(a: int, b: int, c: int) -> Tuple[int, int, int]: + return a, b, c + +def test_call_native_function_with_keyword_args() -> None: + assert g(1, c = 3, b = 2) == (1, 2, 3) + assert g(c = 3, a = 1, b = 2) == (1, 2, 3) + +def test_call_module_function_with_keyword_args() -> None: + assert testmodule.g(1, c = 3, b = 2) == (1, 2, 3) + assert testmodule.g(c = 3, a = 1, b = 2) == (1, 2, 3) + +def test_call_python_function_with_keyword_args() -> None: + assert int("11", base=2) == 3 + +def test_call_lambda_function_with_keyword_args() -> None: + g = testmodule.get_lambda_function() + assert g(1, c = 3, b = 2) == (1, 2, 3) + assert g(c = 3, a = 1, b = 2) == (1, 2, 3) + +[file testmodule.py] +from typing import Tuple + +def g(a: int, b: int, c: int) -> Tuple[int, int, int]: + return a, b, c + +def get_lambda_function(): + return (lambda a, b, c: (a, b, c)) + +[file driver.py] +import native +native.test_call_native_function_with_keyword_args() +native.test_call_module_function_with_keyword_args() +native.test_call_python_function_with_keyword_args() +native.test_call_lambda_function_with_keyword_args() + +[case testStarArgs] +from typing import Tuple + +def g(a: int, b: int, c: int) -> Tuple[int, int, int]: + return a, b, c + +def test_star_args() -> None: + assert g(*[1, 2, 3]) == (1, 2, 3) + assert g(*(1, 2, 3)) == (1, 2, 3) + assert g(*(1,), *[2, 3]) == (1, 2, 3) + assert g(*(), *(1,), *(), *(2,), *(3,), *()) == (1, 2, 3) + assert g(*range(3)) == (0, 1, 2) + +[file driver.py] +import native +native.test_star_args() + +[case testStar2Args] +from typing import Tuple + +def g(a: int, b: int, c: int) -> Tuple[int, int, int]: + return a, b, c + +def test_star2_args() -> None: + assert g(**{'a': 1, 'b': 2, 'c': 3}) == (1, 2, 3) + assert g(**{'c': 3, 'a': 1, 'b': 2}) == (1, 2, 3) + assert g(b=2, **{'a': 1, 'c': 3}) == (1, 2, 3) + +def test_star2_args_bad(v: dict) -> bool: + return g(a=1, b=2, **v) == (1, 2, 3) +[file driver.py] +import native +native.test_star2_args() + +# this should raise TypeError due to duplicate kwarg, but currently it doesn't +assert native.test_star2_args_bad({'b': 2, 'c': 3}) + +[case testStarAndStar2Args] +from typing import Tuple +def g(a: int, b: int, c: int) -> Tuple[int, int, int]: + return a, b, c + +class C: + def g(self, a: int, b: int, c: int) -> Tuple[int, int, int]: + return a, b, c + +def test_star_and_star2_args() -> None: + assert g(1, *(2,), **{'c': 3}) == (1, 2, 3) + assert g(*[1], **{'b': 2, 'c': 3}) == (1, 2, 3) + c = C() + assert c.g(1, *(2,), **{'c': 3}) == (1, 2, 3) + assert c.g(*[1], **{'b': 2, 'c': 3}) == (1, 2, 3) + +[file driver.py] +import native +native.test_star_and_star2_args() + +[case testAllTheArgCombinations] +from typing import Tuple +def g(a: int, b: int, c: int, d: int = -1) -> Tuple[int, int, int, int]: + return a, b, c, d + +class C: + def g(self, a: int, b: int, c: int, d: int = -1) -> Tuple[int, int, int, int]: + return a, b, c, d + +def test_all_the_arg_combinations() -> None: + assert g(1, *(2,), **{'c': 3}) == (1, 2, 3, -1) + assert g(*[1], **{'b': 2, 'c': 3, 'd': 4}) == (1, 2, 3, 4) + c = C() + assert c.g(1, *(2,), **{'c': 3}) == (1, 2, 3, -1) + assert c.g(*[1], **{'b': 2, 'c': 3, 'd': 4}) == (1, 2, 3, 4) + +[file driver.py] +import native +native.test_all_the_arg_combinations() + +[case testOverloads] +from typing import overload, Union, Tuple + +@overload +def foo(x: int) -> int: ... + +@overload +def foo(x: str) -> str: ... + +def foo(x: Union[int, str]) -> Union[int, str]: + return x + +class A: + @overload + def foo(self, x: int) -> int: ... + + @overload + def foo(self, x: str) -> str: ... + + def foo(self, x: Union[int, str]) -> Union[int, str]: + return x + +def call1() -> Tuple[int, str]: + return (foo(10), foo('10')) +def call2() -> Tuple[int, str]: + x = A() + return (x.foo(10), x.foo('10')) + +[file driver.py] +from native import * +assert call1() == (10, '10') +assert call2() == (10, '10') + +[case testDecorators1] +from typing import Generator, Callable, Iterator +from contextlib import contextmanager + +def a(f: Callable[[], None]) -> Callable[[], None]: + def g() -> None: + print('Entering') + f() + print('Exited') + return g + +def b(f: Callable[[], None]) -> Callable[[], None]: + def g() -> None: + print('***') + f() + print('***') + return g + +@contextmanager +def foo() -> Iterator[int]: + try: + print('started') + yield 0 + finally: + print('finished') + +@contextmanager +def catch() -> Iterator[None]: + try: + print('started') + yield + except IndexError: + print('index') + raise + except Exception: + print('lol') + +def thing() -> None: + c() + +@a +@b +def c() -> None: + @a + @b + def d() -> None: + print('d') + print('c') + d() + +def hm() -> None: + x = [1] + with catch(): + x[2] + +[file driver.py] +from native import foo, c, thing, hm + +with foo() as f: + print('hello') + +c() +thing() +print('==') +try: + hm() +except IndexError: + pass +else: + assert False + +[out] +started +hello +finished +Entering +*** +c +Entering +*** +d +*** +Exited +*** +Exited +Entering +*** +c +Entering +*** +d +*** +Exited +*** +Exited +== +started +index + +[case testDecoratorsMethods] +from typing import Any, Callable, Iterator, TypeVar +from contextlib import contextmanager + +T = TypeVar('T') +def dec(f: T) -> T: + return f + +def a(f: Callable[[Any], None]) -> Callable[[Any], None]: + def g(a: Any) -> None: + print('Entering') + f(a) + print('Exited') + return g + +class A: + @a + def foo(self) -> None: + print('foo') + + @contextmanager + def generator(self) -> Iterator[int]: + try: + print('contextmanager: entering') + yield 0 + finally: + print('contextmanager: exited') + +class Lol: + @staticmethod + def foo() -> None: + Lol.bar() + Lol.baz() + + @staticmethod + @dec + def bar() -> None: + pass + + @classmethod + @dec + def baz(cls) -> None: + pass + +def inside() -> None: + with A().generator() as g: + print('hello!') + +with A().generator() as g: + print('hello!') + +def lol() -> None: + with A().generator() as g: + raise Exception + +[file driver.py] +from native import A, lol + +A.foo(A()) +A().foo() +with A().generator() as g: + print('hello!') +try: + lol() +except: + pass +else: + assert False + +[out] +contextmanager: entering +hello! +contextmanager: exited +Entering +foo +Exited +Entering +foo +Exited +contextmanager: entering +hello! +contextmanager: exited +contextmanager: entering +contextmanager: exited + +[case testUnannotatedFunction] +def g(x: int) -> int: + return x * 2 + +def f(x): + return g(x) +[file driver.py] +from native import f +assert f(3) == 6 + +[case testComplicatedArgs] +from typing import Tuple, Dict + +def kwonly1(x: int = 0, *, y: int) -> Tuple[int, int]: + return x, y + +def kwonly2(*, x: int = 0, y: int) -> Tuple[int, int]: + return x, y + +def kwonly3(a: int, b: int = 0, *, y: int, x: int = 1) -> Tuple[int, int, int, int]: + return a, b, x, y + +def kwonly4(*, x: int, y: int) -> Tuple[int, int]: + return x, y + +def varargs1(*args: int) -> Tuple[int, ...]: + return args + +def varargs2(*args: int, **kwargs: int) -> Tuple[Tuple[int, ...], Dict[str, int]]: + return args, kwargs + +def varargs3(**kwargs: int) -> Dict[str, int]: + return kwargs + +def varargs4(a: int, b: int = 0, + *args: int, y: int, x: int = 1, + **kwargs: int) -> Tuple[Tuple[int, ...], Dict[str, int]]: + return (a, b, *args), {'x': x, 'y': y, **kwargs} + +class A: + def f(self, x: int) -> Tuple[int, ...]: + return (x,) + def g(self, x: int) -> Tuple[Tuple[int, ...], Dict[str, int]]: + return (x,), {} + +class B(A): + def f(self, *args: int) -> Tuple[int, ...]: + return args + def g(self, *args: int, **kwargs: int) -> Tuple[Tuple[int, ...], Dict[str, int]]: + return args, kwargs + +[file other.py] +# This file is imported in both compiled and interpreted mode in order to +# test both native calls and calls via the C API. + +from native import ( + kwonly1, kwonly2, kwonly3, kwonly4, + varargs1, varargs2, varargs3, varargs4, + A, B +) + +# kwonly arg tests +assert kwonly1(10, y=20) == (10, 20) +assert kwonly1(y=20) == (0, 20) + +assert kwonly2(x=10, y=20) == (10, 20) +assert kwonly2(y=20) == (0, 20) + +assert kwonly3(10, y=20) == (10, 0, 1, 20) +assert kwonly3(a=10, y=20) == (10, 0, 1, 20) +assert kwonly3(10, 30, y=20) == (10, 30, 1, 20) +assert kwonly3(10, b=30, y=20) == (10, 30, 1, 20) +assert kwonly3(a=10, b=30, y=20) == (10, 30, 1, 20) + +assert kwonly3(10, x=40, y=20) == (10, 0, 40, 20) +assert kwonly3(a=10, x=40, y=20) == (10, 0, 40, 20) +assert kwonly3(10, 30, x=40, y=20) == (10, 30, 40, 20) +assert kwonly3(10, b=30, x=40, y=20) == (10, 30, 40, 20) +assert kwonly3(a=10, b=30, x=40, y=20) == (10, 30, 40, 20) + +assert kwonly4(x=1, y=2) == (1, 2) +assert kwonly4(y=2, x=1) == (1, 2) + +# varargs tests +assert varargs1() == () +assert varargs1(1, 2, 3) == (1, 2, 3) +assert varargs2(1, 2, 3) == ((1, 2, 3), {}) +assert varargs2(1, 2, 3, x=4) == ((1, 2, 3), {'x': 4}) +assert varargs2(x=4) == ((), {'x': 4}) +assert varargs3() == {} +assert varargs3(x=4) == {'x': 4} +assert varargs3(x=4, y=5) == {'x': 4, 'y': 5} + +assert varargs4(-1, y=2) == ((-1, 0), {'x': 1, 'y': 2}) +assert varargs4(-1, 2, y=2) == ((-1, 2), {'x': 1, 'y': 2}) +assert varargs4(-1, 2, 3, y=2) == ((-1, 2, 3), {'x': 1, 'y': 2}) +assert varargs4(-1, 2, 3, x=10, y=2) == ((-1, 2, 3), {'x': 10, 'y': 2}) +assert varargs4(-1, x=10, y=2) == ((-1, 0), {'x': 10, 'y': 2}) +assert varargs4(-1, y=2, z=20) == ((-1, 0), {'x': 1, 'y': 2, 'z': 20}) +assert varargs4(-1, 2, y=2, z=20) == ((-1, 2), {'x': 1, 'y': 2, 'z': 20}) +assert varargs4(-1, 2, 3, y=2, z=20) == ((-1, 2, 3), {'x': 1, 'y': 2, 'z': 20}) +assert varargs4(-1, 2, 3, x=10, y=2, z=20) == ((-1, 2, 3), {'x': 10, 'y': 2, 'z': 20}) +assert varargs4(-1, x=10, y=2, z=20) == ((-1, 0), {'x': 10, 'y': 2, 'z': 20}) + +x = B() # type: A +assert x.f(1) == (1,) +assert x.g(1) == ((1,), {}) +# This one is really funny! When we make native calls we lose +# track of which arguments are positional or keyword, so the glue +# calls them all positional unless they are keyword only... +# It would be possible to fix this by dynamically tracking which +# arguments were passed by keyword (for example, by passing a bitmask +# to functions indicating this), but paying a speed, size, and complexity +# cost for something so deeply marginal seems like a bad choice. +# assert x.g(x=1) == ((), {'x': 1}) + +[file driver.py] +from testutil import assertRaises +from native import ( + kwonly1, kwonly2, kwonly3, kwonly4, + varargs1, varargs2, varargs3, varargs4, +) + +# Run the non-exceptional tests in both interpreted and compiled mode +import other +import other_interpreted + + +# And the tests for errors at the interfaces in interpreted only +with assertRaises(TypeError, "missing required keyword-only argument 'y'"): + kwonly1() +with assertRaises(TypeError, "takes at most 1 positional argument (2 given)"): + kwonly1(10, 20) + +with assertRaises(TypeError, "missing required keyword-only argument 'y'"): + kwonly2() +with assertRaises(TypeError, "takes no positional arguments"): + kwonly2(10, 20) + +with assertRaises(TypeError, "missing required argument 'a'"): + kwonly3(b=30, x=40, y=20) +with assertRaises(TypeError, "missing required keyword-only argument 'y'"): + kwonly3(10) + +with assertRaises(TypeError, "missing required keyword-only argument 'y'"): + kwonly4(x=1) +with assertRaises(TypeError, "missing required keyword-only argument 'x'"): + kwonly4(y=1) +with assertRaises(TypeError, "missing required keyword-only argument 'x'"): + kwonly4() + +with assertRaises(TypeError, "'x' is an invalid keyword argument for varargs1()"): + varargs1(x=10) +with assertRaises(TypeError, "'x' is an invalid keyword argument for varargs1()"): + varargs1(1, x=10) +with assertRaises(TypeError, "varargs3() takes no positional arguments"): + varargs3(10) +with assertRaises(TypeError, "varargs3() takes no positional arguments"): + varargs3(10, x=10) + +with assertRaises(TypeError, "varargs4() missing required argument 'a' (pos 1)"): + varargs4() +with assertRaises(TypeError, "varargs4() missing required keyword-only argument 'y'"): + varargs4(1, 2) +with assertRaises(TypeError, "varargs4() missing required keyword-only argument 'y'"): + varargs4(1, 2, x=1) +with assertRaises(TypeError, "varargs4() missing required keyword-only argument 'y'"): + varargs4(1, 2, 3) +with assertRaises(TypeError, "varargs4() missing required argument 'a' (pos 1)"): + varargs4(y=20) diff --git a/mypyc/test-data/run-generators.test b/mypyc/test-data/run-generators.test new file mode 100644 index 000000000000..3f34c732b522 --- /dev/null +++ b/mypyc/test-data/run-generators.test @@ -0,0 +1,518 @@ +# Test cases for generators and yield (compile and run) + +[case testYield] +from typing import Generator, Iterable, Union, Tuple, Dict + +def yield_three_times() -> Iterable[int]: + yield 1 + yield 2 + yield 3 + +def yield_twice_and_return() -> Generator[int, None, int]: + yield 1 + yield 2 + return 4 + +def yield_while_loop() -> Generator[int, None, int]: + i = 0 + while i < 5: + if i == 3: + return i + yield i + i += 1 + return -1 + +def yield_for_loop() -> Iterable[int]: + l = [i for i in range(3)] + for i in l: + yield i + + d = {k: None for k in range(3)} + for k in d: + yield k + + for i in range(3): + yield i + + for i in range(three()): + yield i + +def yield_with_except() -> Generator[int, None, None]: + yield 10 + try: + return + except: + print('Caught exception inside generator function') + +def complex_yield(a: int, b: str, c: float) -> Generator[Union[str, int], None, float]: + x = 2 + while x < a: + if x % 2 == 0: + dummy_var = 1 + yield str(x) + ' ' + b + dummy_var = 1 + else: + dummy_var = 1 + yield x + dummy_var = 1 + x += 1 + return c + +def yield_with_default(x: bool = False) -> Iterable[int]: + if x: + yield 0 + +def yield_dict_methods(d1: Dict[int, int], + d2: Dict[int, int], + d3: Dict[int, int]) -> Iterable[int]: + for k in d1.keys(): + yield k + for k, v in d2.items(): + yield k + yield v + for v in d3.values(): + yield v + +def three() -> int: + return 3 + +class A(object): + def __init__(self, x: int) -> None: + self.x = x + + def generator(self) -> Iterable[int]: + yield self.x + +def return_tuple() -> Generator[int, None, Tuple[int, int]]: + yield 0 + return 1, 2 + +[file driver.py] +from native import ( + yield_three_times, + yield_twice_and_return, + yield_while_loop, + yield_for_loop, + yield_with_except, + complex_yield, + yield_with_default, + A, + return_tuple, + yield_dict_methods, +) +from testutil import run_generator +from collections import defaultdict + +assert run_generator(yield_three_times()) == ((1, 2, 3), None) +assert run_generator(yield_twice_and_return()) == ((1, 2), 4) +assert run_generator(yield_while_loop()) == ((0, 1, 2), 3) +assert run_generator(yield_for_loop()) == (tuple(4 * [i for i in range(3)]), None) +assert run_generator(yield_with_except()) == ((10,), None) +assert run_generator(complex_yield(5, 'foo', 1.0)) == (('2 foo', 3, '4 foo'), 1.0) +assert run_generator(yield_with_default()) == ((), None) +assert run_generator(A(0).generator()) == ((0,), None) +assert run_generator(return_tuple()) == ((0,), (1, 2)) +assert run_generator(yield_dict_methods({}, {}, {})) == ((), None) +assert run_generator(yield_dict_methods({1: 2}, {3: 4}, {5: 6})) == ((1, 3, 4, 6), None) +dd = defaultdict(int, {0: 1}) +assert run_generator(yield_dict_methods(dd, dd, dd)) == ((0, 0, 1, 1), None) + +for i in yield_twice_and_return(): + print(i) + +for i in yield_while_loop(): + print(i) + +[out] +1 +2 +0 +1 +2 + +[case testYieldTryFinallyWith] +from typing import Generator, Any + +class Thing: + def __init__(self, x: str) -> None: + self.x = x + def __enter__(self) -> str: + print('enter!', self.x) + if self.x == 'crash': + raise Exception('ohno') + return self.x + def __exit__(self, x: Any, y: Any, z: Any) -> None: + print('exit!', self.x, y) + +def yield_try_finally() -> Generator[int, None, str]: + try: + yield 1 + yield 2 + return 'lol' + except Exception: + raise + finally: + print('goodbye!') + +def yield_with(i: int) -> Generator[int, None, int]: + with Thing('a') as x: + yield 1 + print("yooo?", x) + if i == 0: + yield 2 + return 10 + elif i == 1: + raise Exception('exception!') + return -1 + +[file driver.py] +from native import yield_try_finally, yield_with +from testutil import run_generator + +print(run_generator(yield_try_finally(), p=True)) +print(run_generator(yield_with(0), p=True)) +print(run_generator(yield_with(1), p=True)) +[out] +1 +2 +goodbye! +((1, 2), 'lol') +enter! a +1 +yooo? a +2 +exit! a None +((1, 2), 10) +enter! a +1 +yooo? a +exit! a exception! +((1,), 'exception!') + +[case testYieldNested] +from typing import Callable, Generator + +def normal(a: int, b: float) -> Callable: + def generator(x: int, y: str) -> Generator: + yield a + yield b + yield x + yield y + return generator + +def generator(a: int) -> Generator: + def normal(x: int) -> int: + return a + x + for i in range(3): + yield normal(i) + +def triple() -> Callable: + def generator() -> Generator: + x = 0 + def inner() -> int: + x += 1 + return x + while x < 3: + yield inner() + return generator + +def another_triple() -> Callable: + def generator() -> Generator: + x = 0 + def inner_generator() -> Generator: + x += 1 + yield x + yield next(inner_generator()) + return generator + +def outer() -> Generator: + def recursive(n: int) -> Generator: + if n < 10: + for i in range(n): + yield i + return + for i in recursive(5): + yield i + return recursive(10) + +[file driver.py] +from native import normal, generator, triple, another_triple, outer +from testutil import run_generator + +assert run_generator(normal(1, 2.0)(3, '4.00')) == ((1, 2.0, 3, '4.00'), None) +assert run_generator(generator(1)) == ((1, 2, 3), None) +assert run_generator(triple()()) == ((1, 2, 3), None) +assert run_generator(another_triple()()) == ((1,), None) +assert run_generator(outer()) == ((0, 1, 2, 3, 4), None) + +[case testYieldThrow] +from typing import Generator, Iterable, Any +from traceback import print_tb +from contextlib import contextmanager +import wrapsys + +def generator() -> Iterable[int]: + try: + yield 1 + yield 2 + yield 3 + except Exception as e: + print_tb(wrapsys.exc_info()[2]) + s = str(e) + if s: + print('caught exception with value ' + s) + else: + print('caught exception without value') + return 0 + +def no_except() -> Iterable[int]: + yield 1 + yield 2 + +def raise_something() -> Iterable[int]: + yield 1 + yield 2 + raise Exception('failure') + +def wrapper(x: Any) -> Any: + return (yield from x) + +def foo() -> Generator[int, None, None]: + try: + yield 1 + except Exception as e: + print(str(e)) + finally: + print('goodbye') + +ctx_manager = contextmanager(foo) + +[file wrapsys.py] +# This is a gross hack around some limitations of the test system/mypyc. +from typing import Any +import sys +def exc_info() -> Any: + return sys.exc_info() # type: ignore + +[file driver.py] +import sys +from typing import Generator, Tuple, TypeVar, Sequence +from native import generator, ctx_manager, wrapper, no_except, raise_something + +T = TypeVar('T') +U = TypeVar('U') + +def run_generator_and_throw(gen: Generator[T, None, U], + num_times: int, + value: object = None, + traceback: object = None) -> Tuple[Sequence[T], U]: + res = [] + try: + for i in range(num_times): + res.append(next(gen)) + if value is not None and traceback is not None: + gen.throw(Exception, value, traceback) + elif value is not None: + gen.throw(Exception, value) + else: + gen.throw(Exception) + except StopIteration as e: + return (tuple(res), e.value) + except Exception as e: + return (tuple(res), str(e)) + +assert run_generator_and_throw(generator(), 0, 'hello') == ((), 'hello') +assert run_generator_and_throw(generator(), 3) == ((1, 2, 3), 0) +assert run_generator_and_throw(generator(), 2, 'some string') == ((1, 2), 0) +try: + raise Exception +except Exception as e: + tb = sys.exc_info()[2] + assert run_generator_and_throw(generator(), 1, 'some other string', tb) == ((1,), 0) + +assert run_generator_and_throw(wrapper(generator()), 0, 'hello') == ((), 'hello') +assert run_generator_and_throw(wrapper(generator()), 3) == ((1, 2, 3), 0) +assert run_generator_and_throw(wrapper(generator()), 2, 'some string') == ((1, 2), 0) +# Make sure we aren't leaking exc_info +assert sys.exc_info()[0] is None + +assert run_generator_and_throw(wrapper([1, 2, 3]), 3, 'lol') == ((1, 2, 3), 'lol') +assert run_generator_and_throw(wrapper(no_except()), 2, 'lol') == ((1, 2), 'lol') + +assert run_generator_and_throw(wrapper(raise_something()), 3) == ((1, 2), 'failure') + +with ctx_manager() as c: + raise Exception('exception') + +[out] + File "native.py", line 10, in generator + yield 3 + File "native.py", line 9, in generator + yield 2 + File "native.py", line 8, in generator + yield 1 + File "driver.py", line 31, in + raise Exception + File "native.py", line 10, in generator + yield 3 + File "native.py", line 30, in wrapper + return (yield from x) + File "native.py", line 9, in generator + yield 2 + File "native.py", line 30, in wrapper + return (yield from x) +caught exception without value +caught exception with value some string +caught exception with value some other string +caught exception without value +caught exception with value some string +exception +goodbye + +[case testYieldSend] +from typing import Generator + +def basic() -> Generator[int, int, int]: + x = yield 1 + y = yield (x + 1) + return y + +def use_from() -> Generator[int, int, int]: + return (yield from basic()) + +[file driver.py] +from native import basic, use_from +from testutil import run_generator + +assert run_generator(basic(), [5, 50]) == ((1, 6), 50) +assert run_generator(use_from(), [5, 50]) == ((1, 6), 50) + +[case testYieldFrom] +from typing import Generator, Iterator, List + +def basic() -> Iterator[int]: + yield from [1, 2, 3] + +def call_next() -> int: + x = [] # type: List[int] + return next(iter(x)) + +def inner(b: bool) -> Generator[int, None, int]: + if b: + yield from [1, 2, 3] + return 10 + +def with_return(b: bool) -> Generator[int, None, int]: + x = yield from inner(b) + for a in [1, 2]: + pass + return x + +[file driver.py] +from native import basic, call_next, with_return +from testutil import run_generator, assertRaises + +assert run_generator(basic()) == ((1, 2, 3), None) + +with assertRaises(StopIteration): + call_next() + +assert run_generator(with_return(True)) == ((1, 2, 3), 10) +assert run_generator(with_return(False)) == ((), 10) + +[case testNextGenerator] +from typing import Iterable + +def f(x: int) -> int: + print(x) + return x + +def call_next_loud(l: Iterable[int], val: int) -> int: + return next(i for i in l if f(i) == val) + +def call_next_default(l: Iterable[int], val: int) -> int: + return next((i*2 for i in l if i == val), -1) + +def call_next_default_list(l: Iterable[int], val: int) -> int: + return next((i*2 for i in l if i == val), -1) +[file driver.py] +from native import call_next_loud, call_next_default, call_next_default_list +from testutil import assertRaises + +assert call_next_default([0, 1, 2], 0) == 0 +assert call_next_default([0, 1, 2], 1) == 2 +assert call_next_default([0, 1, 2], 2) == 4 +assert call_next_default([0, 1, 2], 3) == -1 +assert call_next_default([], 0) == -1 +assert call_next_default_list([0, 1, 2], 0) == 0 +assert call_next_default_list([0, 1, 2], 1) == 2 +assert call_next_default_list([0, 1, 2], 2) == 4 +assert call_next_default_list([0, 1, 2], 3) == -1 +assert call_next_default_list([], 0) == -1 + +assert call_next_loud([0, 1, 2], 0) == 0 +assert call_next_loud([0, 1, 2], 1) == 1 +assert call_next_loud([0, 1, 2], 2) == 2 +with assertRaises(StopIteration): + call_next_loud([42], 3) +with assertRaises(StopIteration): + call_next_loud([], 3) + +[out] +0 +0 +1 +0 +1 +2 +42 + +[case testGeneratorSuper] +from typing import Iterator, Callable, Any + +class A(): + def testA(self) -> int: + return 2 + +class B(A): + def testB(self) -> Iterator[int]: + x = super().testA() + while True: + yield x + +def testAsserts(): + b = B() + b_gen = b.testB() + assert next(b_gen) == 2 + +[file driver.py] +from native import testAsserts + +testAsserts() + +[case testNameClashIssues] +class A: + def foo(self) -> object: + yield +class B: + def foo(self) -> object: + yield + +class C: + def foo(self) -> None: + def bar(self) -> None: + pass + +def C___foo() -> None: pass + +class D: + def foo(self) -> None: + def bar(self) -> None: + pass + +class E: + default: int + switch: int + +[file driver.py] +# really I only care it builds diff --git a/mypyc/test-data/run-imports.test b/mypyc/test-data/run-imports.test new file mode 100644 index 000000000000..6b5a70cf6ced --- /dev/null +++ b/mypyc/test-data/run-imports.test @@ -0,0 +1,89 @@ +# Test cases for imports and related features (compile and run) + +[case testImports] +import testmodule + +def f(x: int) -> int: + return testmodule.factorial(5) +def g(x: int) -> int: + from welp import foo + return foo(x) +[file testmodule.py] +def factorial(x: int) -> int: + if x == 0: + return 1 + else: + return x * factorial(x-1) +[file welp.py] +def foo(x: int) -> int: + return x +[file driver.py] +from native import f, g +print(f(5)) +print(g(5)) +[out] +120 +5 + +[case testImportMissing] +# The unchecked module is configured by the test harness to not be +# picked up by mypy, so we can test that we do that right thing when +# calling library modules without stubs. +import unchecked # type: ignore +import unchecked as lol # type: ignore +assert unchecked.x == 10 +assert lol.x == 10 +[file unchecked.py] +x = 10 + +[file driver.py] +import native + +[case testFromImport] +from testmodule import g + +def f(x: int) -> int: + return g(x) +[file testmodule.py] +def g(x: int) -> int: + return x + 1 +[file driver.py] +from native import f +assert f(1) == 2 + +[case testReexport] +# Test that we properly handle accessing values that have been reexported +import a +def f(x: int) -> int: + return a.g(x) + a.foo + a.b.foo + +whatever = a.A() + +[file a.py] +from b import g as g, A as A, foo as foo +import b + +[file b.py] +def g(x: int) -> int: + return x + 1 + +class A: + pass + +foo = 20 + +[file driver.py] +from native import f, whatever +import b + +assert f(20) == 61 +assert isinstance(whatever, b.A) + +[case testAssignModule] +import a +assert a.x == 20 +a.x = 10 +[file a.py] +x = 20 +[file driver.py] +import native diff --git a/mypyc/test-data/run-integers.test b/mypyc/test-data/run-integers.test new file mode 100644 index 000000000000..a24a36470207 --- /dev/null +++ b/mypyc/test-data/run-integers.test @@ -0,0 +1,132 @@ +# Test cases for integers (compile and run) + +[case testInc] +def inc(x: int) -> int: + return x + 1 +[file driver.py] +from native import inc +print(inc(3)) +print(inc(-5)) +print(inc(10**20)) +[out] +4 +-4 +100000000000000000001 + +[case testCount] +def count(n: int) -> int: + i = 1 + while i <= n: + i = i + 1 + return i +[file driver.py] +from native import count +print(count(0)) +print(count(1)) +print(count(5)) +[out] +1 +2 +6 + +[case testIntMathOps] +# This tests integer math things that are either easier to test in Python than +# in our C tests or are tested here because (for annoying reasons) we don't run +# the C unit tests in our 32-bit CI. +def multiply(x: int, y: int) -> int: + return x * y + +# these stringify their outputs because that will catch if exceptions are mishandled +def floor_div(x: int, y: int) -> str: + return str(x // y) +def remainder(x: int, y: int) -> str: + return str(x % y) + +[file driver.py] +from native import multiply, floor_div, remainder + +def test_multiply(x, y): + assert multiply(x, y) == x * y +def test_floor_div(x, y): + assert floor_div(x, y) == str(x // y) +def test_remainder(x, y): + assert remainder(x, y) == str(x % y) + +test_multiply(10**6, 10**6) +test_multiply(2**15, 2**15-1) +test_multiply(2**14, 2**14) + +test_multiply(10**12, 10**12) +test_multiply(2**30, 2**30-1) +test_multiply(2**29, 2**29) + +test_floor_div(-2**62, -1) +test_floor_div(-2**30, -1) +try: + floor_div(10, 0) +except ZeroDivisionError: + pass +else: + assert False, "Expected ZeroDivisionError" + +test_remainder(-2**62, -1) +test_remainder(-2**30, -1) +try: + remainder(10, 0) +except ZeroDivisionError: + pass +else: + assert False, "Expected ZeroDivisionError" + +[case testBigIntLiteral] +def big_int() -> None: + a_62_bit = 4611686018427387902 + max_62_bit = 4611686018427387903 + b_63_bit = 4611686018427387904 + c_63_bit = 9223372036854775806 + max_63_bit = 9223372036854775807 + d_64_bit = 9223372036854775808 + max_32_bit = 2147483647 + max_31_bit = 1073741823 + print(a_62_bit) + print(max_62_bit) + print(b_63_bit) + print(c_63_bit) + print(max_63_bit) + print(d_64_bit) + print(max_32_bit) + print(max_31_bit) +[file driver.py] +from native import big_int +big_int() +[out] +4611686018427387902 +4611686018427387903 +4611686018427387904 +9223372036854775806 +9223372036854775807 +9223372036854775808 +2147483647 +1073741823 + +[case testNeg] +def neg(x: int) -> int: + return -x +[file driver.py] +from native import neg +assert neg(5) == -5 +assert neg(-5) == 5 +assert neg(1073741823) == -1073741823 +assert neg(-1073741823) == 1073741823 +assert neg(1073741824) == -1073741824 +assert neg(-1073741824) == 1073741824 +assert neg(2147483647) == -2147483647 +assert neg(-2147483647) == 2147483647 +assert neg(2147483648) == -2147483648 +assert neg(-2147483648) == 2147483648 +assert neg(4611686018427387904) == -4611686018427387904 +assert neg(-4611686018427387904) == 4611686018427387904 +assert neg(9223372036854775807) == -9223372036854775807 +assert neg(-9223372036854775807) == 9223372036854775807 +assert neg(9223372036854775808) == -9223372036854775808 +assert neg(-9223372036854775808) == 9223372036854775808 diff --git a/mypyc/test-data/run-lists.test b/mypyc/test-data/run-lists.test new file mode 100644 index 000000000000..25fb993e601b --- /dev/null +++ b/mypyc/test-data/run-lists.test @@ -0,0 +1,128 @@ +# Test cases for lists (compile and run) + +[case testListPlusEquals] +from typing import Any +def append(x: Any) -> None: + x += [1] + +[file driver.py] +from native import append +x = [] +append(x) +assert x == [1] + +[case testListSum] +from typing import List +def sum(a: List[int], l: int) -> int: + sum = 0 + i = 0 + while i < l: + sum = sum + a[i] + i = i + 1 + return sum +[file driver.py] +from native import sum +print(sum([], 0)) +print(sum([3], 1)) +print(sum([5, 6, -4], 3)) +print(sum([2**128 + 5, -2**127 - 8], 2)) +[out] +0 +3 +7 +170141183460469231731687303715884105725 + +[case testListSet] +from typing import List +def copy(a: List[int], b: List[int], l: int) -> int: + i = 0 + while i < l: + a[i] = b[i] + i = i + 1 + return 0 +[file driver.py] +from native import copy +a = [0, ''] +copy(a, [-1, 5], 2) +print(1, a) +copy(a, [2**128 + 5, -2**127 - 8], 2) +print(2, a) +[out] +1 [-1, 5] +2 [340282366920938463463374607431768211461, -170141183460469231731687303715884105736] + +[case testSieve] +from typing import List + +def primes(n: int) -> List[int]: + a = [1] * (n + 1) + a[0] = 0 + a[1] = 0 + i = 0 + while i < n: + if a[i] == 1: + j = i * i + while j < n: + a[j] = 0 + j = j + i + i = i + 1 + return a +[file driver.py] +from native import primes +print(primes(3)) +print(primes(13)) +[out] +\[0, 0, 1, 1] +\[0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1] + +[case testListPrims] +from typing import List +def append(x: List[int], n: int) -> None: + x.append(n) +def pop_last(x: List[int]) -> int: + return x.pop() +def pop(x: List[int], i: int) -> int: + return x.pop(i) +def count(x: List[int], i: int) -> int: + return x.count(i) +[file driver.py] +from native import append, pop_last, pop, count +l = [1, 2] +append(l, 10) +assert l == [1, 2, 10] +append(l, 3) +append(l, 4) +append(l, 5) +assert l == [1, 2, 10, 3, 4, 5] +pop_last(l) +pop_last(l) +assert l == [1, 2, 10, 3] +pop(l, 2) +assert l == [1, 2, 3] +pop(l, -2) +assert l == [1, 3] +assert count(l, 1) == 1 +assert count(l, 2) == 0 + +[case testListOfUserDefinedClass] +class C: + x: int + +def f() -> int: + c = C() + c.x = 5 + a = [c] + d = a[0] + return d.x + 1 + +def g() -> int: + a = [C()] + a[0].x = 3 + return a[0].x + 4 +[file driver.py] +from native import f, g +print(f()) +print(g()) +[out] +6 +7 diff --git a/mypyc/test-data/run-loops.test b/mypyc/test-data/run-loops.test new file mode 100644 index 000000000000..b83853bc6d16 --- /dev/null +++ b/mypyc/test-data/run-loops.test @@ -0,0 +1,454 @@ +# Test cases for "for" and "while" loops (compile and run) + +[case testFor] +from typing import List, Tuple +def count(n: int) -> None: + for i in range(n): + print(i) +def count_between(n: int, k: int) -> None: + for i in range(n, k): + print(i) + print('n=', n) +def count_down(n: int, k: int) -> None: + for i in range(n, k, -1): + print(i) +def count_double(n: int, k: int) -> None: + for i in range(n, k, 2): + print(i) +def list_iter(l: List[int]) -> None: + for i in l: + print(i) +def tuple_iter(l: Tuple[int, ...]) -> None: + for i in l: + print(i) +def str_iter(l: str) -> None: + for i in l: + print(i) +def list_rev_iter(l: List[int]) -> None: + for i in reversed(l): + print(i) +def list_rev_iter_lol(l: List[int]) -> None: + for i in reversed(l): + print(i) + if i == 3: + while l: + l.pop() +def count_down_short() -> None: + for i in range(10, 0, -1): + print(i) +[file driver.py] +from native import ( + count, list_iter, list_rev_iter, list_rev_iter_lol, count_between, count_down, count_double, + count_down_short, tuple_iter, str_iter, +) +count(5) +list_iter(list(reversed(range(5)))) +list_rev_iter(list(reversed(range(5)))) +count_between(11, 15) +count_between(10**20, 10**20+3) +count_down(20, 10) +count_double(10, 15) +count_down_short() +print('==') +list_rev_iter_lol(list(reversed(range(5)))) +tuple_iter((1, 2, 3)) +str_iter("abc") +[out] +0 +1 +2 +3 +4 +4 +3 +2 +1 +0 +0 +1 +2 +3 +4 +11 +12 +13 +14 +n= 11 +100000000000000000000 +100000000000000000001 +100000000000000000002 +n= 100000000000000000000 +20 +19 +18 +17 +16 +15 +14 +13 +12 +11 +10 +12 +14 +10 +9 +8 +7 +6 +5 +4 +3 +2 +1 +== +0 +1 +2 +3 +1 +2 +3 +a +b +c + +[case testLoopElse] +from typing import Iterator +def run_for_range(n: int) -> None: + for i in range(n): + if i == 3: + break + print(i) + else: + print(n+1) + +def run_for_list(n: int) -> None: + for i in list(range(n)): + if i == 3: + break + print(i) + else: + print(n+1) + +def run_for_iter(n: int) -> None: + def identity(x: Iterator[int]) -> Iterator[int]: + return x + for i in identity(range(n)): + if i == 3: + break + print(i) + else: + print(n+1) + +def count(n: int) -> int: + i = 1 + while i <= n: + i = i + 1 + if i == 5: + break + else: + i *= -1 + return i + +def nested_while() -> int: + while True: + while False: + pass + else: + break + else: + return -1 + return 0 + +def nested_for() -> int: + for x in range(1000): + for y in [1,2,3]: + pass + else: + break + else: + return -1 + return 0 + +[file driver.py] +from native import run_for_range, run_for_list, run_for_iter, count, nested_while, nested_for +assert nested_while() == 0 +assert nested_for() == 0 +assert count(0) == -1 +assert count(1) == -2 +assert count(5) == 5 +assert count(6) == 5 +run_for_range(3) +run_for_range(5) +print('==') +run_for_list(3) +run_for_list(5) +print('==') +run_for_iter(3) +run_for_iter(5) +[out] +0 +1 +2 +4 +0 +1 +2 +== +0 +1 +2 +4 +0 +1 +2 +== +0 +1 +2 +4 +0 +1 +2 + +[case testNestedLoopSameIdx] +from typing import List, Generator + +def nested_enumerate() -> None: + l1 = [0,1,2] + l2 = [0,1,2] + outer_seen = [] + outer = 0 + for i, j in enumerate(l1): + assert i == outer + outer_seen.append(i) + inner = 0 + for i, k in enumerate(l2): + assert i == inner + inner += 1 + outer += 1 + assert outer_seen == l1 + +def nested_range() -> None: + outer = 0 + outer_seen = [] + for i in range(3): + assert i == outer + outer_seen.append(i) + inner = 0 + for i in range(3): + assert i == inner + inner += 1 + outer += 1 + assert outer_seen == [0,1,2] + +def nested_list() -> None: + l1 = [0,1,2] + l2 = [0,1,2] + outer_seen = [] + outer = 0 + for i in l1: + assert i == outer + outer_seen.append(i) + inner = 0 + for i in l2: + assert i == inner + inner += 1 + outer += 1 + assert outer_seen == l1 + +def nested_yield() -> Generator: + for i in range(3): + for i in range(3): + yield i + yield i + + +[file driver.py] +from native import nested_enumerate, nested_range, nested_list, nested_yield +nested_enumerate() +nested_range() +nested_list() +gen = nested_yield() +for k in range(12): + assert next(gen) == k % 4 +[out] + +[case testForIterable] +from typing import Iterable, Dict, Any, Tuple +def iterate_over_any(a: Any) -> None: + for element in a: + print(element) + +def iterate_over_iterable(iterable: Iterable[T]) -> None: + for element in iterable: + print(element) + +def iterate_and_delete(d: Dict[int, int]) -> None: + for key in d: + d.pop(key) + +def sum_over_values(d: Dict[int, int]) -> int: + s = 0 + for key in d: + s = s + d[key] + return s + +def sum_over_even_values(d: Dict[int, int]) -> int: + s = 0 + for key in d: + if d[key] % 2: + continue + s = s + d[key] + return s + +def sum_over_two_values(d: Dict[int, int]) -> int: + s = 0 + i = 0 + for key in d: + if i == 2: + break + s = s + d[key] + i = i + 1 + return s + +def iterate_over_tuple(iterable: Tuple[int, int, int]) -> None: + for element in iterable: + print(element) + +[file driver.py] +from native import iterate_over_any, iterate_over_iterable, iterate_and_delete, sum_over_values, sum_over_even_values, sum_over_two_values, iterate_over_tuple +import traceback +def broken_generator(n): + num = 0 + while num < n: + yield num + num += 1 + raise Exception('Exception Manually Raised') + +d = {1:1, 2:2, 3:3, 4:4, 5:5} +print(sum_over_values(d)) +print(sum_over_even_values(d)) +print(sum_over_two_values(d)) + +try: + iterate_over_any(5) +except TypeError: + traceback.print_exc() +try: + iterate_over_iterable(broken_generator(5)) +except Exception: + traceback.print_exc() +try: + iterate_and_delete(d) +except RuntimeError: + traceback.print_exc() + +iterate_over_tuple((1, 2, 3)) +[out] +Traceback (most recent call last): + File "driver.py", line 16, in + iterate_over_any(5) + File "native.py", line 3, in iterate_over_any + for element in a: +TypeError: 'int' object is not iterable +Traceback (most recent call last): + File "driver.py", line 20, in + iterate_over_iterable(broken_generator(5)) + File "native.py", line 7, in iterate_over_iterable + for element in iterable: + File "driver.py", line 8, in broken_generator + raise Exception('Exception Manually Raised') +Exception: Exception Manually Raised +Traceback (most recent call last): + File "driver.py", line 24, in + iterate_and_delete(d) + File "native.py", line 11, in iterate_and_delete + for key in d: +RuntimeError: dictionary changed size during iteration +15 +6 +3 +0 +1 +2 +3 +4 +1 +2 +3 + +[case testContinueFor] +def f() -> None: + for n in range(5): + continue +[file driver.py] +from native import f +f() + +[case testMultipleVarsWithLoops] +# Test comprehensions and for loops with multiple index variables +l = [(1, 2, 'a'), (3, 4, 'b'), (5, 6, 'c')] +l2 = [str(a*100+b)+' '+c for a, b, c in l] +l3 = [] +for a, b, c in l: + l3.append(str(a*1000+b)+' '+c) +[file driver.py] +from native import l, l2, l3 +for a in l2 + l3: + print(a) +[out] +102 a +304 b +506 c +1002 a +3004 b +5006 c + +[case testForZipAndEnumerate] +from typing import Iterable, List, Any +def f(a: Iterable[int], b: List[int]) -> List[Any]: + res = [] + for (x, y), z in zip(enumerate(a), b): + res.append((x, y, z)) + return res +def g(a: Iterable[int], b: Iterable[str]) -> List[Any]: + res = [] + for x, (y, z) in enumerate(zip(a, b)): + res.append((x, y, z)) + return res + +[file driver.py] +from native import f, g + +assert f([6, 7], [8, 9]) == [(0, 6, 8), (1, 7, 9)] +assert g([6, 7], ['a', 'b']) == [(0, 6, 'a'), (1, 7, 'b')] +assert f([6, 7], [8]) == [(0, 6, 8)] +assert f([6], [8, 9]) == [(0, 6, 8)] + +[case testIterTypeTrickiness] +# Test inferring the type of a for loop body doesn't cause us grief +# Extracted from somethings that broke in mypy + +from typing import Optional + +# really I only care that this one build +def foo(x: object) -> None: + if isinstance(x, dict): + for a in x: + pass + +def bar(x: Optional[str]) -> None: + vars = ( + ("a", 'lol'), + ("b", 'asdf'), + ("lol", x), + ("an int", 10), + ) + for name, value in vars: + pass + +[file driver.py] +from native import bar +bar(None) diff --git a/mypyc/test-data/run-misc.test b/mypyc/test-data/run-misc.test new file mode 100644 index 000000000000..d0b66920658d --- /dev/null +++ b/mypyc/test-data/run-misc.test @@ -0,0 +1,997 @@ +# Misc test cases (compile and run) + +[case testAsync] +import asyncio + +async def h() -> int: + return 1 + +async def g() -> int: + await asyncio.sleep(0.01) + return await h() + +async def f() -> int: + return await g() + +loop = asyncio.get_event_loop() +result = loop.run_until_complete(f()) +assert result == 1 + +[typing fixtures/typing-full.pyi] + +[file driver.py] +from native import f +import asyncio +loop = asyncio.get_event_loop() +result = loop.run_until_complete(f()) +assert result == 1 + +[case testTrue] +def f() -> bool: + return True +[file driver.py] +from native import f +print(f()) +[out] +True + +[case testBoolIf] +def f(x: bool) -> bool: + if x: + return False + else: + return True +[file driver.py] +from native import f +print(f(True)) +print(f(False)) +[out] +False +True + +[case testMaybeUninitVar] +class C: + def __init__(self, x: int) -> None: + self.x = x + +def f(b: bool) -> None: + u = C(1) + while b: + v = C(2) + if v is not u: + break + print(v.x) +[file driver.py] +from native import f +f(True) +[out] +2 + +[case testUninitBoom] +def f(a: bool, b: bool) -> None: + if a: + x = 'lol' + if b: + print(x) + +def g() -> None: + try: + [0][1] + y = 1 + except Exception: + pass + print(y) + +[file driver.py] +from native import f, g +from testutil import assertRaises + +f(True, True) +f(False, False) +with assertRaises(NameError): + f(False, True) +with assertRaises(NameError): + g() +[out] +lol + +[case testBuiltins] +y = 10 +def f(x: int) -> None: + print(5) + d = globals() + assert d['y'] == 10 + d['y'] = 20 + assert y == 20 +[file driver.py] +from native import f +f(5) +[out] +5 + +[case testOptional] +from typing import Optional + +class A: pass + +def f(x: Optional[A]) -> Optional[A]: + return x + +def g(x: Optional[A]) -> int: + if x is None: + return 1 + if x is not None: + return 2 + return 3 + +def h(x: Optional[int], y: Optional[bool]) -> None: + pass + +[file driver.py] +from native import f, g, A +a = A() +assert f(None) is None +assert f(a) is a +assert g(None) == 1 +assert g(a) == 2 + +[case testWith] +from typing import Any +class Thing: + def __init__(self, x: str) -> None: + self.x = x + def __enter__(self) -> str: + print('enter!', self.x) + if self.x == 'crash': + raise Exception('ohno') + return self.x + def __exit__(self, x: Any, y: Any, z: Any) -> None: + print('exit!', self.x, y) + +def foo(i: int) -> int: + with Thing('a') as x: + print("yooo?", x) + if i == 0: + return 10 + elif i == 1: + raise Exception('exception!') + return -1 + +def bar() -> None: + with Thing('a') as x, Thing('b') as y: + print("yooo?", x, y) + +def baz() -> None: + with Thing('a') as x, Thing('crash') as y: + print("yooo?", x, y) + +[file driver.py] +from native import foo, bar, baz +assert foo(0) == 10 +print('== foo ==') +try: + foo(1) +except Exception: + print('caught') +assert foo(2) == -1 + +print('== bar ==') +bar() + +print('== baz ==') +try: + baz() +except Exception: + print('caught') + +[out] +enter! a +yooo? a +exit! a None +== foo == +enter! a +yooo? a +exit! a exception! +caught +enter! a +yooo? a +exit! a None +== bar == +enter! a +enter! b +yooo? a b +exit! b None +exit! a None +== baz == +enter! a +enter! crash +exit! a ohno +caught + +[case testDisplays] +from typing import List, Set, Tuple, Sequence, Dict, Any + +def listDisplay(x: List[int], y: List[int]) -> List[int]: + return [1, 2, *x, *y, 3] + +def setDisplay(x: Set[int], y: Set[int]) -> Set[int]: + return {1, 2, *x, *y, 3} + +def tupleDisplay(x: Sequence[str], y: Sequence[str]) -> Tuple[str, ...]: + return ('1', '2', *x, *y, '3') + +def dictDisplay(x: str, y1: Dict[str, int], y2: Dict[str, int]) -> Dict[str, int]: + return {x: 2, **y1, 'z': 3, **y2} + +[file driver.py] +from native import listDisplay, setDisplay, tupleDisplay, dictDisplay +assert listDisplay([4], [5, 6]) == [1, 2, 4, 5, 6, 3] +assert setDisplay({4}, {5}) == {1, 2, 3, 4, 5} +assert tupleDisplay(['4', '5'], ['6']) == ('1', '2', '4', '5', '6', '3') +assert dictDisplay('x', {'y1': 1}, {'y2': 2, 'z': 5}) == {'x': 2, 'y1': 1, 'y2': 2, 'z': 5} + +[case testArbitraryLvalues] +from typing import List, Dict, Any + +class O(object): + def __init__(self) -> None: + self.x = 1 + +def increment_attr(a: Any) -> Any: + a.x += 1 + return a + +def increment_attr_o(o: O) -> O: + o.x += 1 + return o + +def increment_all_indices(l: List[int]) -> List[int]: + for i in range(len(l)): + l[i] += 1 + return l + +def increment_all_keys(d: Dict[str, int]) -> Dict[str, int]: + for k in d: + d[k] += 1 + return d + +[file driver.py] +from native import O, increment_attr, increment_attr_o, increment_all_indices, increment_all_keys + +class P(object): + def __init__(self) -> None: + self.x = 0 + +assert increment_attr(P()).x == 1 +assert increment_attr_o(O()).x == 2 +assert increment_all_indices([1, 2, 3]) == [2, 3, 4] +assert increment_all_keys({'a':1, 'b':2, 'c':3}) == {'a':2, 'b':3, 'c':4} + +[case testControlFlowExprs] +from typing import Tuple +def foo() -> object: + print('foo') + return 'foo' +def bar() -> object: + print('bar') + return 'bar' +def t(x: int) -> int: + print(x) + return x + +def f(b: bool) -> Tuple[object, object, object]: + x = foo() if b else bar() + y = b or foo() + z = b and foo() + return (x, y, z) +def g() -> Tuple[object, object]: + return (foo() or bar(), foo() and bar()) + +def nand(p: bool, q: bool) -> bool: + if not (p and q): + return True + return False + +def chained(x: int, y: int, z: int) -> bool: + return t(x) < t(y) > t(z) + +def chained2(x: int, y: int, z: int, w: int) -> bool: + return t(x) < t(y) < t(z) < t(w) +[file driver.py] +from native import f, g, nand, chained, chained2 +assert f(True) == ('foo', True, 'foo') +print() +assert f(False) == ('bar', 'foo', False) +print() +assert g() == ('foo', 'bar') + +assert nand(True, True) == False +assert nand(True, False) == True +assert nand(False, True) == True +assert nand(False, False) == True + +print() +assert chained(10, 20, 15) == True +print() +assert chained(10, 20, 30) == False +print() +assert chained(21, 20, 30) == False +print() +assert chained2(1, 2, 3, 4) == True +print() +assert chained2(1, 0, 3, 4) == False +print() +assert chained2(1, 2, 0, 4) == False +[out] +foo +foo + +bar +foo + +foo +foo +bar + +10 +20 +15 + +10 +20 +30 + +21 +20 + +1 +2 +3 +4 + +1 +0 + +1 +2 +0 + +[case testMultipleAssignment] +from typing import Tuple, List, Any + +def from_tuple(t: Tuple[int, str]) -> List[Any]: + x, y = t + return [y, x] + +def from_list(l: List[int]) -> List[int]: + x, y = l + return [y, x] + +def from_any(o: Any) -> List[Any]: + x, y = o + return [y, x] +[file driver.py] +from native import from_tuple, from_list, from_any + +assert from_tuple((1, 'x')) == ['x', 1] +assert from_list([3, 4]) == [4, 3] +assert from_any('xy') == ['y', 'x'] + +[case testUnpack] +from typing import List + +a, *b = [1, 2, 3, 4, 5] + +*c, d = [1, 2, 3, 4, 5] + +e, *f = [1,2] + +j, *k, l = [1, 2, 3] + +m, *n, o = [1, 2, 3, 4, 5, 6] + +p, q, r, *s, t = [1,2,3,4,5,6,7,8,9,10] + +tup = (1,2,3) +y, *z = tup + +def unpack1(l : List[int]) -> None: + *v1, v2, v3 = l + +def unpack2(l : List[int]) -> None: + v1, *v2, v3 = l + +def unpack3(l : List[int]) -> None: + v1, v2, *v3 = l + +[file driver.py] +from native import a, b, c, d, e, f, j, k, l, m, n, o, p, q, r, s, t, y, z +from native import unpack1, unpack2, unpack3 +from testutil import assertRaises + +assert a == 1 +assert b == [2,3,4,5] +assert c == [1,2,3,4] +assert d == 5 +assert e == 1 +assert f == [2] +assert j == 1 +assert k == [2] +assert l == 3 +assert m == 1 +assert n == [2,3,4,5] +assert o == 6 +assert p == 1 +assert q == 2 +assert r == 3 +assert s == [4,5,6,7,8,9] +assert t == 10 +assert y == 1 +assert z == [2,3] + +with assertRaises(ValueError, "not enough values to unpack"): + unpack1([1]) + +with assertRaises(ValueError, "not enough values to unpack"): + unpack2([1]) + +with assertRaises(ValueError, "not enough values to unpack"): + unpack3([1]) + +[out] + +[case testModuleTopLevel] +x = 1 +print(x) + +def f() -> None: + print(x + 1) + +def g() -> None: + global x + x = 77 + +[file driver.py] +import native +native.f() +native.x = 5 +native.f() +native.g() +print(native.x) + +[out] +1 +2 +6 +77 + +[case testComprehensions] +# A list comprehension +l = [str(x) + " " + str(y) + " " + str(x*y) for x in range(10) + if x != 6 if x != 5 for y in range(x) if y*x != 8] + +# Test short-circuiting as well +def pred(x: int) -> bool: + if x > 6: + raise Exception() + return x > 3 +# If we fail to short-circuit, pred(x) will be called with x=7 +# eventually and will raise an exception. +l2 = [x for x in range(10) if x <= 6 if pred(x)] + +# A dictionary comprehension +d = {k: k*k for k in range(10) if k != 5 if k != 6} + +# A set comprehension +s = {str(x) + " " + str(y) + " " + str(x*y) for x in range(10) + if x != 6 if x != 5 for y in range(x) if y*x != 8} + +[file driver.py] +from native import l, l2, d, s +for a in l: + print(a) +print(tuple(l2)) +for k in sorted(d): + print(k, d[k]) +for a in sorted(s): + print(a) +[out] +1 0 0 +2 0 0 +2 1 2 +3 0 0 +3 1 3 +3 2 6 +4 0 0 +4 1 4 +4 3 12 +7 0 0 +7 1 7 +7 2 14 +7 3 21 +7 4 28 +7 5 35 +7 6 42 +8 0 0 +8 2 16 +8 3 24 +8 4 32 +8 5 40 +8 6 48 +8 7 56 +9 0 0 +9 1 9 +9 2 18 +9 3 27 +9 4 36 +9 5 45 +9 6 54 +9 7 63 +9 8 72 +(4, 5, 6) +0 0 +1 1 +2 4 +3 9 +4 16 +7 49 +8 64 +9 81 +1 0 0 +2 0 0 +2 1 2 +3 0 0 +3 1 3 +3 2 6 +4 0 0 +4 1 4 +4 3 12 +7 0 0 +7 1 7 +7 2 14 +7 3 21 +7 4 28 +7 5 35 +7 6 42 +8 0 0 +8 2 16 +8 3 24 +8 4 32 +8 5 40 +8 6 48 +8 7 56 +9 0 0 +9 1 9 +9 2 18 +9 3 27 +9 4 36 +9 5 45 +9 6 54 +9 7 63 +9 8 72 + +[case testDunders] +from typing import Any +class Item: + def __init__(self, value: str) -> None: + self.value = value + + def __hash__(self) -> int: + return hash(self.value) + + def __eq__(self, rhs: object) -> bool: + return isinstance(rhs, Item) and self.value == rhs.value + + def __lt__(self, x: 'Item') -> bool: + return self.value < x.value + +class Subclass1(Item): + def __bool__(self) -> bool: + return bool(self.value) + +class NonBoxedThing: + def __getitem__(self, index: Item) -> Item: + return Item("2 * " + index.value + " + 1") + +class BoxedThing: + def __getitem__(self, index: int) -> int: + return 2 * index + 1 + +class Subclass2(BoxedThing): + pass + +class UsesNotImplemented: + def __eq__(self, b: object) -> bool: + return NotImplemented + +def index_into(x : Any, y : Any) -> Any: + return x[y] + +def internal_index_into() -> None: + x = BoxedThing() + print (x[3]) + y = NonBoxedThing() + z = Item("3") + print(y[z].value) + +def is_truthy(x: Item) -> bool: + return True if x else False + +[file driver.py] +from native import * +x = BoxedThing() +y = 3 +print(x[y], index_into(x, y)) + +x = Subclass2() +y = 3 +print(x[y], index_into(x, y)) + +z = NonBoxedThing() +w = Item("3") +print(z[w].value, index_into(z, w).value) + +i1 = Item('lolol') +i2 = Item('lol' + 'ol') +i3 = Item('xyzzy') +assert hash(i1) == hash(i2) + +assert i1 == i2 +assert not i1 != i2 +assert not i1 == i3 +assert i1 != i3 +assert i2 < i3 +assert not i1 < i2 +assert i1 == Subclass1('lolol') + +assert is_truthy(Item('')) +assert is_truthy(Item('a')) +assert not is_truthy(Subclass1('')) +assert is_truthy(Subclass1('a')) + +assert UsesNotImplemented() != object() + +internal_index_into() +[out] +7 7 +7 7 +2 * 3 + 1 2 * 3 + 1 +7 +2 * 3 + 1 + +[case testDummyTypes] +from typing import Tuple, List, Dict, NamedTuple +from typing_extensions import Literal, TypedDict, NewType + +class A: + pass + +T = List[A] +U = List[Tuple[int, str]] +Z = List[List[int]] +D = Dict[int, List[int]] +N = NewType('N', int) +G = Tuple[int, str] +def foo(x: N) -> int: + return x +foo(N(10)) +z = N(10) +Lol = NamedTuple('Lol', (('a', int), ('b', T))) +x = Lol(1, []) +def take_lol(x: Lol) -> int: + return x.a + +TD = TypedDict('TD', {'a': int}) +def take_typed_dict(x: TD) -> int: + return x['a'] + +def take_literal(x: Literal[1, 2, 3]) -> None: + print(x) + +[file driver.py] +import sys +from native import * + +if sys.version_info[:3] > (3, 5, 2): + assert "%s %s %s %s" % (T, U, Z, D) == "typing.List[native.A] typing.List[typing.Tuple[int, str]] typing.List[typing.List[int]] typing.Dict[int, typing.List[int]]" +print(x) +print(z) +print(take_lol(x)) +print(take_typed_dict({'a': 20})) +try: + take_typed_dict(None) +except Exception as e: + print(type(e).__name__) + + +take_literal(1) +# We check that the type is the real underlying type +try: + take_literal(None) +except Exception as e: + print(type(e).__name__) +# ... but not that it is a valid literal value +take_literal(10) +[out] +Lol(a=1, b=[]) +10 +1 +20 +TypeError +1 +TypeError +10 + +[case testUnion] +from typing import Union + +class A: + def __init__(self, x: int) -> None: + self.x = x + def f(self, y: int) -> int: + return y + self.x + +class B: + def __init__(self, x: object) -> None: + self.x = x + def f(self, y: object) -> object: + return y + +def f(x: Union[A, str]) -> object: + if isinstance(x, A): + return x.x + else: + return x + 'x' + +def g(x: int) -> Union[A, int]: + if x == 0: + return A(1) + else: + return x + 1 + +def get(x: Union[A, B]) -> object: + return x.x + +def call(x: Union[A, B]) -> object: + return x.f(5) + +[file driver.py] +from native import A, B, f, g, get, call +assert f('a') == 'ax' +assert f(A(4)) == 4 +assert isinstance(g(0), A) +assert g(2) == 3 +assert get(A(5)) == 5 +assert get(B('x')) == 'x' +assert call(A(4)) == 9 +assert call(B('x')) == 5 +try: + f(1) +except TypeError: + pass +else: + assert False + +[case testAnyAll] +from typing import Iterable + +def call_any_nested(l: Iterable[Iterable[int]], val: int = 0) -> int: + res = any(i == val for l2 in l for i in l2) + return 0 if res else 1 + +def call_any(l: Iterable[int], val: int = 0) -> int: + res = any(i == val for i in l) + return 0 if res else 1 + +def call_all(l: Iterable[int], val: int = 0) -> int: + res = all(i == val for i in l) + return 0 if res else 1 + +[file driver.py] +from native import call_any, call_all, call_any_nested + +zeros = [0, 0, 0] +ones = [1, 1, 1] +mixed_001 = [0, 0, 1] +mixed_010 = [0, 1, 0] +mixed_100 = [1, 0, 0] +mixed_011 = [0, 1, 1] +mixed_101 = [1, 0, 1] +mixed_110 = [1, 1, 0] + +assert call_any([]) == 1 +assert call_any(zeros) == 0 +assert call_any(ones) == 1 +assert call_any(mixed_001) == 0 +assert call_any(mixed_010) == 0 +assert call_any(mixed_100) == 0 +assert call_any(mixed_011) == 0 +assert call_any(mixed_101) == 0 +assert call_any(mixed_110) == 0 + +assert call_all([]) == 0 +assert call_all(zeros) == 0 +assert call_all(ones) == 1 +assert call_all(mixed_001) == 1 +assert call_all(mixed_010) == 1 +assert call_all(mixed_100) == 1 +assert call_all(mixed_011) == 1 +assert call_all(mixed_101) == 1 +assert call_all(mixed_110) == 1 + +assert call_any_nested([[1, 1, 1], [1, 1], []]) == 1 +assert call_any_nested([[1, 1, 1], [0, 1], []]) == 0 + +[case testNoneStuff] +from typing import Optional +class A: + x: int + +def lol(x: A) -> None: + setattr(x, 'x', 5) + +def none() -> None: + return + +def arg(x: Optional[A]) -> bool: + return x is None + + +[file driver.py] +import native +native.lol(native.A()) + +# Catch refcounting failures +for i in range(10000): + native.none() + native.arg(None) + +[case testBorrowRefs] +def make_garbage(arg: object) -> None: + b = True + while b: + arg = None + b = False + +[file driver.py] +from native import make_garbage +import sys + +def test(): + x = object() + r0 = sys.getrefcount(x) + make_garbage(x) + r1 = sys.getrefcount(x) + assert r0 == r1 + +test() + +[case testFinalStaticRunFail] +if False: + from typing import Final + +if bool(): + x: 'Final' = [1] + +def f() -> int: + return x[0] + +[file driver.py] +from native import f +try: + print(f()) +except NameError as e: + print(e.args[0]) +[out] +value for final name "x" was not set + +[case testFinalStaticRunListTupleInt] +if False: + from typing import Final + +x: 'Final' = [1] +y: 'Final' = (1, 2) +z: 'Final' = 1 + 1 + +def f() -> int: + return x[0] +def g() -> int: + return y[0] +def h() -> int: + return z - 1 + +[file driver.py] +from native import f, g, h, x, y, z +print(f()) +print(x[0]) +print(g()) +print(y) +print(h()) +print(z) +[out] +1 +1 +1 +(1, 2) +1 +2 + +[case testCheckVersion] +import sys + +# We lie about the version we are running in tests if it is 3.5, so +# that hits a crash case. +if sys.version_info[:2] == (3, 9): + def version() -> int: + return 9 +elif sys.version_info[:2] == (3, 8): + def version() -> int: + return 8 +elif sys.version_info[:2] == (3, 7): + def version() -> int: + return 7 +elif sys.version_info[:2] == (3, 6): + def version() -> int: + return 6 +else: + raise Exception("we don't support this version yet!") + + +[file driver.py] +import sys +version = sys.version_info[:2] + +try: + import native + assert version != (3, 5), "3.5 should fail!" + assert native.version() == sys.version_info[1] +except RuntimeError: + assert version == (3, 5), "only 3.5 should fail!" + +[case testTypeErrorMessages] +from typing import Tuple +class A: + pass +class B: + pass + +def f(x: B) -> None: + pass +def g(x: Tuple[int, A]) -> None: + pass +[file driver.py] +from testutil import assertRaises +from native import A, f, g + +class Busted: + pass +Busted.__module__ = None + +with assertRaises(TypeError, "int"): + f(0) +with assertRaises(TypeError, "native.A"): + f(A()) +with assertRaises(TypeError, "tuple[None, native.A]"): + f((None, A())) +with assertRaises(TypeError, "tuple[tuple[int, str], native.A]"): + f(((1, "ha"), A())) +with assertRaises(TypeError, "tuple[<50 items>]"): + f(tuple(range(50))) + +with assertRaises(TypeError, "errored formatting real type!"): + f(Busted()) + +with assertRaises(TypeError, "tuple[int, native.A] object expected; got tuple[int, int]"): + g((20, 30)) + +[case testComprehensionShadowBinder] +def foo(x: object) -> object: + if isinstance(x, list): + return tuple(x for x in x), x + return None + +[file driver.py] +from native import foo + +assert foo(None) == None +assert foo([1, 2, 3]) == ((1, 2, 3), [1, 2, 3]) diff --git a/mypyc/test-data/run-primitives.test b/mypyc/test-data/run-primitives.test new file mode 100644 index 000000000000..450480d3f0a6 --- /dev/null +++ b/mypyc/test-data/run-primitives.test @@ -0,0 +1,399 @@ +# Test cases for misc primitives (compile and run) +# +# Please only add tests that don't have an obvious place in type-specific test +# files such as run-strings.test, run-lists.test, etc. + +[case testGenericEquality] +def eq(a: object, b: object) -> bool: + if a == b: + return True + else: + return False +def ne(a: object, b: object) -> bool: + if a != b: + return True + else: + return False +def f(o: object) -> bool: + if [1, 2] == o: + return True + else: + return False +[file driver.py] +from native import eq, ne, f +assert eq('xz', 'x' + 'z') +assert not eq('x', 'y') +assert not ne('xz', 'x' + 'z') +assert ne('x', 'y') +assert f([1, 2]) +assert not f([2, 2]) +assert not f(1) + +[case testGenericBinaryOps] +from typing import Any +def add(x: Any, y: Any) -> Any: + return x + y +def subtract(x: Any, y: Any) -> Any: + return x - y +def multiply(x: Any, y: Any) -> Any: + return x * y +def floor_div(x: Any, y: Any) -> Any: + return x // y +def true_div(x: Any, y: Any) -> Any: + return x / y +def remainder(x: Any, y: Any) -> Any: + return x % y +def power(x: Any, y: Any) -> Any: + return x ** y +def lshift(x: Any, y: Any) -> Any: + return x << y +def rshift(x: Any, y: Any) -> Any: + return x >> y +def num_and(x: Any, y: Any) -> Any: + return x & y +def num_xor(x: Any, y: Any) -> Any: + return x ^ y +def num_or(x: Any, y: Any) -> Any: + return x | y +def lt(x: Any, y: Any) -> Any: + if x < y: + return True + else: + return False +def le(x: Any, y: Any) -> Any: + if x <= y: + return True + else: + return False +def gt(x: Any, y: Any) -> Any: + if x > y: + return True + else: + return False +def ge(x: Any, y: Any) -> Any: + if x >= y: + return True + else: + return False +def contains(x: Any, y: Any) -> Any: + if x in y: + return True + else: + return False +def identity(x: Any, y: Any) -> Any: + if x is y: + return True + else: + return False +def disidentity(x: Any, y: Any) -> Any: + if x is not y: + return True + else: + return False +def not_eq_cond(a: Any, b: Any) -> bool: + if not (a == b): + return True + else: + return False +def eq2(a: Any, b: Any) -> bool: + return a == b +def slice1(x: Any) -> Any: + return x[:] +def slice2(x: Any, y: Any) -> Any: + return x[y:] +def slice3(x: Any, y: Any) -> Any: + return x[:y] +def slice4(x: Any, y: Any, z: Any) -> Any: + return x[y:z] +def slice5(x: Any, y: Any, z: Any, zz: Any) -> Any: + return x[y:z:zz] +[file driver.py] +from native import * +assert add(5, 6) == 11 +assert add('x', 'y') == 'xy' +assert subtract(8, 3) == 5 +assert multiply(8, 3) == 24 +assert floor_div(8, 3) == 2 +assert true_div(7, 2) == 3.5 +assert remainder(11, 4) == 3 +assert remainder('%.3d', 5) == '005' +assert remainder('%d-%s', (5, 'xy')) == '5-xy' +assert power(3, 4) == 81 +assert lshift(5, 3) == 40 +assert rshift(41, 3) == 5 +assert num_and(99, 56) == 32 +assert num_xor(99, 56) == 91 +assert num_or(99, 56) == 123 +assert lt('a', 'b') +assert not lt('a', 'a') +assert not lt('b', 'a') +assert not gt('a', 'b') +assert not gt('a', 'a') +assert gt('b', 'a') +assert le('a', 'b') +assert le('a', 'a') +assert not le('b', 'a') +assert not ge('a', 'b') +assert ge('a', 'a') +assert ge('b', 'a') +assert contains('x', 'axb') +assert not contains('X', 'axb') +assert contains('x', {'x', 'y'}) +a = [1, 3, 5] +assert slice1(a) == a +assert slice1(a) is not a +assert slice2(a, 1) == [3, 5] +assert slice3(a, -1) == [1, 3] +assert slice4(a, 1, -1) == [3] +assert slice5(a, 2, 0, -1) == [5, 3] +o1, o2 = object(), object() +assert identity(o1, o1) +assert not identity(o1, o2) +assert not disidentity(o1, o1) +assert disidentity(o1, o2) +assert eq2('xz', 'x' + 'z') +assert not eq2('x', 'y') +assert not not_eq_cond('xz', 'x' + 'z') +assert not_eq_cond('x', 'y') + +[case testGenericMiscOps] +from typing import Any +def neg(x: Any) -> Any: + return -x +def pos(x: Any) -> Any: + return +x +def invert(x: Any) -> Any: + return ~x +def get_item(o: Any, k: Any) -> Any: + return o[k] +def set_item(o: Any, k: Any, v: Any) -> Any: + o[k] = v +[file driver.py] +from native import * +assert neg(6) == -6 +assert pos(6) == 6 +assert invert(6) == -7 +d = {'x': 5} +assert get_item(d, 'x') == 5 +set_item(d, 'y', 6) +assert d['y'] == 6 + +[case testAnyAttributeAndMethodAccess] +from typing import Any, List +class C: + a: int + def m(self, x: int, a: List[int]) -> int: + return self.a + x + a[0] +def get_a(x: Any) -> Any: + return x.a +def set_a(x: Any, y: Any) -> None: + x.a = y +def call_m(x: Any) -> Any: + return x.m(1, [3]) +[file driver.py] +from native import C, get_a, set_a, call_m +class D: + def m(self, x, a): + return self.a + x + a[0] + +c = C() +c.a = 6 +d = D() +d.a = 2 +assert get_a(c) == 6 +assert get_a(d) == 2 +assert call_m(c) == 10 +assert call_m(d) == 6 +set_a(c, 5) +assert c.a == 5 +set_a(d, 4) +assert d.a == 4 +try: + get_a(object()) +except AttributeError: + pass +else: + assert False +try: + call_m(object()) +except AttributeError: + pass +else: + assert False +try: + set_a(object(), 5) +except AttributeError: + pass +else: + assert False + +[case testFloat] +def assign_and_return_float_sum() -> float: + f1 = 1.0 + f2 = 2.0 + f3 = 3.0 + return f1 * f2 + f3 + +def from_int(i: int) -> float: + return float(i) + +def to_int(x: float) -> int: + return int(x) + +def get_complex() -> complex: + return 5.0j + 3.0 + +[file driver.py] +from native import assign_and_return_float_sum, from_int, to_int, get_complex +sum = 0.0 +for i in range(10): + sum += assign_and_return_float_sum() +assert sum == 50.0 + +assert str(from_int(10)) == '10.0' +assert str(to_int(3.14)) == '3' +assert str(to_int(3)) == '3' +assert get_complex() == 3+5j + +[case testBytes] +def f(x: bytes) -> bytes: + return x + +def concat(a: bytes, b: bytes) -> bytes: + return a + b + +def eq(a: bytes, b: bytes) -> bool: + return a == b + +def neq(a: bytes, b: bytes) -> bool: + return a != b + +def join() -> bytes: + seq = (b'1', b'"', b'\xf0') + return b'\x07'.join(seq) +[file driver.py] +from native import f, concat, eq, neq, join +assert f(b'123') == b'123' +assert f(b'\x07 \x0b " \t \x7f \xf0') == b'\x07 \x0b " \t \x7f \xf0' +assert concat(b'123', b'456') == b'123456' +assert eq(b'123', b'123') +assert not eq(b'123', b'1234') +assert neq(b'123', b'1234') +assert join() == b'1\x07"\x07\xf0' + +[case testDel] +from typing import List +from testutil import assertRaises + +def printDict(dict) -> None: + l = list(dict.keys()) # type: List[str] + l.sort() + for key in l: + print(key, dict[key]) + print("#########") + +def delList() -> None: + l = [1, 2, 3] + print(tuple(l)) + del l[1] + print(tuple(l)) + +def delDict() -> None: + d = {"one":1, "two":2} + printDict(d) + del d["one"] + printDict(d) + +def delListMultiple() -> None: + l = [1, 2, 3, 4, 5, 6, 7] + print(tuple(l)) + del l[1], l[2], l[3] + print(tuple(l)) + +def delDictMultiple() -> None: + d = {"one":1, "two":2, "three":3, "four":4} + printDict(d) + del d["two"], d["four"] + printDict(d) + +class Dummy(): + def __init__(self, x: int, y: int) -> None: + self.x = x + self.y = y + +def delAttribute() -> None: + dummy = Dummy(1, 2) + del dummy.x + with assertRaises(AttributeError): + dummy.x + +def delAttributeMultiple() -> None: + dummy = Dummy(1, 2) + del dummy.x, dummy.y + with assertRaises(AttributeError): + dummy.x + with assertRaises(AttributeError): + dummy.y + +def delLocal(b: bool) -> int: + dummy = 10 + if b: + del dummy + return dummy + +def delLocalLoop() -> None: + # Try deleting a local in a loop to make sure the control flow analysis works + dummy = 1 + for i in range(10): + print(dummy) + dummy *= 2 + if i == 4: + del dummy + +global_var = 10 +del global_var + +[file driver.py] +from native import ( + delList, delDict, delListMultiple, delDictMultiple, delAttribute, + delAttributeMultiple, delLocal, delLocalLoop, +) +import native +from testutil import assertRaises + +delList() +delDict() +delListMultiple() +delDictMultiple() +delAttribute() +delAttributeMultiple() +with assertRaises(AttributeError): + native.global_var +with assertRaises(NameError, "local variable 'dummy' referenced before assignment"): + delLocal(True) +assert delLocal(False) == 10 +with assertRaises(NameError, "local variable 'dummy' referenced before assignment"): + delLocalLoop() +[out] +(1, 2, 3) +(1, 3) +one 1 +two 2 +######### +two 2 +######### +(1, 2, 3, 4, 5, 6, 7) +(1, 3, 5, 7) +four 4 +one 1 +three 3 +two 2 +######### +one 1 +three 3 +######### +1 +2 +4 +8 +16 diff --git a/mypyc/test-data/run-sets.test b/mypyc/test-data/run-sets.test new file mode 100644 index 000000000000..93b86771b19f --- /dev/null +++ b/mypyc/test-data/run-sets.test @@ -0,0 +1,107 @@ +# Test cases for sets (compile and run) + +[case testSets] +from typing import Set, List +def instantiateLiteral() -> Set[int]: + return {1, 2, 3, 5, 8} + +def fromIterator() -> List[Set[int]]: + x = set([1, 3, 5]) + y = set((1, 3, 5)) + z = set({1: '1', 3: '3', 5: '5'}) + return [x, y, z] + +def addIncrementing(s : Set[int]) -> None: + for a in [1, 2, 3]: + if a not in s: + s.add(a) + return + +def replaceWith1(s : Set[int]) -> None: + s.clear() + s.add(1) + +def remove1(s : Set[int]) -> None: + s.remove(1) + +def discard1(s: Set[int]) -> None: + s.discard(1) + +def pop(s : Set[int]) -> int: + return s.pop() + +def update(s: Set[int], x: List[int]) -> None: + s.update(x) + +[file driver.py] +from native import instantiateLiteral +from testutil import assertRaises + +val = instantiateLiteral() +assert 1 in val +assert 2 in val +assert 3 in val +assert 5 in val +assert 8 in val +assert len(val) == 5 +assert val == {1, 2, 3, 5, 8} +s = 0 +for i in val: + s += i +assert s == 19 + +from native import fromIterator +sets = fromIterator() +for s in sets: + assert s == {1, 3, 5} + +from native import addIncrementing +s = set() +addIncrementing(s) +assert s == {1} +addIncrementing(s) +assert s == {1, 2} +addIncrementing(s) +assert s == {1, 2, 3} + +from native import replaceWith1 +s = {3, 7, 12} +replaceWith1(s) +assert s == {1} + +from native import remove1 +import traceback +s = {1, 4, 6} +remove1(s) +assert s == {4, 6} +with assertRaises(KeyError, '1'): + remove1(s) + +from native import discard1 +s = {1, 4, 6} +discard1(s) +assert s == {4, 6} +discard1(s) +assert s == {4, 6} + +from native import pop +s = {1, 2, 3} +x = pop(s) +assert len(s) == 2 +assert x in [1, 2, 3] +y = pop(s) +assert len(s) == 1 +assert y in [1, 2, 3] +assert x != y +z = pop(s) +assert len(s) == 0 +assert z in [1, 2, 3] +assert x != z +assert y != z +with assertRaises(KeyError, 'pop from an empty set'): + pop(s) + +from native import update +s = {1, 2, 3} +update(s, [5, 4, 3]) +assert s == {1, 2, 3, 4, 5} diff --git a/mypyc/test-data/run.test b/mypyc/test-data/run.test deleted file mode 100644 index 8db18e19ef7d..000000000000 --- a/mypyc/test-data/run.test +++ /dev/null @@ -1,4600 +0,0 @@ -# Misc test cases (compile and run) - -[case testCallTrivialFunction] -def f(x: int) -> int: - return x -[file driver.py] -from native import f -print(f(3)) -print(f(-157)) -print(f(10**20)) -print(f(-10**20)) -[out] -3 --157 -100000000000000000000 --100000000000000000000 - -[case testInc] -def inc(x: int) -> int: - return x + 1 -[file driver.py] -from native import inc -print(inc(3)) -print(inc(-5)) -print(inc(10**20)) -[out] -4 --4 -100000000000000000001 - -[case testCount] -def count(n: int) -> int: - i = 1 - while i <= n: - i = i + 1 - return i -[file driver.py] -from native import count -print(count(0)) -print(count(1)) -print(count(5)) -[out] -1 -2 -6 - -[case testFor] -from typing import List, Tuple -def count(n: int) -> None: - for i in range(n): - print(i) -def count_between(n: int, k: int) -> None: - for i in range(n, k): - print(i) - print('n=', n) -def count_down(n: int, k: int) -> None: - for i in range(n, k, -1): - print(i) -def count_double(n: int, k: int) -> None: - for i in range(n, k, 2): - print(i) -def list_iter(l: List[int]) -> None: - for i in l: - print(i) -def tuple_iter(l: Tuple[int, ...]) -> None: - for i in l: - print(i) -def str_iter(l: str) -> None: - for i in l: - print(i) -def list_rev_iter(l: List[int]) -> None: - for i in reversed(l): - print(i) -def list_rev_iter_lol(l: List[int]) -> None: - for i in reversed(l): - print(i) - if i == 3: - while l: - l.pop() -def count_down_short() -> None: - for i in range(10, 0, -1): - print(i) -[file driver.py] -from native import ( - count, list_iter, list_rev_iter, list_rev_iter_lol, count_between, count_down, count_double, - count_down_short, tuple_iter, str_iter, -) -count(5) -list_iter(list(reversed(range(5)))) -list_rev_iter(list(reversed(range(5)))) -count_between(11, 15) -count_between(10**20, 10**20+3) -count_down(20, 10) -count_double(10, 15) -count_down_short() -print('==') -list_rev_iter_lol(list(reversed(range(5)))) -tuple_iter((1, 2, 3)) -str_iter("abc") -[out] -0 -1 -2 -3 -4 -4 -3 -2 -1 -0 -0 -1 -2 -3 -4 -11 -12 -13 -14 -n= 11 -100000000000000000000 -100000000000000000001 -100000000000000000002 -n= 100000000000000000000 -20 -19 -18 -17 -16 -15 -14 -13 -12 -11 -10 -12 -14 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 -== -0 -1 -2 -3 -1 -2 -3 -a -b -c - -[case testLoopElse] -from typing import Iterator -def run_for_range(n: int) -> None: - for i in range(n): - if i == 3: - break - print(i) - else: - print(n+1) - -def run_for_list(n: int) -> None: - for i in list(range(n)): - if i == 3: - break - print(i) - else: - print(n+1) - -def run_for_iter(n: int) -> None: - def identity(x: Iterator[int]) -> Iterator[int]: - return x - for i in identity(range(n)): - if i == 3: - break - print(i) - else: - print(n+1) - -def count(n: int) -> int: - i = 1 - while i <= n: - i = i + 1 - if i == 5: - break - else: - i *= -1 - return i - -def nested_while() -> int: - while True: - while False: - pass - else: - break - else: - return -1 - return 0 - -def nested_for() -> int: - for x in range(1000): - for y in [1,2,3]: - pass - else: - break - else: - return -1 - return 0 - -[file driver.py] -from native import run_for_range, run_for_list, run_for_iter, count, nested_while, nested_for -assert nested_while() == 0 -assert nested_for() == 0 -assert count(0) == -1 -assert count(1) == -2 -assert count(5) == 5 -assert count(6) == 5 -run_for_range(3) -run_for_range(5) -print('==') -run_for_list(3) -run_for_list(5) -print('==') -run_for_iter(3) -run_for_iter(5) -[out] -0 -1 -2 -4 -0 -1 -2 -== -0 -1 -2 -4 -0 -1 -2 -== -0 -1 -2 -4 -0 -1 -2 - -[case testNestedLoopSameIdx] -from typing import List, Generator - -def nested_enumerate() -> None: - l1 = [0,1,2] - l2 = [0,1,2] - outer_seen = [] - outer = 0 - for i, j in enumerate(l1): - assert i == outer - outer_seen.append(i) - inner = 0 - for i, k in enumerate(l2): - assert i == inner - inner += 1 - outer += 1 - assert outer_seen == l1 - -def nested_range() -> None: - outer = 0 - outer_seen = [] - for i in range(3): - assert i == outer - outer_seen.append(i) - inner = 0 - for i in range(3): - assert i == inner - inner += 1 - outer += 1 - assert outer_seen == [0,1,2] - -def nested_list() -> None: - l1 = [0,1,2] - l2 = [0,1,2] - outer_seen = [] - outer = 0 - for i in l1: - assert i == outer - outer_seen.append(i) - inner = 0 - for i in l2: - assert i == inner - inner += 1 - outer += 1 - assert outer_seen == l1 - -def nested_yield() -> Generator: - for i in range(3): - for i in range(3): - yield i - yield i - - -[file driver.py] -from native import nested_enumerate, nested_range, nested_list, nested_yield -nested_enumerate() -nested_range() -nested_list() -gen = nested_yield() -for k in range(12): - assert next(gen) == k % 4 -[out] - -[case testAsync] -import asyncio - -async def h() -> int: - return 1 - -async def g() -> int: - await asyncio.sleep(0.01) - return await h() - -async def f() -> int: - return await g() - -loop = asyncio.get_event_loop() -result = loop.run_until_complete(f()) -assert result == 1 - -[typing fixtures/typing-full.pyi] - -[file driver.py] -from native import f -import asyncio -loop = asyncio.get_event_loop() -result = loop.run_until_complete(f()) -assert result == 1 - -[case testRecursiveFibonacci] -def fib(n: int) -> int: - if n <= 1: - return 1 - else: - return fib(n - 1) + fib(n - 2) - return 0 # TODO: This should be unnecessary -[file driver.py] -from native import fib -print(fib(0)) -print(fib(1)) -print(fib(2)) -print(fib(6)) -[out] -1 -1 -2 -13 - -[case testListPlusEquals] -from typing import Any -def append(x: Any) -> None: - x += [1] - -[file driver.py] -from native import append -x = [] -append(x) -assert x == [1] - -[case testListSum] -from typing import List -def sum(a: List[int], l: int) -> int: - sum = 0 - i = 0 - while i < l: - sum = sum + a[i] - i = i + 1 - return sum -[file driver.py] -from native import sum -print(sum([], 0)) -print(sum([3], 1)) -print(sum([5, 6, -4], 3)) -print(sum([2**128 + 5, -2**127 - 8], 2)) -[out] -0 -3 -7 -170141183460469231731687303715884105725 - -[case testListSet] -from typing import List -def copy(a: List[int], b: List[int], l: int) -> int: - i = 0 - while i < l: - a[i] = b[i] - i = i + 1 - return 0 -[file driver.py] -from native import copy -a = [0, ''] -copy(a, [-1, 5], 2) -print(1, a) -copy(a, [2**128 + 5, -2**127 - 8], 2) -print(2, a) -[out] -1 [-1, 5] -2 [340282366920938463463374607431768211461, -170141183460469231731687303715884105736] - -[case testSieve] -from typing import List - -def primes(n: int) -> List[int]: - a = [1] * (n + 1) - a[0] = 0 - a[1] = 0 - i = 0 - while i < n: - if a[i] == 1: - j = i * i - while j < n: - a[j] = 0 - j = j + i - i = i + 1 - return a -[file driver.py] -from native import primes -print(primes(3)) -print(primes(13)) -[out] -\[0, 0, 1, 1] -\[0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1] - - -[case testListPrims] -from typing import List -def append(x: List[int], n: int) -> None: - x.append(n) -def pop_last(x: List[int]) -> int: - return x.pop() -def pop(x: List[int], i: int) -> int: - return x.pop(i) -def count(x: List[int], i: int) -> int: - return x.count(i) -[file driver.py] -from native import append, pop_last, pop, count -l = [1, 2] -append(l, 10) -assert l == [1, 2, 10] -append(l, 3) -append(l, 4) -append(l, 5) -assert l == [1, 2, 10, 3, 4, 5] -pop_last(l) -pop_last(l) -assert l == [1, 2, 10, 3] -pop(l, 2) -assert l == [1, 2, 3] -pop(l, -2) -assert l == [1, 3] -assert count(l, 1) == 1 -assert count(l, 2) == 0 - -[case testTrue] -def f() -> bool: - return True -[file driver.py] -from native import f -print(f()) -[out] -True - -[case testBoolIf] -def f(x: bool) -> bool: - if x: - return False - else: - return True -[file driver.py] -from native import f -print(f(True)) -print(f(False)) -[out] -False -True - -[case testMaybeUninitVar] -class C: - def __init__(self, x: int) -> None: - self.x = x - -def f(b: bool) -> None: - u = C(1) - while b: - v = C(2) - if v is not u: - break - print(v.x) -[file driver.py] -from native import f -f(True) -[out] -2 - -[case testUninitBoom] -def f(a: bool, b: bool) -> None: - if a: - x = 'lol' - if b: - print(x) - -def g() -> None: - try: - [0][1] - y = 1 - except Exception: - pass - print(y) - -[file driver.py] -from native import f, g -from testutil import assertRaises - -f(True, True) -f(False, False) -with assertRaises(NameError): - f(False, True) -with assertRaises(NameError): - g() -[out] -lol - -[case testFunctionCallWithDefaultArgs] -from typing import Tuple, List, Optional, Callable, Any -def f(x: int, y: int = 3, s: str = "test", z: object = 5) -> Tuple[int, str]: - def inner() -> int: - return x + y - return inner(), s -def g() -> None: - assert f(2) == (5, "test") - assert f(s = "123", x = -2) == (1, "123") -def h(a: Optional[object] = None, b: Optional[str] = None) -> Tuple[object, Optional[str]]: - return (a, b) - -def same(x: object = object()) -> object: - return x - -a_lambda: Callable[..., Any] = lambda n=20: n - -def nested_funcs(n: int) -> List[Callable[..., Any]]: - ls: List[Callable[..., Any]] = [] - for i in range(n): - def f(i: int = i) -> int: - return i - ls.append(f) - return ls - - -[file driver.py] -from native import f, g, h, same, nested_funcs, a_lambda -g() -assert f(2) == (5, "test") -assert f(s = "123", x = -2) == (1, "123") -assert h() == (None, None) -assert h(10) == (10, None) -assert h(b='a') == (None, 'a') -assert h(10, 'a') == (10, 'a') -assert same() == same() - -assert [f() for f in nested_funcs(10)] == list(range(10)) - -assert a_lambda(10) == 10 -assert a_lambda() == 20 - -[case testMethodCallWithDefaultArgs] -from typing import Tuple, List -class A: - def f(self, x: int, y: int = 3, s: str = "test") -> Tuple[int, str]: - def inner() -> int: - return x + y - return inner(), s -def g() -> None: - a = A() - assert a.f(2) == (5, "test") - assert a.f(s = "123", x = -2) == (1, "123") -[file driver.py] -from native import A, g -g() -a = A() -assert a.f(2) == (5, "test") -assert a.f(s = "123", x = -2) == (1, "123") - -[case testMethodCallOrdering] -class A: - def __init__(self, s: str) -> None: - print(s) - def f(self, x: 'A', y: 'A') -> None: - pass - -def g() -> None: - A('A!').f(A('hello'), A('world')) -[file driver.py] -from native import g -g() -[out] -A! -hello -world - -[case testImports] -import testmodule - -def f(x: int) -> int: - return testmodule.factorial(5) -def g(x: int) -> int: - from welp import foo - return foo(x) -[file testmodule.py] -def factorial(x: int) -> int: - if x == 0: - return 1 - else: - return x * factorial(x-1) -[file welp.py] -def foo(x: int) -> int: - return x -[file driver.py] -from native import f, g -print(f(5)) -print(g(5)) -[out] -120 -5 - -[case testBuiltins] -y = 10 -def f(x: int) -> None: - print(5) - d = globals() - assert d['y'] == 10 - d['y'] = 20 - assert y == 20 -[file driver.py] -from native import f -f(5) -[out] -5 - -[case testImportMissing] -# The unchecked module is configured by the test harness to not be -# picked up by mypy, so we can test that we do that right thing when -# calling library modules without stubs. -import unchecked # type: ignore -import unchecked as lol # type: ignore -assert unchecked.x == 10 -assert lol.x == 10 -[file unchecked.py] -x = 10 - -[file driver.py] -import native - -[case testOptional] -from typing import Optional - -class A: pass - -def f(x: Optional[A]) -> Optional[A]: - return x - -def g(x: Optional[A]) -> int: - if x is None: - return 1 - if x is not None: - return 2 - return 3 - -def h(x: Optional[int], y: Optional[bool]) -> None: - pass - -[file driver.py] -from native import f, g, A -a = A() -assert f(None) is None -assert f(a) is a -assert g(None) == 1 -assert g(a) == 2 - -[case testFromImport] -from testmodule import g - -def f(x: int) -> int: - return g(x) -[file testmodule.py] -def g(x: int) -> int: - return x + 1 -[file driver.py] -from native import f -assert f(1) == 2 - -[case testSets] -from typing import Set, List -def instantiateLiteral() -> Set[int]: - return {1, 2, 3, 5, 8} - -def fromIterator() -> List[Set[int]]: - x = set([1, 3, 5]) - y = set((1, 3, 5)) - z = set({1: '1', 3: '3', 5: '5'}) - return [x, y, z] - -def addIncrementing(s : Set[int]) -> None: - for a in [1, 2, 3]: - if a not in s: - s.add(a) - return - -def replaceWith1(s : Set[int]) -> None: - s.clear() - s.add(1) - -def remove1(s : Set[int]) -> None: - s.remove(1) - -def discard1(s: Set[int]) -> None: - s.discard(1) - -def pop(s : Set[int]) -> int: - return s.pop() - -def update(s: Set[int], x: List[int]) -> None: - s.update(x) - -[file driver.py] -from native import instantiateLiteral -from testutil import assertRaises - -val = instantiateLiteral() -assert 1 in val -assert 2 in val -assert 3 in val -assert 5 in val -assert 8 in val -assert len(val) == 5 -assert val == {1, 2, 3, 5, 8} -s = 0 -for i in val: - s += i -assert s == 19 - -from native import fromIterator -sets = fromIterator() -for s in sets: - assert s == {1, 3, 5} - -from native import addIncrementing -s = set() -addIncrementing(s) -assert s == {1} -addIncrementing(s) -assert s == {1, 2} -addIncrementing(s) -assert s == {1, 2, 3} - -from native import replaceWith1 -s = {3, 7, 12} -replaceWith1(s) -assert s == {1} - -from native import remove1 -import traceback -s = {1, 4, 6} -remove1(s) -assert s == {4, 6} -with assertRaises(KeyError, '1'): - remove1(s) - -from native import discard1 -s = {1, 4, 6} -discard1(s) -assert s == {4, 6} -discard1(s) -assert s == {4, 6} - -from native import pop -s = {1, 2, 3} -x = pop(s) -assert len(s) == 2 -assert x in [1, 2, 3] -y = pop(s) -assert len(s) == 1 -assert y in [1, 2, 3] -assert x != y -z = pop(s) -assert len(s) == 0 -assert z in [1, 2, 3] -assert x != z -assert y != z -with assertRaises(KeyError, 'pop from an empty set'): - pop(s) - -from native import update -s = {1, 2, 3} -update(s, [5, 4, 3]) -assert s == {1, 2, 3, 4, 5} - -[case testDictStuff] -from typing import Dict, Any, List, Set, Tuple -from defaultdictwrap import make_dict - -def f(x: int) -> int: - dict1 = {} # type: Dict[int, int] - dict1[1] = 1 - dict2 = {} # type: Dict[int, int] - dict2[x] = 2 - dict1.update(dict2) - - l = [(5, 2)] # type: Any - dict1.update(l) - d2 = {6: 4} # type: Any - dict1.update(d2) - - return dict1[1] - -def g() -> int: - d = make_dict() - d['a'] = 10 - d['a'] += 10 - d['b'] += 10 - l = [('c', 2)] # type: Any - d.update(l) - d2 = {'d': 4} # type: Any - d.update(d2) - return d['a'] + d['b'] - -def h() -> None: - d = {} # type: Dict[Any, Any] - d[{}] - -def update_dict(x: Dict[Any, Any], y: Any): - x.update(y) - -def make_dict1(x: Any) -> Dict[Any, Any]: - return dict(x) - -def make_dict2(x: Dict[Any, Any]) -> Dict[Any, Any]: - return dict(x) - -def u(x: int) -> int: - d = {} # type: Dict[str, int] - d.update(x=x) - return d['x'] - -def get_content(d: Dict[int, int]) -> Tuple[List[int], List[int], List[Tuple[int, int]]]: - return list(d.keys()), list(d.values()), list(d.items()) - -def get_content_set(d: Dict[int, int]) -> Tuple[Set[int], Set[int], Set[Tuple[int, int]]]: - return set(d.keys()), set(d.values()), set(d.items()) -[file defaultdictwrap.py] -from typing import Dict -from collections import defaultdict # type: ignore -def make_dict() -> Dict[str, int]: - return defaultdict(int) - -[file driver.py] -from collections import OrderedDict -from native import ( - f, g, h, u, make_dict1, make_dict2, update_dict, get_content, get_content_set -) -assert f(1) == 2 -assert f(2) == 1 -assert g() == 30 -# Make sure we get a TypeError from indexing with unhashable and not KeyError -try: - h() -except TypeError: - pass -else: - assert False -d = {'a': 1, 'b': 2} -assert make_dict1(d) == d -assert make_dict1(d.items()) == d -assert make_dict2(d) == d -# object.__dict__ is a "mappingproxy" and not a dict -assert make_dict1(object.__dict__) == dict(object.__dict__) -d = {} -update_dict(d, object.__dict__) -assert d == dict(object.__dict__) - -assert u(10) == 10 -assert get_content({1: 2}) == ([1], [2], [(1, 2)]) -od = OrderedDict([(1, 2), (3, 4)]) -assert get_content(od) == ([1, 3], [2, 4], [(1, 2), (3, 4)]) -od.move_to_end(1) -assert get_content(od) == ([3, 1], [4, 2], [(3, 4), (1, 2)]) -assert get_content_set({1: 2}) == ({1}, {2}, {(1, 2)}) -assert get_content_set(od) == ({1, 3}, {2, 4}, {(1, 2), (3, 4)}) -[typing fixtures/typing-full.pyi] - -[case testDictIterationMethodsRun] -from typing import Dict -def print_dict_methods(d1: Dict[int, int], - d2: Dict[int, int], - d3: Dict[int, int]) -> None: - for k in d1.keys(): - print(k) - for k, v in d2.items(): - print(k) - print(v) - for v in d3.values(): - print(v) - -def clear_during_iter(d: Dict[int, int]) -> None: - for k in d: - d.clear() - -class Custom(Dict[int, int]): pass -[file driver.py] -from native import print_dict_methods, Custom, clear_during_iter -from collections import OrderedDict -print_dict_methods({}, {}, {}) -print_dict_methods({1: 2}, {3: 4, 5: 6}, {7: 8}) -print('==') -c = Custom({0: 1}) -print_dict_methods(c, c, c) -print('==') -d = OrderedDict([(1, 2), (3, 4)]) -print_dict_methods(d, d, d) -print('==') -d.move_to_end(1) -print_dict_methods(d, d, d) -clear_during_iter({}) # OK -try: - clear_during_iter({1: 2, 3: 4}) -except RuntimeError as e: - assert str(e) == "dictionary changed size during iteration" -else: - assert False -try: - clear_during_iter(d) -except RuntimeError as e: - assert str(e) == "OrderedDict changed size during iteration" -else: - assert False - -class CustomMad(dict): - def __iter__(self): - return self - def __next__(self): - raise ValueError -m = CustomMad() -try: - clear_during_iter(m) -except ValueError: - pass -else: - assert False - -class CustomBad(dict): - def items(self): - return [(1, 2, 3)] # Oops -b = CustomBad() -try: - print_dict_methods(b, b, b) -except TypeError as e: - assert str(e) == "a tuple of length 2 expected" -else: - assert False -[out] -1 -3 -4 -5 -6 -8 -== -0 -0 -1 -1 -== -1 -3 -1 -2 -3 -4 -2 -4 -== -3 -1 -3 -4 -1 -2 -4 -2 - -[case testPyMethodCall] -from typing import List -def f(x: List[int]) -> int: - return x.pop() -def g(x: List[int], y: List[int]) -> None: - x.extend(y) -[file driver.py] -from native import f, g -l = [1, 2] -assert f(l) == 2 -g(l, [10]) -assert l == [1, 10] -assert f(l) == 10 -assert f(l) == 1 -g(l, [11, 12]) -assert l == [11, 12] - -[case testMethodCallWithKeywordArgs] -from typing import Tuple -import testmodule -class A: - def echo(self, a: int, b: int, c: int) -> Tuple[int, int, int]: - return a, b, c -def test_native_method_call_with_kwargs() -> None: - a = A() - assert a.echo(1, c=3, b=2) == (1, 2, 3) - assert a.echo(c = 3, a = 1, b = 2) == (1, 2, 3) -def test_module_method_call_with_kwargs() -> None: - a = testmodule.A() - assert a.echo(1, c=3, b=2) == (1, 2, 3) - assert a.echo(c = 3, a = 1, b = 2) == (1, 2, 3) -[file testmodule.py] -from typing import Tuple -class A: - def echo(self, a: int, b: int, c: int) -> Tuple[int, int, int]: - return a, b, c -[file driver.py] -import native -native.test_native_method_call_with_kwargs() -native.test_module_method_call_with_kwargs() - -[case testException] -from typing import List -def f(x: List[int]) -> None: - g(x) - -def g(x: List[int]) -> bool: - x[5] = 2 - return True - -def r1() -> None: - q1() - -def q1() -> None: - raise Exception("test") - -def r2() -> None: - q2() - -def q2() -> None: - raise Exception - -class A: - def __init__(self) -> None: - raise Exception - -def hey() -> None: - A() - -[file driver.py] -from native import f, r1, r2, hey -import traceback -try: - f([]) -except IndexError: - traceback.print_exc() -try: - r1() -except Exception: - traceback.print_exc() -try: - r2() -except Exception: - traceback.print_exc() -try: - hey() -except Exception: - traceback.print_exc() -[out] -Traceback (most recent call last): - File "driver.py", line 4, in - f([]) - File "native.py", line 3, in f - g(x) - File "native.py", line 6, in g - x[5] = 2 -IndexError: list assignment index out of range -Traceback (most recent call last): - File "driver.py", line 8, in - r1() - File "native.py", line 10, in r1 - q1() - File "native.py", line 13, in q1 - raise Exception("test") -Exception: test -Traceback (most recent call last): - File "driver.py", line 12, in - r2() - File "native.py", line 16, in r2 - q2() - File "native.py", line 19, in q2 - raise Exception -Exception -Traceback (most recent call last): - File "driver.py", line 16, in - hey() - File "native.py", line 26, in hey - A() - File "native.py", line 23, in __init__ - raise Exception -Exception - -[case testTryExcept] -from typing import Any, Iterator -import wrapsys -def g(b: bool) -> None: - try: - if b: - x = [0] - x[1] - else: - raise Exception('hi') - except: - print("caught!") - -def r(x: int) -> None: - if x == 0: - [0][1] - elif x == 1: - raise Exception('hi') - elif x == 2: - {1: 1}[0] - elif x == 3: - a = object() # type: Any - a.lol - -def f(b: bool) -> None: - try: - r(int(b)) - except AttributeError: - print('no') - except: - print(str(wrapsys.exc_info()[1])) - print(str(wrapsys.exc_info()[1])) - -def h() -> None: - while True: - try: - raise Exception('gonna break') - except: - print(str(wrapsys.exc_info()[1])) - break - print(str(wrapsys.exc_info()[1])) - -def i() -> None: - try: - r(0) - except: - print(type(wrapsys.exc_info()[1])) - raise - -def j(n: int) -> None: - try: - r(n) - except (IndexError, KeyError): - print("lookup!") - except AttributeError as e: - print("attr! --", e) - -def k() -> None: - try: - r(1) - except: - r(0) - -def l() -> None: - try: - r(0) - except IndexError: - try: - r(2) - except KeyError as e: - print("key! --", e) - -def m(x: object) -> int: - try: - st = id(x) - except Exception: - return -1 - return st + 1 - -def iter_exception() -> Iterator[str]: - try: - r(0) - except KeyError as e: - yield 'lol' - -[file wrapsys.py] -# This is a gross hack around some limitations of the test system/mypyc. -from typing import Any -import sys -def exc_info() -> Any: - return sys.exc_info() # type: ignore - -[file driver.py] -import sys, traceback -from native import g, f, h, i, j, k, l, m, iter_exception -from testutil import assertRaises -print("== i ==") -try: - i() -except: - traceback.print_exc(file=sys.stdout) - -print("== k ==") -try: - k() -except: - traceback.print_exc(file=sys.stdout) - -print("== g ==") -g(True) -g(False) - -print("== f ==") -f(True) -f(False) - -print("== h ==") -h() - -print("== j ==") -j(0) -j(2) -j(3) -try: - j(1) -except: - print("out!") - -print("== l ==") -l() - -m('lol') - -with assertRaises(IndexError): - list(iter_exception()) - -[out] -== i == - -Traceback (most recent call last): - File "driver.py", line 6, in - i() - File "native.py", line 44, in i - r(0) - File "native.py", line 15, in r - [0][1] -IndexError: list index out of range -== k == -Traceback (most recent call last): - File "native.py", line 59, in k - r(1) - File "native.py", line 17, in r - raise Exception('hi') -Exception: hi - -During handling of the above exception, another exception occurred: - -Traceback (most recent call last): - File "driver.py", line 12, in - k() - File "native.py", line 61, in k - r(0) - File "native.py", line 15, in r - [0][1] -IndexError: list index out of range -== g == -caught! -caught! -== f == -hi -None -list index out of range -None -== h == -gonna break -None -== j == -lookup! -lookup! -attr! -- 'object' object has no attribute 'lol' -out! -== l == -key! -- 0 - -[case testTryFinally] -from typing import Any -import wrapsys - -def a(b1: bool, b2: int) -> None: - try: - if b1: - raise Exception('hi') - finally: - print('finally:', str(wrapsys.exc_info()[1])) - if b2 == 2: - return - if b2 == 1: - raise Exception('again!') - -def b(b1: int, b2: int) -> str: - try: - if b1 == 1: - raise Exception('hi') - elif b1 == 2: - [0][1] - elif b1 == 3: - return 'try' - except IndexError: - print('except') - finally: - print('finally:', str(wrapsys.exc_info()[1])) - if b2 == 2: - return 'finally' - if b2 == 1: - raise Exception('again!') - return 'outer' - -def c() -> str: - try: - try: - return 'wee' - finally: - print("out a") - finally: - print("out b") - - -[file wrapsys.py] -# This is a gross hack around some limitations of the test system/mypyc. -from typing import Any -import sys -def exc_info() -> Any: - return sys.exc_info() # type: ignore - -[file driver.py] -import traceback -import sys -from native import a, b, c - -def run(f): - try: - x = f() - if x: - print("returned:", x) - except Exception as e: - print("caught:", type(e).__name__ + ": " + str(e)) - -print("== a ==") -for i in range(3): - for b1 in [False, True]: - run(lambda: a(b1, i)) - -print("== b ==") -for i in range(4): - for j in range(3): - run(lambda: b(i, j)) - -print("== b ==") -print(c()) - -[out] -== a == -finally: None -finally: hi -caught: Exception: hi -finally: None -caught: Exception: again! -finally: hi -caught: Exception: again! -finally: None -finally: hi -== b == -finally: None -returned: outer -finally: None -caught: Exception: again! -finally: None -returned: finally -finally: hi -caught: Exception: hi -finally: hi -caught: Exception: again! -finally: hi -returned: finally -except -finally: None -returned: outer -except -finally: None -caught: Exception: again! -except -finally: None -returned: finally -finally: None -returned: try -finally: None -caught: Exception: again! -finally: None -returned: finally -== b == -out a -out b -wee - -[case testCustomException] -from typing import List - -class ListOutOfBounds(IndexError): - pass - -class UserListWarning(UserWarning): - pass - -def f(l: List[int], k: int) -> int: - try: - return l[k] - except IndexError: - raise ListOutOfBounds("Ruh-roh from f!") - -def g(l: List[int], k: int) -> int: - try: - return f([1,2,3], 3) - except ListOutOfBounds: - raise ListOutOfBounds("Ruh-roh from g!") - -def k(l: List[int], k: int) -> int: - try: - return g([1,2,3], 3) - except IndexError: - raise UserListWarning("Ruh-roh from k!") - -def h() -> int: - try: - return k([1,2,3], 3) - except UserWarning: - return -1 - -[file driver.py] -from native import h -assert h() == -1 - -[case testWith] -from typing import Any -class Thing: - def __init__(self, x: str) -> None: - self.x = x - def __enter__(self) -> str: - print('enter!', self.x) - if self.x == 'crash': - raise Exception('ohno') - return self.x - def __exit__(self, x: Any, y: Any, z: Any) -> None: - print('exit!', self.x, y) - -def foo(i: int) -> int: - with Thing('a') as x: - print("yooo?", x) - if i == 0: - return 10 - elif i == 1: - raise Exception('exception!') - return -1 - -def bar() -> None: - with Thing('a') as x, Thing('b') as y: - print("yooo?", x, y) - -def baz() -> None: - with Thing('a') as x, Thing('crash') as y: - print("yooo?", x, y) - -[file driver.py] -from native import foo, bar, baz -assert foo(0) == 10 -print('== foo ==') -try: - foo(1) -except Exception: - print('caught') -assert foo(2) == -1 - -print('== bar ==') -bar() - -print('== baz ==') -try: - baz() -except Exception: - print('caught') - -[out] -enter! a -yooo? a -exit! a None -== foo == -enter! a -yooo? a -exit! a exception! -caught -enter! a -yooo? a -exit! a None -== bar == -enter! a -enter! b -yooo? a b -exit! b None -exit! a None -== baz == -enter! a -enter! crash -exit! a ohno -caught - -[case testGenericEquality] -def eq(a: object, b: object) -> bool: - if a == b: - return True - else: - return False -def ne(a: object, b: object) -> bool: - if a != b: - return True - else: - return False -def f(o: object) -> bool: - if [1, 2] == o: - return True - else: - return False -[file driver.py] -from native import eq, ne, f -assert eq('xz', 'x' + 'z') -assert not eq('x', 'y') -assert not ne('xz', 'x' + 'z') -assert ne('x', 'y') -assert f([1, 2]) -assert not f([2, 2]) -assert not f(1) - -[case testGenericBinaryOps] -from typing import Any -def add(x: Any, y: Any) -> Any: - return x + y -def subtract(x: Any, y: Any) -> Any: - return x - y -def multiply(x: Any, y: Any) -> Any: - return x * y -def floor_div(x: Any, y: Any) -> Any: - return x // y -def true_div(x: Any, y: Any) -> Any: - return x / y -def remainder(x: Any, y: Any) -> Any: - return x % y -def power(x: Any, y: Any) -> Any: - return x ** y -def lshift(x: Any, y: Any) -> Any: - return x << y -def rshift(x: Any, y: Any) -> Any: - return x >> y -def num_and(x: Any, y: Any) -> Any: - return x & y -def num_xor(x: Any, y: Any) -> Any: - return x ^ y -def num_or(x: Any, y: Any) -> Any: - return x | y -def lt(x: Any, y: Any) -> Any: - if x < y: - return True - else: - return False -def le(x: Any, y: Any) -> Any: - if x <= y: - return True - else: - return False -def gt(x: Any, y: Any) -> Any: - if x > y: - return True - else: - return False -def ge(x: Any, y: Any) -> Any: - if x >= y: - return True - else: - return False -def contains(x: Any, y: Any) -> Any: - if x in y: - return True - else: - return False -def identity(x: Any, y: Any) -> Any: - if x is y: - return True - else: - return False -def disidentity(x: Any, y: Any) -> Any: - if x is not y: - return True - else: - return False -def not_eq_cond(a: Any, b: Any) -> bool: - if not (a == b): - return True - else: - return False -def eq2(a: Any, b: Any) -> bool: - return a == b -def slice1(x: Any) -> Any: - return x[:] -def slice2(x: Any, y: Any) -> Any: - return x[y:] -def slice3(x: Any, y: Any) -> Any: - return x[:y] -def slice4(x: Any, y: Any, z: Any) -> Any: - return x[y:z] -def slice5(x: Any, y: Any, z: Any, zz: Any) -> Any: - return x[y:z:zz] -[file driver.py] -from native import * -assert add(5, 6) == 11 -assert add('x', 'y') == 'xy' -assert subtract(8, 3) == 5 -assert multiply(8, 3) == 24 -assert floor_div(8, 3) == 2 -assert true_div(7, 2) == 3.5 -assert remainder(11, 4) == 3 -assert remainder('%.3d', 5) == '005' -assert remainder('%d-%s', (5, 'xy')) == '5-xy' -assert power(3, 4) == 81 -assert lshift(5, 3) == 40 -assert rshift(41, 3) == 5 -assert num_and(99, 56) == 32 -assert num_xor(99, 56) == 91 -assert num_or(99, 56) == 123 -assert lt('a', 'b') -assert not lt('a', 'a') -assert not lt('b', 'a') -assert not gt('a', 'b') -assert not gt('a', 'a') -assert gt('b', 'a') -assert le('a', 'b') -assert le('a', 'a') -assert not le('b', 'a') -assert not ge('a', 'b') -assert ge('a', 'a') -assert ge('b', 'a') -assert contains('x', 'axb') -assert not contains('X', 'axb') -assert contains('x', {'x', 'y'}) -a = [1, 3, 5] -assert slice1(a) == a -assert slice1(a) is not a -assert slice2(a, 1) == [3, 5] -assert slice3(a, -1) == [1, 3] -assert slice4(a, 1, -1) == [3] -assert slice5(a, 2, 0, -1) == [5, 3] -o1, o2 = object(), object() -assert identity(o1, o1) -assert not identity(o1, o2) -assert not disidentity(o1, o1) -assert disidentity(o1, o2) -assert eq2('xz', 'x' + 'z') -assert not eq2('x', 'y') -assert not not_eq_cond('xz', 'x' + 'z') -assert not_eq_cond('x', 'y') - -[case testGenericMiscOps] -from typing import Any -def neg(x: Any) -> Any: - return -x -def pos(x: Any) -> Any: - return +x -def invert(x: Any) -> Any: - return ~x -def get_item(o: Any, k: Any) -> Any: - return o[k] -def set_item(o: Any, k: Any, v: Any) -> Any: - o[k] = v -[file driver.py] -from native import * -assert neg(6) == -6 -assert pos(6) == 6 -assert invert(6) == -7 -d = {'x': 5} -assert get_item(d, 'x') == 5 -set_item(d, 'y', 6) -assert d['y'] == 6 - -[case testIntMathOps] -# This tests integer math things that are either easier to test in Python than -# in our C tests or are tested here because (for annoying reasons) we don't run -# the C unit tests in our 32-bit CI. -def multiply(x: int, y: int) -> int: - return x * y - -# these stringify their outputs because that will catch if exceptions are mishandled -def floor_div(x: int, y: int) -> str: - return str(x // y) -def remainder(x: int, y: int) -> str: - return str(x % y) - -[file driver.py] -from native import multiply, floor_div, remainder - -def test_multiply(x, y): - assert multiply(x, y) == x * y -def test_floor_div(x, y): - assert floor_div(x, y) == str(x // y) -def test_remainder(x, y): - assert remainder(x, y) == str(x % y) - -test_multiply(10**6, 10**6) -test_multiply(2**15, 2**15-1) -test_multiply(2**14, 2**14) - -test_multiply(10**12, 10**12) -test_multiply(2**30, 2**30-1) -test_multiply(2**29, 2**29) - -test_floor_div(-2**62, -1) -test_floor_div(-2**30, -1) -try: - floor_div(10, 0) -except ZeroDivisionError: - pass -else: - assert False, "Expected ZeroDivisionError" - -test_remainder(-2**62, -1) -test_remainder(-2**30, -1) -try: - remainder(10, 0) -except ZeroDivisionError: - pass -else: - assert False, "Expected ZeroDivisionError" - -[case testSubclassAttributeAccess] -from mypy_extensions import trait - -class A: - v = 0 - -class B(A): - v = 1 - -class C(B): - v = 2 - -[file driver.py] -from native import A, B, C - -a = A() -b = B() -c = C() - -[case testAnyAttributeAndMethodAccess] -from typing import Any, List -class C: - a: int - def m(self, x: int, a: List[int]) -> int: - return self.a + x + a[0] -def get_a(x: Any) -> Any: - return x.a -def set_a(x: Any, y: Any) -> None: - x.a = y -def call_m(x: Any) -> Any: - return x.m(1, [3]) -[file driver.py] -from native import C, get_a, set_a, call_m -class D: - def m(self, x, a): - return self.a + x + a[0] - -c = C() -c.a = 6 -d = D() -d.a = 2 -assert get_a(c) == 6 -assert get_a(d) == 2 -assert call_m(c) == 10 -assert call_m(d) == 6 -set_a(c, 5) -assert c.a == 5 -set_a(d, 4) -assert d.a == 4 -try: - get_a(object()) -except AttributeError: - pass -else: - assert False -try: - call_m(object()) -except AttributeError: - pass -else: - assert False -try: - set_a(object(), 5) -except AttributeError: - pass -else: - assert False - -[case testAnyCall] -from typing import Any -def call(f: Any) -> Any: - return f(1, 'x') -[file driver.py] -from native import call -def f(x, y): - return (x, y) -def g(x): pass - -assert call(f) == (1, 'x') -for bad in g, 1: - try: - call(bad) - except TypeError: - pass - else: - assert False, bad - -[case testFloat] -def assign_and_return_float_sum() -> float: - f1 = 1.0 - f2 = 2.0 - f3 = 3.0 - return f1 * f2 + f3 - -def from_int(i: int) -> float: - return float(i) - -def to_int(x: float) -> int: - return int(x) - -def get_complex() -> complex: - return 5.0j + 3.0 - -[file driver.py] -from native import assign_and_return_float_sum, from_int, to_int, get_complex -sum = 0.0 -for i in range(10): - sum += assign_and_return_float_sum() -assert sum == 50.0 - -assert str(from_int(10)) == '10.0' -assert str(to_int(3.14)) == '3' -assert str(to_int(3)) == '3' -assert get_complex() == 3+5j - -[case testBytes] -def f(x: bytes) -> bytes: - return x - -def concat(a: bytes, b: bytes) -> bytes: - return a + b - -def eq(a: bytes, b: bytes) -> bool: - return a == b - -def neq(a: bytes, b: bytes) -> bool: - return a != b - -def join() -> bytes: - seq = (b'1', b'"', b'\xf0') - return b'\x07'.join(seq) -[file driver.py] -from native import f, concat, eq, neq, join -assert f(b'123') == b'123' -assert f(b'\x07 \x0b " \t \x7f \xf0') == b'\x07 \x0b " \t \x7f \xf0' -assert concat(b'123', b'456') == b'123456' -assert eq(b'123', b'123') -assert not eq(b'123', b'1234') -assert neq(b'123', b'1234') -assert join() == b'1\x07"\x07\xf0' - -[case testBigIntLiteral] -def big_int() -> None: - a_62_bit = 4611686018427387902 - max_62_bit = 4611686018427387903 - b_63_bit = 4611686018427387904 - c_63_bit = 9223372036854775806 - max_63_bit = 9223372036854775807 - d_64_bit = 9223372036854775808 - max_32_bit = 2147483647 - max_31_bit = 1073741823 - print(a_62_bit) - print(max_62_bit) - print(b_63_bit) - print(c_63_bit) - print(max_63_bit) - print(d_64_bit) - print(max_32_bit) - print(max_31_bit) -[file driver.py] -from native import big_int -big_int() -[out] -4611686018427387902 -4611686018427387903 -4611686018427387904 -9223372036854775806 -9223372036854775807 -9223372036854775808 -2147483647 -1073741823 - -[case testForIterable] -from typing import Iterable, Dict, Any, Tuple -def iterate_over_any(a: Any) -> None: - for element in a: - print(element) - -def iterate_over_iterable(iterable: Iterable[T]) -> None: - for element in iterable: - print(element) - -def iterate_and_delete(d: Dict[int, int]) -> None: - for key in d: - d.pop(key) - -def sum_over_values(d: Dict[int, int]) -> int: - s = 0 - for key in d: - s = s + d[key] - return s - -def sum_over_even_values(d: Dict[int, int]) -> int: - s = 0 - for key in d: - if d[key] % 2: - continue - s = s + d[key] - return s - -def sum_over_two_values(d: Dict[int, int]) -> int: - s = 0 - i = 0 - for key in d: - if i == 2: - break - s = s + d[key] - i = i + 1 - return s - -def iterate_over_tuple(iterable: Tuple[int, int, int]) -> None: - for element in iterable: - print(element) - -[file driver.py] -from native import iterate_over_any, iterate_over_iterable, iterate_and_delete, sum_over_values, sum_over_even_values, sum_over_two_values, iterate_over_tuple -import traceback -def broken_generator(n): - num = 0 - while num < n: - yield num - num += 1 - raise Exception('Exception Manually Raised') - -d = {1:1, 2:2, 3:3, 4:4, 5:5} -print(sum_over_values(d)) -print(sum_over_even_values(d)) -print(sum_over_two_values(d)) - -try: - iterate_over_any(5) -except TypeError: - traceback.print_exc() -try: - iterate_over_iterable(broken_generator(5)) -except Exception: - traceback.print_exc() -try: - iterate_and_delete(d) -except RuntimeError: - traceback.print_exc() - -iterate_over_tuple((1, 2, 3)) -[out] -Traceback (most recent call last): - File "driver.py", line 16, in - iterate_over_any(5) - File "native.py", line 3, in iterate_over_any - for element in a: -TypeError: 'int' object is not iterable -Traceback (most recent call last): - File "driver.py", line 20, in - iterate_over_iterable(broken_generator(5)) - File "native.py", line 7, in iterate_over_iterable - for element in iterable: - File "driver.py", line 8, in broken_generator - raise Exception('Exception Manually Raised') -Exception: Exception Manually Raised -Traceback (most recent call last): - File "driver.py", line 24, in - iterate_and_delete(d) - File "native.py", line 11, in iterate_and_delete - for key in d: -RuntimeError: dictionary changed size during iteration -15 -6 -3 -0 -1 -2 -3 -4 -1 -2 -3 - -[case testNeg] -def neg(x: int) -> int: - return -x -[file driver.py] -from native import neg -assert neg(5) == -5 -assert neg(-5) == 5 -assert neg(1073741823) == -1073741823 -assert neg(-1073741823) == 1073741823 -assert neg(1073741824) == -1073741824 -assert neg(-1073741824) == 1073741824 -assert neg(2147483647) == -2147483647 -assert neg(-2147483647) == 2147483647 -assert neg(2147483648) == -2147483648 -assert neg(-2147483648) == 2147483648 -assert neg(4611686018427387904) == -4611686018427387904 -assert neg(-4611686018427387904) == 4611686018427387904 -assert neg(9223372036854775807) == -9223372036854775807 -assert neg(-9223372036854775807) == 9223372036854775807 -assert neg(9223372036854775808) == -9223372036854775808 -assert neg(-9223372036854775808) == 9223372036854775808 - -[case testContinueFor] -def f() -> None: - for n in range(5): - continue -[file driver.py] -from native import f -f() - -[case testDisplays] -from typing import List, Set, Tuple, Sequence, Dict, Any - -def listDisplay(x: List[int], y: List[int]) -> List[int]: - return [1, 2, *x, *y, 3] - -def setDisplay(x: Set[int], y: Set[int]) -> Set[int]: - return {1, 2, *x, *y, 3} - -def tupleDisplay(x: Sequence[str], y: Sequence[str]) -> Tuple[str, ...]: - return ('1', '2', *x, *y, '3') - -def dictDisplay(x: str, y1: Dict[str, int], y2: Dict[str, int]) -> Dict[str, int]: - return {x: 2, **y1, 'z': 3, **y2} - -[file driver.py] -from native import listDisplay, setDisplay, tupleDisplay, dictDisplay -assert listDisplay([4], [5, 6]) == [1, 2, 4, 5, 6, 3] -assert setDisplay({4}, {5}) == {1, 2, 3, 4, 5} -assert tupleDisplay(['4', '5'], ['6']) == ('1', '2', '4', '5', '6', '3') -assert dictDisplay('x', {'y1': 1}, {'y2': 2, 'z': 5}) == {'x': 2, 'y1': 1, 'y2': 2, 'z': 5} - -[case testCallableTypes] -from typing import Callable -def absolute_value(x: int) -> int: - return x if x > 0 else -x - -def call_native_function(x: int) -> int: - return absolute_value(x) - -def call_python_function(x: int) -> int: - return int(x) - -def return_float() -> float: - return 5.0 - -def return_callable_type() -> Callable[[], float]: - return return_float - -def call_callable_type() -> float: - f = return_callable_type() - return f() - -def return_passed_in_callable_type(f: Callable[[], float]) -> Callable[[], float]: - return f - -def call_passed_in_callable_type(f: Callable[[], float]) -> float: - return f() - -[file driver.py] -from native import call_native_function, call_python_function, return_float, return_callable_type, call_callable_type, return_passed_in_callable_type, call_passed_in_callable_type -a = call_native_function(1) -b = call_python_function(1) -c = return_callable_type() -d = call_callable_type() -e = return_passed_in_callable_type(return_float) -f = call_passed_in_callable_type(return_float) -assert a == 1 -assert b == 1 -assert c() == 5.0 -assert d == 5.0 -assert e() == 5.0 -assert f == 5.0 - -[case testKeywordArgs] -from typing import Tuple -import testmodule - -def g(a: int, b: int, c: int) -> Tuple[int, int, int]: - return a, b, c - -def test_call_native_function_with_keyword_args() -> None: - assert g(1, c = 3, b = 2) == (1, 2, 3) - assert g(c = 3, a = 1, b = 2) == (1, 2, 3) - -def test_call_module_function_with_keyword_args() -> None: - assert testmodule.g(1, c = 3, b = 2) == (1, 2, 3) - assert testmodule.g(c = 3, a = 1, b = 2) == (1, 2, 3) - -def test_call_python_function_with_keyword_args() -> None: - assert int("11", base=2) == 3 - -def test_call_lambda_function_with_keyword_args() -> None: - g = testmodule.get_lambda_function() - assert g(1, c = 3, b = 2) == (1, 2, 3) - assert g(c = 3, a = 1, b = 2) == (1, 2, 3) - -[file testmodule.py] -from typing import Tuple - -def g(a: int, b: int, c: int) -> Tuple[int, int, int]: - return a, b, c - -def get_lambda_function(): - return (lambda a, b, c: (a, b, c)) - -[file driver.py] -import native -native.test_call_native_function_with_keyword_args() -native.test_call_module_function_with_keyword_args() -native.test_call_python_function_with_keyword_args() -native.test_call_lambda_function_with_keyword_args() - -[case testStarArgs] -from typing import Tuple - -def g(a: int, b: int, c: int) -> Tuple[int, int, int]: - return a, b, c - -def test_star_args() -> None: - assert g(*[1, 2, 3]) == (1, 2, 3) - assert g(*(1, 2, 3)) == (1, 2, 3) - assert g(*(1,), *[2, 3]) == (1, 2, 3) - assert g(*(), *(1,), *(), *(2,), *(3,), *()) == (1, 2, 3) - assert g(*range(3)) == (0, 1, 2) - -[file driver.py] -import native -native.test_star_args() - -[case testStar2Args] -from typing import Tuple - -def g(a: int, b: int, c: int) -> Tuple[int, int, int]: - return a, b, c - -def test_star2_args() -> None: - assert g(**{'a': 1, 'b': 2, 'c': 3}) == (1, 2, 3) - assert g(**{'c': 3, 'a': 1, 'b': 2}) == (1, 2, 3) - assert g(b=2, **{'a': 1, 'c': 3}) == (1, 2, 3) - -def test_star2_args_bad(v: dict) -> bool: - return g(a=1, b=2, **v) == (1, 2, 3) -[file driver.py] -import native -native.test_star2_args() - -# this should raise TypeError due to duplicate kwarg, but currently it doesn't -assert native.test_star2_args_bad({'b': 2, 'c': 3}) - -[case testStarAndStar2Args] -from typing import Tuple -def g(a: int, b: int, c: int) -> Tuple[int, int, int]: - return a, b, c - -class C: - def g(self, a: int, b: int, c: int) -> Tuple[int, int, int]: - return a, b, c - -def test_star_and_star2_args() -> None: - assert g(1, *(2,), **{'c': 3}) == (1, 2, 3) - assert g(*[1], **{'b': 2, 'c': 3}) == (1, 2, 3) - c = C() - assert c.g(1, *(2,), **{'c': 3}) == (1, 2, 3) - assert c.g(*[1], **{'b': 2, 'c': 3}) == (1, 2, 3) - -[file driver.py] -import native -native.test_star_and_star2_args() - -[case testAllTheArgCombinations] -from typing import Tuple -def g(a: int, b: int, c: int, d: int = -1) -> Tuple[int, int, int, int]: - return a, b, c, d - -class C: - def g(self, a: int, b: int, c: int, d: int = -1) -> Tuple[int, int, int, int]: - return a, b, c, d - -def test_all_the_arg_combinations() -> None: - assert g(1, *(2,), **{'c': 3}) == (1, 2, 3, -1) - assert g(*[1], **{'b': 2, 'c': 3, 'd': 4}) == (1, 2, 3, 4) - c = C() - assert c.g(1, *(2,), **{'c': 3}) == (1, 2, 3, -1) - assert c.g(*[1], **{'b': 2, 'c': 3, 'd': 4}) == (1, 2, 3, 4) - -[file driver.py] -import native -native.test_all_the_arg_combinations() - -[case testArbitraryLvalues] -from typing import List, Dict, Any - -class O(object): - def __init__(self) -> None: - self.x = 1 - -def increment_attr(a: Any) -> Any: - a.x += 1 - return a - -def increment_attr_o(o: O) -> O: - o.x += 1 - return o - -def increment_all_indices(l: List[int]) -> List[int]: - for i in range(len(l)): - l[i] += 1 - return l - -def increment_all_keys(d: Dict[str, int]) -> Dict[str, int]: - for k in d: - d[k] += 1 - return d - -[file driver.py] -from native import O, increment_attr, increment_attr_o, increment_all_indices, increment_all_keys - -class P(object): - def __init__(self) -> None: - self.x = 0 - -assert increment_attr(P()).x == 1 -assert increment_attr_o(O()).x == 2 -assert increment_all_indices([1, 2, 3]) == [2, 3, 4] -assert increment_all_keys({'a':1, 'b':2, 'c':3}) == {'a':2, 'b':3, 'c':4} - -[case testNestedFunctions] -from typing import Callable - -def outer() -> Callable[[], object]: - def inner() -> object: - return None - return inner - -def first() -> Callable[[], Callable[[], str]]: - def second() -> Callable[[], str]: - def third() -> str: - return 'third: nested function' - return third - return second - -def f1() -> int: - x = 1 - def f2() -> int: - y = 2 - def f3() -> int: - z = 3 - return y - return f3() - return f2() - -def outer_func() -> int: - def inner_func() -> int: - return x - x = 1 - return inner_func() - -def mutual_recursion(start : int) -> int: - def f1(k : int) -> int: - if k <= 0: - return 0 - k -= 1 - return f2(k) - - def f2(k : int) -> int: - if k <= 0: - return 0 - k -= 1 - return f1(k) - return f1(start) - -def topLayer() -> int: - def middleLayer() -> int: - def bottomLayer() -> int: - return x - - return bottomLayer() - - x = 1 - return middleLayer() - -def nest1() -> str: - def nest2() -> str: - def nest3() -> str: - def mut1(val: int) -> str: - if val <= 0: - return "bottomed" - val -= 1 - return mut2(val) - def mut2(val: int) -> str: - if val <= 0: - return "bottomed" - val -= 1 - return mut1(val) - return mut1(start) - return nest3() - start = 3 - return nest2() - -def uno(num: float) -> Callable[[str], str]: - def dos(s: str) -> str: - return s + '!' - return dos - -def eins(num: float) -> str: - def zwei(s: str) -> str: - return s + '?' - a = zwei('eins') - b = zwei('zwei') - return a - -def call_other_inner_func(a: int) -> int: - def foo() -> int: - return a + 1 - - def bar() -> int: - return foo() - - def baz(n: int) -> int: - if n == 0: - return 0 - return n + baz(n - 1) - - return bar() + baz(a) - -def inner() -> str: - return 'inner: normal function' - -def second() -> str: - return 'second: normal function' - -def third() -> str: - return 'third: normal function' - -[file driver.py] -from native import (outer, inner, first, uno, eins, call_other_inner_func, -second, third, f1, outer_func, mutual_recursion, topLayer, nest1) - -assert outer()() == None -assert inner() == 'inner: normal function' -assert first()()() == 'third: nested function' -assert uno(5.0)('uno') == 'uno!' -assert eins(4.0) == 'eins?' -assert call_other_inner_func(5) == 21 -assert second() == 'second: normal function' -assert third() == 'third: normal function' -assert f1() == 2 -assert outer_func() == 1 -assert mutual_recursion(5) == 0 -assert topLayer() == 1 -assert nest1() == "bottomed" - -[case testOverloads] -from typing import overload, Union, Tuple - -@overload -def foo(x: int) -> int: ... - -@overload -def foo(x: str) -> str: ... - -def foo(x: Union[int, str]) -> Union[int, str]: - return x - -class A: - @overload - def foo(self, x: int) -> int: ... - - @overload - def foo(self, x: str) -> str: ... - - def foo(self, x: Union[int, str]) -> Union[int, str]: - return x - -def call1() -> Tuple[int, str]: - return (foo(10), foo('10')) -def call2() -> Tuple[int, str]: - x = A() - return (x.foo(10), x.foo('10')) - -[file driver.py] -from native import * -assert call1() == (10, '10') -assert call2() == (10, '10') - -[case testControlFlowExprs] -from typing import Tuple -def foo() -> object: - print('foo') - return 'foo' -def bar() -> object: - print('bar') - return 'bar' -def t(x: int) -> int: - print(x) - return x - -def f(b: bool) -> Tuple[object, object, object]: - x = foo() if b else bar() - y = b or foo() - z = b and foo() - return (x, y, z) -def g() -> Tuple[object, object]: - return (foo() or bar(), foo() and bar()) - -def nand(p: bool, q: bool) -> bool: - if not (p and q): - return True - return False - -def chained(x: int, y: int, z: int) -> bool: - return t(x) < t(y) > t(z) - -def chained2(x: int, y: int, z: int, w: int) -> bool: - return t(x) < t(y) < t(z) < t(w) -[file driver.py] -from native import f, g, nand, chained, chained2 -assert f(True) == ('foo', True, 'foo') -print() -assert f(False) == ('bar', 'foo', False) -print() -assert g() == ('foo', 'bar') - -assert nand(True, True) == False -assert nand(True, False) == True -assert nand(False, True) == True -assert nand(False, False) == True - -print() -assert chained(10, 20, 15) == True -print() -assert chained(10, 20, 30) == False -print() -assert chained(21, 20, 30) == False -print() -assert chained2(1, 2, 3, 4) == True -print() -assert chained2(1, 0, 3, 4) == False -print() -assert chained2(1, 2, 0, 4) == False -[out] -foo -foo - -bar -foo - -foo -foo -bar - -10 -20 -15 - -10 -20 -30 - -21 -20 - -1 -2 -3 -4 - -1 -0 - -1 -2 -0 - -[case testMultipleAssignment] -from typing import Tuple, List, Any - -def from_tuple(t: Tuple[int, str]) -> List[Any]: - x, y = t - return [y, x] - -def from_list(l: List[int]) -> List[int]: - x, y = l - return [y, x] - -def from_any(o: Any) -> List[Any]: - x, y = o - return [y, x] -[file driver.py] -from native import from_tuple, from_list, from_any - -assert from_tuple((1, 'x')) == ['x', 1] -assert from_list([3, 4]) == [4, 3] -assert from_any('xy') == ['y', 'x'] - -[case testUnpack] -from typing import List - -a, *b = [1, 2, 3, 4, 5] - -*c, d = [1, 2, 3, 4, 5] - -e, *f = [1,2] - -j, *k, l = [1, 2, 3] - -m, *n, o = [1, 2, 3, 4, 5, 6] - -p, q, r, *s, t = [1,2,3,4,5,6,7,8,9,10] - -tup = (1,2,3) -y, *z = tup - -def unpack1(l : List[int]) -> None: - *v1, v2, v3 = l - -def unpack2(l : List[int]) -> None: - v1, *v2, v3 = l - -def unpack3(l : List[int]) -> None: - v1, v2, *v3 = l - -[file driver.py] -from native import a, b, c, d, e, f, j, k, l, m, n, o, p, q, r, s, t, y, z -from native import unpack1, unpack2, unpack3 -from testutil import assertRaises - -assert a == 1 -assert b == [2,3,4,5] -assert c == [1,2,3,4] -assert d == 5 -assert e == 1 -assert f == [2] -assert j == 1 -assert k == [2] -assert l == 3 -assert m == 1 -assert n == [2,3,4,5] -assert o == 6 -assert p == 1 -assert q == 2 -assert r == 3 -assert s == [4,5,6,7,8,9] -assert t == 10 -assert y == 1 -assert z == [2,3] - -with assertRaises(ValueError, "not enough values to unpack"): - unpack1([1]) - -with assertRaises(ValueError, "not enough values to unpack"): - unpack2([1]) - -with assertRaises(ValueError, "not enough values to unpack"): - unpack3([1]) - -[out] - -[case testModuleTopLevel] -x = 1 -print(x) - -def f() -> None: - print(x + 1) - -def g() -> None: - global x - x = 77 - -[file driver.py] -import native -native.f() -native.x = 5 -native.f() -native.g() -print(native.x) - -[out] -1 -2 -6 -77 - -[case testExceptionAtModuleTopLevel] -from typing import Any - -def f(x: int) -> None: pass - -y: Any = '' -f(y) - -[file driver.py] -import traceback -try: - import native -except TypeError: - traceback.print_exc() -else: - assert False - -[out] -Traceback (most recent call last): - File "driver.py", line 3, in - import native - File "native.py", line 6, in - f(y) -TypeError: int object expected; got str - -[case testComprehensions] -# A list comprehension -l = [str(x) + " " + str(y) + " " + str(x*y) for x in range(10) - if x != 6 if x != 5 for y in range(x) if y*x != 8] - -# Test short-circuiting as well -def pred(x: int) -> bool: - if x > 6: - raise Exception() - return x > 3 -# If we fail to short-circuit, pred(x) will be called with x=7 -# eventually and will raise an exception. -l2 = [x for x in range(10) if x <= 6 if pred(x)] - -# A dictionary comprehension -d = {k: k*k for k in range(10) if k != 5 if k != 6} - -# A set comprehension -s = {str(x) + " " + str(y) + " " + str(x*y) for x in range(10) - if x != 6 if x != 5 for y in range(x) if y*x != 8} - -[file driver.py] -from native import l, l2, d, s -for a in l: - print(a) -print(tuple(l2)) -for k in sorted(d): - print(k, d[k]) -for a in sorted(s): - print(a) -[out] -1 0 0 -2 0 0 -2 1 2 -3 0 0 -3 1 3 -3 2 6 -4 0 0 -4 1 4 -4 3 12 -7 0 0 -7 1 7 -7 2 14 -7 3 21 -7 4 28 -7 5 35 -7 6 42 -8 0 0 -8 2 16 -8 3 24 -8 4 32 -8 5 40 -8 6 48 -8 7 56 -9 0 0 -9 1 9 -9 2 18 -9 3 27 -9 4 36 -9 5 45 -9 6 54 -9 7 63 -9 8 72 -(4, 5, 6) -0 0 -1 1 -2 4 -3 9 -4 16 -7 49 -8 64 -9 81 -1 0 0 -2 0 0 -2 1 2 -3 0 0 -3 1 3 -3 2 6 -4 0 0 -4 1 4 -4 3 12 -7 0 0 -7 1 7 -7 2 14 -7 3 21 -7 4 28 -7 5 35 -7 6 42 -8 0 0 -8 2 16 -8 3 24 -8 4 32 -8 5 40 -8 6 48 -8 7 56 -9 0 0 -9 1 9 -9 2 18 -9 3 27 -9 4 36 -9 5 45 -9 6 54 -9 7 63 -9 8 72 - -[case testMultipleVarsWithLoops] -# Test comprehensions and for loops with multiple index variables -l = [(1, 2, 'a'), (3, 4, 'b'), (5, 6, 'c')] -l2 = [str(a*100+b)+' '+c for a, b, c in l] -l3 = [] -for a, b, c in l: - l3.append(str(a*1000+b)+' '+c) -[file driver.py] -from native import l, l2, l3 -for a in l2 + l3: - print(a) -[out] -102 a -304 b -506 c -1002 a -3004 b -5006 c - -[case testDel] -from typing import List -from testutil import assertRaises - -def printDict(dict) -> None: - l = list(dict.keys()) # type: List[str] - l.sort() - for key in l: - print(key, dict[key]) - print("#########") - -def delList() -> None: - l = [1, 2, 3] - print(tuple(l)) - del l[1] - print(tuple(l)) - -def delDict() -> None: - d = {"one":1, "two":2} - printDict(d) - del d["one"] - printDict(d) - -def delListMultiple() -> None: - l = [1, 2, 3, 4, 5, 6, 7] - print(tuple(l)) - del l[1], l[2], l[3] - print(tuple(l)) - -def delDictMultiple() -> None: - d = {"one":1, "two":2, "three":3, "four":4} - printDict(d) - del d["two"], d["four"] - printDict(d) - -class Dummy(): - def __init__(self, x: int, y: int) -> None: - self.x = x - self.y = y - -def delAttribute() -> None: - dummy = Dummy(1, 2) - del dummy.x - with assertRaises(AttributeError): - dummy.x - -def delAttributeMultiple() -> None: - dummy = Dummy(1, 2) - del dummy.x, dummy.y - with assertRaises(AttributeError): - dummy.x - with assertRaises(AttributeError): - dummy.y - -def delLocal(b: bool) -> int: - dummy = 10 - if b: - del dummy - return dummy - -def delLocalLoop() -> None: - # Try deleting a local in a loop to make sure the control flow analysis works - dummy = 1 - for i in range(10): - print(dummy) - dummy *= 2 - if i == 4: - del dummy - -global_var = 10 -del global_var - -[file driver.py] -from native import ( - delList, delDict, delListMultiple, delDictMultiple, delAttribute, - delAttributeMultiple, delLocal, delLocalLoop, -) -import native -from testutil import assertRaises - -delList() -delDict() -delListMultiple() -delDictMultiple() -delAttribute() -delAttributeMultiple() -with assertRaises(AttributeError): - native.global_var -with assertRaises(NameError, "local variable 'dummy' referenced before assignment"): - delLocal(True) -assert delLocal(False) == 10 -with assertRaises(NameError, "local variable 'dummy' referenced before assignment"): - delLocalLoop() -[out] -(1, 2, 3) -(1, 3) -one 1 -two 2 -######### -two 2 -######### -(1, 2, 3, 4, 5, 6, 7) -(1, 3, 5, 7) -four 4 -one 1 -three 3 -two 2 -######### -one 1 -three 3 -######### -1 -2 -4 -8 -16 - -[case testProperty] -from typing import Callable -from mypy_extensions import trait -class Temperature: - @property - def celsius(self) -> float: - return 5.0 * (self.farenheit - 32.0) / 9.0 - - def __init__(self, farenheit: float) -> None: - self.farenheit = farenheit - - def print_temp(self) -> None: - print("F:", self.farenheit, "C:", self.celsius) - - @property - def rankine(self) -> float: - raise NotImplementedError - -class Access: - @property - def number_of_accesses(self) -> int: - self._count += 1 - return self._count - def __init__(self) -> None: - self._count = 0 - -from typing import Callable -class BaseProperty: - @property - def doc(self) -> str: - return "Represents a sequence of values. Updates itself by next, which is a new value." - - @property - def value(self) -> object: - return self._incrementer - - @property - def bad_value(self) -> object: - return self._incrementer - - @property - def next(self) -> BaseProperty: - return BaseProperty(self._incrementer + 1) - - def __init__(self, value: int) -> None: - self._incrementer = value - -class DerivedProperty(BaseProperty): - @property - def value(self) -> int: - return self._incrementer - - @property - def bad_value(self) -> object: - return self._incrementer - - def __init__(self, incr_func: Callable[[int], int], value: int) -> None: - BaseProperty.__init__(self, value) - self._incr_func = incr_func - - @property - def next(self) -> DerivedProperty: - return DerivedProperty(self._incr_func, self._incr_func(self.value)) - -class AgainProperty(DerivedProperty): - @property - def next(self) -> AgainProperty: - return AgainProperty(self._incr_func, self._incr_func(self._incr_func(self.value))) - - @property - def bad_value(self) -> int: - return self._incrementer - -def print_first_n(n: int, thing: BaseProperty) -> None: - vals = [] - cur_thing = thing - for _ in range(n): - vals.append(cur_thing.value) - cur_thing = cur_thing.next - print ('', vals) - -@trait -class Trait: - @property - def value(self) -> int: - return 3 - -class Printer(Trait): - def print_value(self) -> None: - print(self.value) - -[file driver.py] -from native import Temperature, Access -import traceback -x = Temperature(32.0) -try: - print (x.rankine) -except NotImplementedError as e: - traceback.print_exc() -print (x.celsius) -x.print_temp() - -y = Temperature(212.0) -print (y.celsius) -y.print_temp() - -z = Access() -print (z.number_of_accesses) -print (z.number_of_accesses) -print (z.number_of_accesses) -print (z.number_of_accesses) - -from native import BaseProperty, DerivedProperty, AgainProperty, print_first_n -a = BaseProperty(7) -b = DerivedProperty((lambda x: x // 2 if (x % 2 == 0) else 3 * x + 1), 7) -c = AgainProperty((lambda x: x // 2 if (x % 2 == 0) else 3 * x + 1), 7) - -def py_print_first_n(n: int, thing: BaseProperty) -> None: - vals = [] - cur_thing = thing - for _ in range(n): - vals.append(cur_thing.value) - cur_thing = cur_thing.next - print ('', vals) - -py_print_first_n(20, a) -py_print_first_n(20, b) -py_print_first_n(20, c) - -print(a.next.next.next.bad_value) -print(b.next.next.next.bad_value) -print(c.next.next.next.bad_value) - -print_first_n(20, a) -print_first_n(20, b) -print_first_n(20, c) - -print (a.doc) -print (b.doc) -print (c.doc) - -from native import Printer -Printer().print_value() -print (Printer().value) -[out] -Traceback (most recent call last): - File "driver.py", line 5, in - print (x.rankine) - File "native.py", line 16, in rankine - raise NotImplementedError -NotImplementedError -0.0 -F: 32.0 C: 0.0 -100.0 -F: 212.0 C: 100.0 -1 -2 -3 -4 - [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26] - [7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1, 4, 2, 1] - [7, 11, 17, 26, 40, 10, 16, 4, 1, 2, 4, 1, 2, 4, 1, 2, 4, 1, 2, 4] -10 -34 -26 - [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26] - [7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1, 4, 2, 1] - [7, 11, 17, 26, 40, 10, 16, 4, 1, 2, 4, 1, 2, 4, 1, 2, 4, 1, 2, 4] -Represents a sequence of values. Updates itself by next, which is a new value. -Represents a sequence of values. Updates itself by next, which is a new value. -Represents a sequence of values. Updates itself by next, which is a new value. -3 -3 - -[case testPropertySetters] - -from mypy_extensions import trait - -class Foo(): - def __init__(self) -> None: - self.attr = "unmodified" - -class A: - def __init__(self) -> None: - self._x = 0 - self._foo = Foo() - - @property - def x(self) -> int: - return self._x - - @x.setter - def x(self, val : int) -> None: - self._x = val - - @property - def foo(self) -> Foo: - return self._foo - - @foo.setter - def foo(self, val : Foo) -> None: - self._foo = val - -# Overrides base property setters and getters -class B(A): - def __init__(self) -> None: - self._x = 10 - - @property - def x(self) -> int: - return self._x + 1 - - @x.setter - def x(self, val : int) -> None: - self._x = val + 1 - -#Inerits base property setters and getters -class C(A): - def __init__(self) -> None: - A.__init__(self) - -@trait -class D(): - def __init__(self) -> None: - self._x = 0 - - @property - def x(self) -> int: - return self._x - - @x.setter - def x(self, val : int) -> None: - self._x = val - -#Inherits trait property setters and getters -class E(D): - def __init__(self) -> None: - D.__init__(self) - -#Overrides trait property setters and getters -class F(D): - def __init__(self) -> None: - self._x = 10 - - @property - def x(self) -> int: - return self._x + 10 - - @x.setter - def x(self, val : int) -> None: - self._x = val + 10 - -# # Property setter and getter are subtypes of base property setters and getters -# # class G(A): -# # def __init__(self) -> None: -# # A.__init__(self) - -# # @property -# # def y(self) -> int: -# # return self._y - -# # @y.setter -# # def y(self, val : object) -> None: -# # self._y = val - -[file other.py] -# Run in both interpreted and compiled mode - -from native import A, B, C, D, E, F - -a = A() -assert a.x == 0 -assert a._x == 0 -a.x = 1 -assert a.x == 1 -assert a._x == 1 -a._x = 0 -assert a.x == 0 -assert a._x == 0 -b = B() -assert b.x == 11 -assert b._x == 10 -b.x = 11 -assert b.x == 13 -b._x = 11 -assert b.x == 12 -c = C() -assert c.x == 0 -c.x = 1000 -assert c.x == 1000 -e = E() -assert e.x == 0 -e.x = 1000 -assert e.x == 1000 -f = F() -assert f.x == 20 -f.x = 30 -assert f.x == 50 - -[file driver.py] -# Run the tests in both interpreted and compiled mode -import other -import other_interpreted - -[out] - -[case testDunders] -from typing import Any -class Item: - def __init__(self, value: str) -> None: - self.value = value - - def __hash__(self) -> int: - return hash(self.value) - - def __eq__(self, rhs: object) -> bool: - return isinstance(rhs, Item) and self.value == rhs.value - - def __lt__(self, x: 'Item') -> bool: - return self.value < x.value - -class Subclass1(Item): - def __bool__(self) -> bool: - return bool(self.value) - -class NonBoxedThing: - def __getitem__(self, index: Item) -> Item: - return Item("2 * " + index.value + " + 1") - -class BoxedThing: - def __getitem__(self, index: int) -> int: - return 2 * index + 1 - -class Subclass2(BoxedThing): - pass - -class UsesNotImplemented: - def __eq__(self, b: object) -> bool: - return NotImplemented - -def index_into(x : Any, y : Any) -> Any: - return x[y] - -def internal_index_into() -> None: - x = BoxedThing() - print (x[3]) - y = NonBoxedThing() - z = Item("3") - print(y[z].value) - -def is_truthy(x: Item) -> bool: - return True if x else False - -[file driver.py] -from native import * -x = BoxedThing() -y = 3 -print(x[y], index_into(x, y)) - -x = Subclass2() -y = 3 -print(x[y], index_into(x, y)) - -z = NonBoxedThing() -w = Item("3") -print(z[w].value, index_into(z, w).value) - -i1 = Item('lolol') -i2 = Item('lol' + 'ol') -i3 = Item('xyzzy') -assert hash(i1) == hash(i2) - -assert i1 == i2 -assert not i1 != i2 -assert not i1 == i3 -assert i1 != i3 -assert i2 < i3 -assert not i1 < i2 -assert i1 == Subclass1('lolol') - -assert is_truthy(Item('')) -assert is_truthy(Item('a')) -assert not is_truthy(Subclass1('')) -assert is_truthy(Subclass1('a')) - -assert UsesNotImplemented() != object() - -internal_index_into() -[out] -7 7 -7 7 -2 * 3 + 1 2 * 3 + 1 -7 -2 * 3 + 1 - -[case testDummyTypes] -from typing import Tuple, List, Dict, NamedTuple -from typing_extensions import Literal, TypedDict, NewType - -class A: - pass - -T = List[A] -U = List[Tuple[int, str]] -Z = List[List[int]] -D = Dict[int, List[int]] -N = NewType('N', int) -G = Tuple[int, str] -def foo(x: N) -> int: - return x -foo(N(10)) -z = N(10) -Lol = NamedTuple('Lol', (('a', int), ('b', T))) -x = Lol(1, []) -def take_lol(x: Lol) -> int: - return x.a - -TD = TypedDict('TD', {'a': int}) -def take_typed_dict(x: TD) -> int: - return x['a'] - -def take_literal(x: Literal[1, 2, 3]) -> None: - print(x) - -[file driver.py] -import sys -from native import * - -if sys.version_info[:3] > (3, 5, 2): - assert "%s %s %s %s" % (T, U, Z, D) == "typing.List[native.A] typing.List[typing.Tuple[int, str]] typing.List[typing.List[int]] typing.Dict[int, typing.List[int]]" -print(x) -print(z) -print(take_lol(x)) -print(take_typed_dict({'a': 20})) -try: - take_typed_dict(None) -except Exception as e: - print(type(e).__name__) - - -take_literal(1) -# We check that the type is the real underlying type -try: - take_literal(None) -except Exception as e: - print(type(e).__name__) -# ... but not that it is a valid literal value -take_literal(10) -[out] -Lol(a=1, b=[]) -10 -1 -20 -TypeError -1 -TypeError -10 - -[case testUnion] -from typing import Union - -class A: - def __init__(self, x: int) -> None: - self.x = x - def f(self, y: int) -> int: - return y + self.x - -class B: - def __init__(self, x: object) -> None: - self.x = x - def f(self, y: object) -> object: - return y - -def f(x: Union[A, str]) -> object: - if isinstance(x, A): - return x.x - else: - return x + 'x' - -def g(x: int) -> Union[A, int]: - if x == 0: - return A(1) - else: - return x + 1 - -def get(x: Union[A, B]) -> object: - return x.x - -def call(x: Union[A, B]) -> object: - return x.f(5) - -[file driver.py] -from native import A, B, f, g, get, call -assert f('a') == 'ax' -assert f(A(4)) == 4 -assert isinstance(g(0), A) -assert g(2) == 3 -assert get(A(5)) == 5 -assert get(B('x')) == 'x' -assert call(A(4)) == 9 -assert call(B('x')) == 5 -try: - f(1) -except TypeError: - pass -else: - assert False - -[case testYield] -from typing import Generator, Iterable, Union, Tuple, Dict - -def yield_three_times() -> Iterable[int]: - yield 1 - yield 2 - yield 3 - -def yield_twice_and_return() -> Generator[int, None, int]: - yield 1 - yield 2 - return 4 - -def yield_while_loop() -> Generator[int, None, int]: - i = 0 - while i < 5: - if i == 3: - return i - yield i - i += 1 - return -1 - -def yield_for_loop() -> Iterable[int]: - l = [i for i in range(3)] - for i in l: - yield i - - d = {k: None for k in range(3)} - for k in d: - yield k - - for i in range(3): - yield i - - for i in range(three()): - yield i - -def yield_with_except() -> Generator[int, None, None]: - yield 10 - try: - return - except: - print('Caught exception inside generator function') - -def complex_yield(a: int, b: str, c: float) -> Generator[Union[str, int], None, float]: - x = 2 - while x < a: - if x % 2 == 0: - dummy_var = 1 - yield str(x) + ' ' + b - dummy_var = 1 - else: - dummy_var = 1 - yield x - dummy_var = 1 - x += 1 - return c - -def yield_with_default(x: bool = False) -> Iterable[int]: - if x: - yield 0 - -def yield_dict_methods(d1: Dict[int, int], - d2: Dict[int, int], - d3: Dict[int, int]) -> Iterable[int]: - for k in d1.keys(): - yield k - for k, v in d2.items(): - yield k - yield v - for v in d3.values(): - yield v - -def three() -> int: - return 3 - -class A(object): - def __init__(self, x: int) -> None: - self.x = x - - def generator(self) -> Iterable[int]: - yield self.x - -def return_tuple() -> Generator[int, None, Tuple[int, int]]: - yield 0 - return 1, 2 - -[file driver.py] -from native import ( - yield_three_times, - yield_twice_and_return, - yield_while_loop, - yield_for_loop, - yield_with_except, - complex_yield, - yield_with_default, - A, - return_tuple, - yield_dict_methods, -) -from testutil import run_generator -from collections import defaultdict - -assert run_generator(yield_three_times()) == ((1, 2, 3), None) -assert run_generator(yield_twice_and_return()) == ((1, 2), 4) -assert run_generator(yield_while_loop()) == ((0, 1, 2), 3) -assert run_generator(yield_for_loop()) == (tuple(4 * [i for i in range(3)]), None) -assert run_generator(yield_with_except()) == ((10,), None) -assert run_generator(complex_yield(5, 'foo', 1.0)) == (('2 foo', 3, '4 foo'), 1.0) -assert run_generator(yield_with_default()) == ((), None) -assert run_generator(A(0).generator()) == ((0,), None) -assert run_generator(return_tuple()) == ((0,), (1, 2)) -assert run_generator(yield_dict_methods({}, {}, {})) == ((), None) -assert run_generator(yield_dict_methods({1: 2}, {3: 4}, {5: 6})) == ((1, 3, 4, 6), None) -dd = defaultdict(int, {0: 1}) -assert run_generator(yield_dict_methods(dd, dd, dd)) == ((0, 0, 1, 1), None) - -for i in yield_twice_and_return(): - print(i) - -for i in yield_while_loop(): - print(i) - -[out] -1 -2 -0 -1 -2 - -[case testYieldTryFinallyWith] -from typing import Generator, Any - -class Thing: - def __init__(self, x: str) -> None: - self.x = x - def __enter__(self) -> str: - print('enter!', self.x) - if self.x == 'crash': - raise Exception('ohno') - return self.x - def __exit__(self, x: Any, y: Any, z: Any) -> None: - print('exit!', self.x, y) - -def yield_try_finally() -> Generator[int, None, str]: - try: - yield 1 - yield 2 - return 'lol' - except Exception: - raise - finally: - print('goodbye!') - -def yield_with(i: int) -> Generator[int, None, int]: - with Thing('a') as x: - yield 1 - print("yooo?", x) - if i == 0: - yield 2 - return 10 - elif i == 1: - raise Exception('exception!') - return -1 - -[file driver.py] -from native import yield_try_finally, yield_with -from testutil import run_generator - -print(run_generator(yield_try_finally(), p=True)) -print(run_generator(yield_with(0), p=True)) -print(run_generator(yield_with(1), p=True)) -[out] -1 -2 -goodbye! -((1, 2), 'lol') -enter! a -1 -yooo? a -2 -exit! a None -((1, 2), 10) -enter! a -1 -yooo? a -exit! a exception! -((1,), 'exception!') - -[case testYieldNested] -from typing import Callable, Generator - -def normal(a: int, b: float) -> Callable: - def generator(x: int, y: str) -> Generator: - yield a - yield b - yield x - yield y - return generator - -def generator(a: int) -> Generator: - def normal(x: int) -> int: - return a + x - for i in range(3): - yield normal(i) - -def triple() -> Callable: - def generator() -> Generator: - x = 0 - def inner() -> int: - x += 1 - return x - while x < 3: - yield inner() - return generator - -def another_triple() -> Callable: - def generator() -> Generator: - x = 0 - def inner_generator() -> Generator: - x += 1 - yield x - yield next(inner_generator()) - return generator - -def outer() -> Generator: - def recursive(n: int) -> Generator: - if n < 10: - for i in range(n): - yield i - return - for i in recursive(5): - yield i - return recursive(10) - -[file driver.py] -from native import normal, generator, triple, another_triple, outer -from testutil import run_generator - -assert run_generator(normal(1, 2.0)(3, '4.00')) == ((1, 2.0, 3, '4.00'), None) -assert run_generator(generator(1)) == ((1, 2, 3), None) -assert run_generator(triple()()) == ((1, 2, 3), None) -assert run_generator(another_triple()()) == ((1,), None) -assert run_generator(outer()) == ((0, 1, 2, 3, 4), None) - -[case testYieldThrow] -from typing import Generator, Iterable, Any -from traceback import print_tb -from contextlib import contextmanager -import wrapsys - -def generator() -> Iterable[int]: - try: - yield 1 - yield 2 - yield 3 - except Exception as e: - print_tb(wrapsys.exc_info()[2]) - s = str(e) - if s: - print('caught exception with value ' + s) - else: - print('caught exception without value') - return 0 - -def no_except() -> Iterable[int]: - yield 1 - yield 2 - -def raise_something() -> Iterable[int]: - yield 1 - yield 2 - raise Exception('failure') - -def wrapper(x: Any) -> Any: - return (yield from x) - -def foo() -> Generator[int, None, None]: - try: - yield 1 - except Exception as e: - print(str(e)) - finally: - print('goodbye') - -ctx_manager = contextmanager(foo) - -[file wrapsys.py] -# This is a gross hack around some limitations of the test system/mypyc. -from typing import Any -import sys -def exc_info() -> Any: - return sys.exc_info() # type: ignore - -[file driver.py] -import sys -from typing import Generator, Tuple, TypeVar, Sequence -from native import generator, ctx_manager, wrapper, no_except, raise_something - -T = TypeVar('T') -U = TypeVar('U') - -def run_generator_and_throw(gen: Generator[T, None, U], - num_times: int, - value: object = None, - traceback: object = None) -> Tuple[Sequence[T], U]: - res = [] - try: - for i in range(num_times): - res.append(next(gen)) - if value is not None and traceback is not None: - gen.throw(Exception, value, traceback) - elif value is not None: - gen.throw(Exception, value) - else: - gen.throw(Exception) - except StopIteration as e: - return (tuple(res), e.value) - except Exception as e: - return (tuple(res), str(e)) - -assert run_generator_and_throw(generator(), 0, 'hello') == ((), 'hello') -assert run_generator_and_throw(generator(), 3) == ((1, 2, 3), 0) -assert run_generator_and_throw(generator(), 2, 'some string') == ((1, 2), 0) -try: - raise Exception -except Exception as e: - tb = sys.exc_info()[2] - assert run_generator_and_throw(generator(), 1, 'some other string', tb) == ((1,), 0) - -assert run_generator_and_throw(wrapper(generator()), 0, 'hello') == ((), 'hello') -assert run_generator_and_throw(wrapper(generator()), 3) == ((1, 2, 3), 0) -assert run_generator_and_throw(wrapper(generator()), 2, 'some string') == ((1, 2), 0) -# Make sure we aren't leaking exc_info -assert sys.exc_info()[0] is None - -assert run_generator_and_throw(wrapper([1, 2, 3]), 3, 'lol') == ((1, 2, 3), 'lol') -assert run_generator_and_throw(wrapper(no_except()), 2, 'lol') == ((1, 2), 'lol') - -assert run_generator_and_throw(wrapper(raise_something()), 3) == ((1, 2), 'failure') - -with ctx_manager() as c: - raise Exception('exception') - -[out] - File "native.py", line 10, in generator - yield 3 - File "native.py", line 9, in generator - yield 2 - File "native.py", line 8, in generator - yield 1 - File "driver.py", line 31, in - raise Exception - File "native.py", line 10, in generator - yield 3 - File "native.py", line 30, in wrapper - return (yield from x) - File "native.py", line 9, in generator - yield 2 - File "native.py", line 30, in wrapper - return (yield from x) -caught exception without value -caught exception with value some string -caught exception with value some other string -caught exception without value -caught exception with value some string -exception -goodbye - -[case testYieldSend] -from typing import Generator - -def basic() -> Generator[int, int, int]: - x = yield 1 - y = yield (x + 1) - return y - -def use_from() -> Generator[int, int, int]: - return (yield from basic()) - -[file driver.py] -from native import basic, use_from -from testutil import run_generator - -assert run_generator(basic(), [5, 50]) == ((1, 6), 50) -assert run_generator(use_from(), [5, 50]) == ((1, 6), 50) - -[case testYieldFrom] -from typing import Generator, Iterator, List - -def basic() -> Iterator[int]: - yield from [1, 2, 3] - -def call_next() -> int: - x = [] # type: List[int] - return next(iter(x)) - -def inner(b: bool) -> Generator[int, None, int]: - if b: - yield from [1, 2, 3] - return 10 - -def with_return(b: bool) -> Generator[int, None, int]: - x = yield from inner(b) - for a in [1, 2]: - pass - return x - -[file driver.py] -from native import basic, call_next, with_return -from testutil import run_generator, assertRaises - -assert run_generator(basic()) == ((1, 2, 3), None) - -with assertRaises(StopIteration): - call_next() - -assert run_generator(with_return(True)) == ((1, 2, 3), 10) -assert run_generator(with_return(False)) == ((), 10) - -[case testDecorators1] -from typing import Generator, Callable, Iterator -from contextlib import contextmanager - -def a(f: Callable[[], None]) -> Callable[[], None]: - def g() -> None: - print('Entering') - f() - print('Exited') - return g - -def b(f: Callable[[], None]) -> Callable[[], None]: - def g() -> None: - print('***') - f() - print('***') - return g - -@contextmanager -def foo() -> Iterator[int]: - try: - print('started') - yield 0 - finally: - print('finished') - -@contextmanager -def catch() -> Iterator[None]: - try: - print('started') - yield - except IndexError: - print('index') - raise - except Exception: - print('lol') - -def thing() -> None: - c() - -@a -@b -def c() -> None: - @a - @b - def d() -> None: - print('d') - print('c') - d() - -def hm() -> None: - x = [1] - with catch(): - x[2] - -[file driver.py] -from native import foo, c, thing, hm - -with foo() as f: - print('hello') - -c() -thing() -print('==') -try: - hm() -except IndexError: - pass -else: - assert False - -[out] -started -hello -finished -Entering -*** -c -Entering -*** -d -*** -Exited -*** -Exited -Entering -*** -c -Entering -*** -d -*** -Exited -*** -Exited -== -started -index - -[case testDecoratorsMethods] -from typing import Any, Callable, Iterator, TypeVar -from contextlib import contextmanager - -T = TypeVar('T') -def dec(f: T) -> T: - return f - -def a(f: Callable[[Any], None]) -> Callable[[Any], None]: - def g(a: Any) -> None: - print('Entering') - f(a) - print('Exited') - return g - -class A: - @a - def foo(self) -> None: - print('foo') - - @contextmanager - def generator(self) -> Iterator[int]: - try: - print('contextmanager: entering') - yield 0 - finally: - print('contextmanager: exited') - -class Lol: - @staticmethod - def foo() -> None: - Lol.bar() - Lol.baz() - - @staticmethod - @dec - def bar() -> None: - pass - - @classmethod - @dec - def baz(cls) -> None: - pass - -def inside() -> None: - with A().generator() as g: - print('hello!') - -with A().generator() as g: - print('hello!') - -def lol() -> None: - with A().generator() as g: - raise Exception - -[file driver.py] -from native import A, lol - -A.foo(A()) -A().foo() -with A().generator() as g: - print('hello!') -try: - lol() -except: - pass -else: - assert False - -[out] -contextmanager: entering -hello! -contextmanager: exited -Entering -foo -Exited -Entering -foo -Exited -contextmanager: entering -hello! -contextmanager: exited -contextmanager: entering -contextmanager: exited - -[case testAnyAll] -from typing import Iterable - -def call_any_nested(l: Iterable[Iterable[int]], val: int = 0) -> int: - res = any(i == val for l2 in l for i in l2) - return 0 if res else 1 - -def call_any(l: Iterable[int], val: int = 0) -> int: - res = any(i == val for i in l) - return 0 if res else 1 - -def call_all(l: Iterable[int], val: int = 0) -> int: - res = all(i == val for i in l) - return 0 if res else 1 - -[file driver.py] -from native import call_any, call_all, call_any_nested - -zeros = [0, 0, 0] -ones = [1, 1, 1] -mixed_001 = [0, 0, 1] -mixed_010 = [0, 1, 0] -mixed_100 = [1, 0, 0] -mixed_011 = [0, 1, 1] -mixed_101 = [1, 0, 1] -mixed_110 = [1, 1, 0] - -assert call_any([]) == 1 -assert call_any(zeros) == 0 -assert call_any(ones) == 1 -assert call_any(mixed_001) == 0 -assert call_any(mixed_010) == 0 -assert call_any(mixed_100) == 0 -assert call_any(mixed_011) == 0 -assert call_any(mixed_101) == 0 -assert call_any(mixed_110) == 0 - -assert call_all([]) == 0 -assert call_all(zeros) == 0 -assert call_all(ones) == 1 -assert call_all(mixed_001) == 1 -assert call_all(mixed_010) == 1 -assert call_all(mixed_100) == 1 -assert call_all(mixed_011) == 1 -assert call_all(mixed_101) == 1 -assert call_all(mixed_110) == 1 - -assert call_any_nested([[1, 1, 1], [1, 1], []]) == 1 -assert call_any_nested([[1, 1, 1], [0, 1], []]) == 0 - -[case testNextGenerator] -from typing import Iterable - -def f(x: int) -> int: - print(x) - return x - -def call_next_loud(l: Iterable[int], val: int) -> int: - return next(i for i in l if f(i) == val) - -def call_next_default(l: Iterable[int], val: int) -> int: - return next((i*2 for i in l if i == val), -1) - -def call_next_default_list(l: Iterable[int], val: int) -> int: - return next((i*2 for i in l if i == val), -1) -[file driver.py] -from native import call_next_loud, call_next_default, call_next_default_list -from testutil import assertRaises - -assert call_next_default([0, 1, 2], 0) == 0 -assert call_next_default([0, 1, 2], 1) == 2 -assert call_next_default([0, 1, 2], 2) == 4 -assert call_next_default([0, 1, 2], 3) == -1 -assert call_next_default([], 0) == -1 -assert call_next_default_list([0, 1, 2], 0) == 0 -assert call_next_default_list([0, 1, 2], 1) == 2 -assert call_next_default_list([0, 1, 2], 2) == 4 -assert call_next_default_list([0, 1, 2], 3) == -1 -assert call_next_default_list([], 0) == -1 - -assert call_next_loud([0, 1, 2], 0) == 0 -assert call_next_loud([0, 1, 2], 1) == 1 -assert call_next_loud([0, 1, 2], 2) == 2 -with assertRaises(StopIteration): - call_next_loud([42], 3) -with assertRaises(StopIteration): - call_next_loud([], 3) - -[out] -0 -0 -1 -0 -1 -2 -42 - -[case testGeneratorSuper] -from typing import Iterator, Callable, Any - -class A(): - def testA(self) -> int: - return 2 - -class B(A): - def testB(self) -> Iterator[int]: - x = super().testA() - while True: - yield x - -def testAsserts(): - b = B() - b_gen = b.testB() - assert next(b_gen) == 2 - -[file driver.py] -from native import testAsserts - -testAsserts() - -[case testAssignModule] -import a -assert a.x == 20 -a.x = 10 -[file a.py] -x = 20 -[file driver.py] -import native - -[case testNoneStuff] -from typing import Optional -class A: - x: int - -def lol(x: A) -> None: - setattr(x, 'x', 5) - -def none() -> None: - return - -def arg(x: Optional[A]) -> bool: - return x is None - - -[file driver.py] -import native -native.lol(native.A()) - -# Catch refcounting failures -for i in range(10000): - native.none() - native.arg(None) - -[case testBorrowRefs] -def make_garbage(arg: object) -> None: - b = True - while b: - arg = None - b = False - -[file driver.py] -from native import make_garbage -import sys - -def test(): - x = object() - r0 = sys.getrefcount(x) - make_garbage(x) - r1 = sys.getrefcount(x) - assert r0 == r1 - -test() - -[case testForZipAndEnumerate] -from typing import Iterable, List, Any -def f(a: Iterable[int], b: List[int]) -> List[Any]: - res = [] - for (x, y), z in zip(enumerate(a), b): - res.append((x, y, z)) - return res -def g(a: Iterable[int], b: Iterable[str]) -> List[Any]: - res = [] - for x, (y, z) in enumerate(zip(a, b)): - res.append((x, y, z)) - return res - -[file driver.py] -from native import f, g - -assert f([6, 7], [8, 9]) == [(0, 6, 8), (1, 7, 9)] -assert g([6, 7], ['a', 'b']) == [(0, 6, 'a'), (1, 7, 'b')] -assert f([6, 7], [8]) == [(0, 6, 8)] -assert f([6], [8, 9]) == [(0, 6, 8)] - -[case testFinalStaticRunFail] -if False: - from typing import Final - -if bool(): - x: 'Final' = [1] - -def f() -> int: - return x[0] - -[file driver.py] -from native import f -try: - print(f()) -except NameError as e: - print(e.args[0]) -[out] -value for final name "x" was not set - -[case testFinalStaticRunListTupleInt] -if False: - from typing import Final - -x: 'Final' = [1] -y: 'Final' = (1, 2) -z: 'Final' = 1 + 1 - -def f() -> int: - return x[0] -def g() -> int: - return y[0] -def h() -> int: - return z - 1 - -[file driver.py] -from native import f, g, h, x, y, z -print(f()) -print(x[0]) -print(g()) -print(y) -print(h()) -print(z) -[out] -1 -1 -1 -(1, 2) -1 -2 - -[case testUnannotatedFunction] -def g(x: int) -> int: - return x * 2 - -def f(x): - return g(x) -[file driver.py] -from native import f -assert f(3) == 6 - -[case testCheckVersion] -import sys - -# We lie about the version we are running in tests if it is 3.5, so -# that hits a crash case. -if sys.version_info[:2] == (3, 9): - def version() -> int: - return 9 -elif sys.version_info[:2] == (3, 8): - def version() -> int: - return 8 -elif sys.version_info[:2] == (3, 7): - def version() -> int: - return 7 -elif sys.version_info[:2] == (3, 6): - def version() -> int: - return 6 -else: - raise Exception("we don't support this version yet!") - - -[file driver.py] -import sys -version = sys.version_info[:2] - -try: - import native - assert version != (3, 5), "3.5 should fail!" - assert native.version() == sys.version_info[1] -except RuntimeError: - assert version == (3, 5), "only 3.5 should fail!" - -[case testNameClashIssues] -class A: - def foo(self) -> object: - yield -class B: - def foo(self) -> object: - yield - -class C: - def foo(self) -> None: - def bar(self) -> None: - pass - -def C___foo() -> None: pass - -class D: - def foo(self) -> None: - def bar(self) -> None: - pass - -class E: - default: int - switch: int - -[file driver.py] -# really I only care it builds - -[case testIterTypeTrickiness] -# Test inferring the type of a for loop body doesn't cause us grief -# Extracted from somethings that broke in mypy - -from typing import Optional - -# really I only care that this one build -def foo(x: object) -> None: - if isinstance(x, dict): - for a in x: - pass - -def bar(x: Optional[str]) -> None: - vars = ( - ("a", 'lol'), - ("b", 'asdf'), - ("lol", x), - ("an int", 10), - ) - for name, value in vars: - pass - -[file driver.py] -from native import bar -bar(None) - -[case testComplicatedArgs] -from typing import Tuple, Dict - -def kwonly1(x: int = 0, *, y: int) -> Tuple[int, int]: - return x, y - -def kwonly2(*, x: int = 0, y: int) -> Tuple[int, int]: - return x, y - -def kwonly3(a: int, b: int = 0, *, y: int, x: int = 1) -> Tuple[int, int, int, int]: - return a, b, x, y - -def kwonly4(*, x: int, y: int) -> Tuple[int, int]: - return x, y - -def varargs1(*args: int) -> Tuple[int, ...]: - return args - -def varargs2(*args: int, **kwargs: int) -> Tuple[Tuple[int, ...], Dict[str, int]]: - return args, kwargs - -def varargs3(**kwargs: int) -> Dict[str, int]: - return kwargs - -def varargs4(a: int, b: int = 0, - *args: int, y: int, x: int = 1, - **kwargs: int) -> Tuple[Tuple[int, ...], Dict[str, int]]: - return (a, b, *args), {'x': x, 'y': y, **kwargs} - -class A: - def f(self, x: int) -> Tuple[int, ...]: - return (x,) - def g(self, x: int) -> Tuple[Tuple[int, ...], Dict[str, int]]: - return (x,), {} - -class B(A): - def f(self, *args: int) -> Tuple[int, ...]: - return args - def g(self, *args: int, **kwargs: int) -> Tuple[Tuple[int, ...], Dict[str, int]]: - return args, kwargs - -[file other.py] -# This file is imported in both compiled and interpreted mode in order to -# test both native calls and calls via the C API. - -from native import ( - kwonly1, kwonly2, kwonly3, kwonly4, - varargs1, varargs2, varargs3, varargs4, - A, B -) - -# kwonly arg tests -assert kwonly1(10, y=20) == (10, 20) -assert kwonly1(y=20) == (0, 20) - -assert kwonly2(x=10, y=20) == (10, 20) -assert kwonly2(y=20) == (0, 20) - -assert kwonly3(10, y=20) == (10, 0, 1, 20) -assert kwonly3(a=10, y=20) == (10, 0, 1, 20) -assert kwonly3(10, 30, y=20) == (10, 30, 1, 20) -assert kwonly3(10, b=30, y=20) == (10, 30, 1, 20) -assert kwonly3(a=10, b=30, y=20) == (10, 30, 1, 20) - -assert kwonly3(10, x=40, y=20) == (10, 0, 40, 20) -assert kwonly3(a=10, x=40, y=20) == (10, 0, 40, 20) -assert kwonly3(10, 30, x=40, y=20) == (10, 30, 40, 20) -assert kwonly3(10, b=30, x=40, y=20) == (10, 30, 40, 20) -assert kwonly3(a=10, b=30, x=40, y=20) == (10, 30, 40, 20) - -assert kwonly4(x=1, y=2) == (1, 2) -assert kwonly4(y=2, x=1) == (1, 2) - -# varargs tests -assert varargs1() == () -assert varargs1(1, 2, 3) == (1, 2, 3) -assert varargs2(1, 2, 3) == ((1, 2, 3), {}) -assert varargs2(1, 2, 3, x=4) == ((1, 2, 3), {'x': 4}) -assert varargs2(x=4) == ((), {'x': 4}) -assert varargs3() == {} -assert varargs3(x=4) == {'x': 4} -assert varargs3(x=4, y=5) == {'x': 4, 'y': 5} - -assert varargs4(-1, y=2) == ((-1, 0), {'x': 1, 'y': 2}) -assert varargs4(-1, 2, y=2) == ((-1, 2), {'x': 1, 'y': 2}) -assert varargs4(-1, 2, 3, y=2) == ((-1, 2, 3), {'x': 1, 'y': 2}) -assert varargs4(-1, 2, 3, x=10, y=2) == ((-1, 2, 3), {'x': 10, 'y': 2}) -assert varargs4(-1, x=10, y=2) == ((-1, 0), {'x': 10, 'y': 2}) -assert varargs4(-1, y=2, z=20) == ((-1, 0), {'x': 1, 'y': 2, 'z': 20}) -assert varargs4(-1, 2, y=2, z=20) == ((-1, 2), {'x': 1, 'y': 2, 'z': 20}) -assert varargs4(-1, 2, 3, y=2, z=20) == ((-1, 2, 3), {'x': 1, 'y': 2, 'z': 20}) -assert varargs4(-1, 2, 3, x=10, y=2, z=20) == ((-1, 2, 3), {'x': 10, 'y': 2, 'z': 20}) -assert varargs4(-1, x=10, y=2, z=20) == ((-1, 0), {'x': 10, 'y': 2, 'z': 20}) - -x = B() # type: A -assert x.f(1) == (1,) -assert x.g(1) == ((1,), {}) -# This one is really funny! When we make native calls we lose -# track of which arguments are positional or keyword, so the glue -# calls them all positional unless they are keyword only... -# It would be possible to fix this by dynamically tracking which -# arguments were passed by keyword (for example, by passing a bitmask -# to functions indicating this), but paying a speed, size, and complexity -# cost for something so deeply marginal seems like a bad choice. -# assert x.g(x=1) == ((), {'x': 1}) - -[file driver.py] -from testutil import assertRaises -from native import ( - kwonly1, kwonly2, kwonly3, kwonly4, - varargs1, varargs2, varargs3, varargs4, -) - -# Run the non-exceptional tests in both interpreted and compiled mode -import other -import other_interpreted - - -# And the tests for errors at the interfaces in interpreted only -with assertRaises(TypeError, "missing required keyword-only argument 'y'"): - kwonly1() -with assertRaises(TypeError, "takes at most 1 positional argument (2 given)"): - kwonly1(10, 20) - -with assertRaises(TypeError, "missing required keyword-only argument 'y'"): - kwonly2() -with assertRaises(TypeError, "takes no positional arguments"): - kwonly2(10, 20) - -with assertRaises(TypeError, "missing required argument 'a'"): - kwonly3(b=30, x=40, y=20) -with assertRaises(TypeError, "missing required keyword-only argument 'y'"): - kwonly3(10) - -with assertRaises(TypeError, "missing required keyword-only argument 'y'"): - kwonly4(x=1) -with assertRaises(TypeError, "missing required keyword-only argument 'x'"): - kwonly4(y=1) -with assertRaises(TypeError, "missing required keyword-only argument 'x'"): - kwonly4() - -with assertRaises(TypeError, "'x' is an invalid keyword argument for varargs1()"): - varargs1(x=10) -with assertRaises(TypeError, "'x' is an invalid keyword argument for varargs1()"): - varargs1(1, x=10) -with assertRaises(TypeError, "varargs3() takes no positional arguments"): - varargs3(10) -with assertRaises(TypeError, "varargs3() takes no positional arguments"): - varargs3(10, x=10) - -with assertRaises(TypeError, "varargs4() missing required argument 'a' (pos 1)"): - varargs4() -with assertRaises(TypeError, "varargs4() missing required keyword-only argument 'y'"): - varargs4(1, 2) -with assertRaises(TypeError, "varargs4() missing required keyword-only argument 'y'"): - varargs4(1, 2, x=1) -with assertRaises(TypeError, "varargs4() missing required keyword-only argument 'y'"): - varargs4(1, 2, 3) -with assertRaises(TypeError, "varargs4() missing required argument 'a' (pos 1)"): - varargs4(y=20) - -[case testTypeErrorMessages] -from typing import Tuple -class A: - pass -class B: - pass - -def f(x: B) -> None: - pass -def g(x: Tuple[int, A]) -> None: - pass -[file driver.py] -from testutil import assertRaises -from native import A, f, g - -class Busted: - pass -Busted.__module__ = None - -with assertRaises(TypeError, "int"): - f(0) -with assertRaises(TypeError, "native.A"): - f(A()) -with assertRaises(TypeError, "tuple[None, native.A]"): - f((None, A())) -with assertRaises(TypeError, "tuple[tuple[int, str], native.A]"): - f(((1, "ha"), A())) -with assertRaises(TypeError, "tuple[<50 items>]"): - f(tuple(range(50))) - -with assertRaises(TypeError, "errored formatting real type!"): - f(Busted()) - -with assertRaises(TypeError, "tuple[int, native.A] object expected; got tuple[int, int]"): - g((20, 30)) - -[case testComprehensionShadowBinder] -def foo(x: object) -> object: - if isinstance(x, list): - return tuple(x for x in x), x - return None - -[file driver.py] -from native import foo - -assert foo(None) == None -assert foo([1, 2, 3]) == ((1, 2, 3), [1, 2, 3]) - -[case testReexport] -# Test that we properly handle accessing values that have been reexported -import a -def f(x: int) -> int: - return a.g(x) + a.foo + a.b.foo - -whatever = a.A() - -[file a.py] -from b import g as g, A as A, foo as foo -import b - -[file b.py] -def g(x: int) -> int: - return x + 1 - -class A: - pass - -foo = 20 - -[file driver.py] -from native import f, whatever -import b - -assert f(20) == 61 -assert isinstance(whatever, b.A) diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index bbe4d31a8dbc..ee9cdf176af9 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -30,12 +30,21 @@ from mypyc.test.test_serialization import check_serialization_roundtrip files = [ + 'run-misc.test', 'run-functions.test', - 'run.test', + 'run-integers.test', 'run-strings.test', 'run-tuples.test', + 'run-lists.test', + 'run-dicts.test', + 'run-sets.test', + 'run-primitives.test', + 'run-loops.test', + 'run-exceptions.test', + 'run-imports.test', 'run-classes.test', 'run-traits.test', + 'run-generators.test', 'run-multimodule.test', 'run-bench.test', 'run-mypy-sim.test', From e8e44e6f9f4a865802d544ea48ea765dfeccdd67 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 11 Jul 2020 14:17:53 +0100 Subject: [PATCH 062/138] [mypyc] Use C optimization level 0 for tests by default (#9131) This speeds up run tests significantly, but it might miss some checks only performed on higher optimization levels. On my Linux desktop, this speeds up `pytest mypyc` from about 18s to about 12s. It should also speed up CI builds, but I haven't measured the impact. Let's try this out. We can always revert this back if this turns out to cause problems. The environment variable MYPYC_OPT_LEVEL can be used to override the optimization level. Example: MYPYC_OPT_LEVEL=3 pytest mypyc Closes mypyc/mypyc#745. --- mypyc/test/test_run.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/mypyc/test/test_run.py b/mypyc/test/test_run.py index ee9cdf176af9..9c7c48aa69ef 100644 --- a/mypyc/test/test_run.py +++ b/mypyc/test/test_run.py @@ -56,7 +56,7 @@ setup(name='test_run_output', ext_modules=mypycify({}, separate={}, skip_cgen_input={!r}, strip_asserts=False, - multi_file={}), + multi_file={}, opt_level='{}'), ) """ @@ -240,10 +240,16 @@ def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> if incremental_step == 1: check_serialization_roundtrip(ir) + opt_level = int(os.environ.get('MYPYC_OPT_LEVEL', 0)) + setup_file = os.path.abspath(os.path.join(WORKDIR, 'setup.py')) # We pass the C file information to the build script via setup.py unfortunately with open(setup_file, 'w', encoding='utf-8') as f: - f.write(setup_format.format(module_paths, separate, cfiles, self.multi_file)) + f.write(setup_format.format(module_paths, + separate, + cfiles, + self.multi_file, + opt_level)) if not run_setup(setup_file, ['build_ext', '--inplace']): if testcase.config.getoption('--mypyc-showc'): From 08cd1d66e82d2ca81cc014716f1a9b864b30f31f Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Sat, 11 Jul 2020 19:30:09 +0100 Subject: [PATCH 063/138] [mypyc] Split CPy.h into multiple C files (#9132) Move functions from CPy.h to C files corresponding to various modules that define primitives. For example, the new file `int_ops.c` corresponds to `mypyc.primitives.int_ops`. Also group related things together in CPy.h The motivation is to clean things up by not having function implementations in a header (for non-inline functions), and to improve the organization of the code. CPy.h used to have a lot of only loosely related functionality. As a side effect, many of the functions are no longer declared as static. I left all inline functions in CPy.h to avoid regressing performance. Closes mypyc/mypyc#733. --- mypyc/build.py | 4 +- mypyc/codegen/emitmodule.py | 6 +- mypyc/common.py | 15 + mypyc/lib-rt/CPy.h | 1884 ++++---------------------------- mypyc/lib-rt/dict_ops.c | 362 ++++++ mypyc/lib-rt/exc_ops.c | 256 +++++ mypyc/lib-rt/generic_ops.c | 37 + mypyc/lib-rt/{CPy.c => init.c} | 7 - mypyc/lib-rt/int_ops.c | 275 +++++ mypyc/lib-rt/list_ops.c | 125 +++ mypyc/lib-rt/misc_ops.c | 496 +++++++++ mypyc/lib-rt/set_ops.c | 17 + mypyc/lib-rt/setup.py | 18 +- mypyc/lib-rt/str_ops.c | 60 + mypyc/lib-rt/tuple_ops.c | 31 + 15 files changed, 1900 insertions(+), 1693 deletions(-) create mode 100644 mypyc/lib-rt/dict_ops.c create mode 100644 mypyc/lib-rt/exc_ops.c create mode 100644 mypyc/lib-rt/generic_ops.c rename mypyc/lib-rt/{CPy.c => init.c} (62%) create mode 100644 mypyc/lib-rt/int_ops.c create mode 100644 mypyc/lib-rt/list_ops.c create mode 100644 mypyc/lib-rt/misc_ops.c create mode 100644 mypyc/lib-rt/set_ops.c create mode 100644 mypyc/lib-rt/str_ops.c create mode 100644 mypyc/lib-rt/tuple_ops.c diff --git a/mypyc/build.py b/mypyc/build.py index d662dac871a0..0a0cf3b03a27 100644 --- a/mypyc/build.py +++ b/mypyc/build.py @@ -37,7 +37,7 @@ from mypyc.namegen import exported_name from mypyc.options import CompilerOptions from mypyc.errors import Errors -from mypyc.common import shared_lib_name +from mypyc.common import RUNTIME_C_FILES, shared_lib_name from mypyc.ir.module_ir import format_modules from mypyc.codegen import emitmodule @@ -536,7 +536,7 @@ def mypycify( # compiler invocations. shared_cfilenames = [] if not compiler_options.include_runtime_files: - for name in ['CPy.c', 'getargs.c']: + for name in RUNTIME_C_FILES: rt_file = os.path.join(build_dir, name) with open(os.path.join(include_dir(), name), encoding='utf-8') as f: write_file(rt_file, f.read()) diff --git a/mypyc/codegen/emitmodule.py b/mypyc/codegen/emitmodule.py index 79d2e3f9605b..8b3ee89a1d76 100644 --- a/mypyc/codegen/emitmodule.py +++ b/mypyc/codegen/emitmodule.py @@ -23,7 +23,7 @@ from mypyc.irbuild.prepare import load_type_map from mypyc.irbuild.mapper import Mapper from mypyc.common import ( - PREFIX, TOP_LEVEL_NAME, INT_PREFIX, MODULE_PREFIX, shared_lib_name, + PREFIX, TOP_LEVEL_NAME, INT_PREFIX, MODULE_PREFIX, RUNTIME_C_FILES, shared_lib_name, ) from mypyc.codegen.cstring import encode_as_c_string, encode_bytes_as_c_string from mypyc.codegen.emit import EmitterContext, Emitter, HeaderDeclaration @@ -493,8 +493,8 @@ def generate_c_for_modules(self) -> List[Tuple[str, str]]: # Optionally just include the runtime library c files to # reduce the number of compiler invocations needed if self.compiler_options.include_runtime_files: - base_emitter.emit_line('#include "CPy.c"') - base_emitter.emit_line('#include "getargs.c"') + for name in RUNTIME_C_FILES: + base_emitter.emit_line('#include "{}"'.format(name)) base_emitter.emit_line('#include "__native{}.h"'.format(self.short_group_suffix)) base_emitter.emit_line('#include "__native_internal{}.h"'.format(self.short_group_suffix)) emitter = base_emitter diff --git a/mypyc/common.py b/mypyc/common.py index 96f5e9079443..bc999f5b6ba1 100644 --- a/mypyc/common.py +++ b/mypyc/common.py @@ -31,6 +31,21 @@ IS_32_BIT_PLATFORM = sys.maxsize < (1 << 31) # type: Final +# Runtime C library files +RUNTIME_C_FILES = [ + 'init.c', + 'getargs.c', + 'int_ops.c', + 'list_ops.c', + 'dict_ops.c', + 'str_ops.c', + 'set_ops.c', + 'tuple_ops.c', + 'exc_ops.c', + 'misc_ops.c', + 'generic_ops.c', +] # type: Final + def decorator_helper_name(func_name: str) -> str: return '__mypyc_{}_decorator_helper__'.format(func_name) diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index e07e9a964579..bec7fd6e19e5 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -1,3 +1,5 @@ +// Mypyc C API + #ifndef CPY_CPY_H #define CPY_CPY_H @@ -16,20 +18,6 @@ extern "C" { } // why isn't emacs smart enough to not indent this #endif -/* We use intentionally non-inlined decrefs since it pretty - * substantially speeds up compile time while only causing a ~1% - * performance degradation. We have our own copies both to avoid the - * null check in Py_DecRef and to avoid making an indirect PIC - * call. */ -CPy_NOINLINE -static void CPy_DecRef(PyObject *p) { - CPy_DECREF(p); -} - -CPy_NOINLINE -static void CPy_XDecRef(PyObject *p) { - CPy_XDECREF(p); -} // Naming conventions: // @@ -39,10 +27,46 @@ static void CPy_XDecRef(PyObject *p) { // Ssize_t: A Py_ssize_t, which ought to be the same width as pointers // Object: CPython object (PyObject *) -static void CPyDebug_Print(const char *msg) { - printf("%s\n", msg); - fflush(stdout); -} + +// Tuple type definitions needed for API functions + + +#ifndef MYPYC_DECLARED_tuple_T3OOO +#define MYPYC_DECLARED_tuple_T3OOO +typedef struct tuple_T3OOO { + PyObject *f0; + PyObject *f1; + PyObject *f2; +} tuple_T3OOO; +static tuple_T3OOO tuple_undefined_T3OOO = { NULL, NULL, NULL }; +#endif + +// Our return tuple wrapper for dictionary iteration helper. +#ifndef MYPYC_DECLARED_tuple_T3CIO +#define MYPYC_DECLARED_tuple_T3CIO +typedef struct tuple_T3CIO { + char f0; // Should continue? + CPyTagged f1; // Last dict offset + PyObject *f2; // Next dictionary key or value +} tuple_T3CIO; +static tuple_T3CIO tuple_undefined_T3CIO = { 2, CPY_INT_TAG, NULL }; +#endif + +// Same as above but for both key and value. +#ifndef MYPYC_DECLARED_tuple_T4CIOO +#define MYPYC_DECLARED_tuple_T4CIOO +typedef struct tuple_T4CIOO { + char f0; // Should continue? + CPyTagged f1; // Last dict offset + PyObject *f2; // Next dictionary key + PyObject *f3; // Next dictionary value +} tuple_T4CIOO; +static tuple_T4CIOO tuple_undefined_T4CIOO = { 2, CPY_INT_TAG, NULL, NULL }; +#endif + + +// Native object operations + // Search backwards through the trait part of a vtable (which sits *before* // the start of the vtable proper) looking for the subvtable describing a trait @@ -67,199 +91,6 @@ static inline size_t CPy_FindAttrOffset(PyTypeObject *trait, CPyVTableItem *vtab } } -static bool _CPy_IsSafeMetaClass(PyTypeObject *metaclass) { - // mypyc classes can't work with metaclasses in - // general. Through some various nasty hacks we *do* - // manage to work with TypingMeta and its friends. - if (metaclass == &PyType_Type) - return true; - PyObject *module = PyObject_GetAttrString((PyObject *)metaclass, "__module__"); - if (!module) { - PyErr_Clear(); - return false; - } - - bool matches = false; - if (PyUnicode_CompareWithASCIIString(module, "typing") == 0 && - (strcmp(metaclass->tp_name, "TypingMeta") == 0 - || strcmp(metaclass->tp_name, "GenericMeta") == 0 - || strcmp(metaclass->tp_name, "_ProtocolMeta") == 0)) { - matches = true; - } else if (PyUnicode_CompareWithASCIIString(module, "typing_extensions") == 0 && - strcmp(metaclass->tp_name, "_ProtocolMeta") == 0) { - matches = true; - } else if (PyUnicode_CompareWithASCIIString(module, "abc") == 0 && - strcmp(metaclass->tp_name, "ABCMeta") == 0) { - matches = true; - } - Py_DECREF(module); - return matches; -} - -// Create a heap type based on a template non-heap type. -// This is super hacky and maybe we should suck it up and use PyType_FromSpec instead. -// We allow bases to be NULL to represent just inheriting from object. -// We don't support NULL bases and a non-type metaclass. -static PyObject *CPyType_FromTemplate(PyTypeObject *template_, - PyObject *orig_bases, - PyObject *modname) { - PyHeapTypeObject *t = NULL; - PyTypeObject *dummy_class = NULL; - PyObject *name = NULL; - PyObject *bases = NULL; - PyObject *slots; - - // If the type of the class (the metaclass) is NULL, we default it - // to being type. (This allows us to avoid needing to initialize - // it explicitly on windows.) - if (!Py_TYPE(template_)) { - Py_TYPE(template_) = &PyType_Type; - } - PyTypeObject *metaclass = Py_TYPE(template_); - - if (orig_bases) { - bases = update_bases(orig_bases); - // update_bases doesn't increment the refcount if nothing changes, - // so we do it to make sure we have distinct "references" to both - if (bases == orig_bases) - Py_INCREF(bases); - - // Find the appropriate metaclass from our base classes. We - // care about this because Generic uses a metaclass prior to - // Python 3.7. - metaclass = _PyType_CalculateMetaclass(metaclass, bases); - if (!metaclass) - goto error; - - if (!_CPy_IsSafeMetaClass(metaclass)) { - PyErr_SetString(PyExc_TypeError, "mypyc classes can't have a metaclass"); - goto error; - } - } - - name = PyUnicode_FromString(template_->tp_name); - if (!name) - goto error; - - // If there is a metaclass other than type, we would like to call - // its __new__ function. Unfortunately there doesn't seem to be a - // good way to mix a C extension class and creating it via a - // metaclass. We need to do it anyways, though, in order to - // support subclassing Generic[T] prior to Python 3.7. - // - // We solve this with a kind of atrocious hack: create a parallel - // class using the metaclass, determine the bases of the real - // class by pulling them out of the parallel class, creating the - // real class, and then merging its dict back into the original - // class. There are lots of cases where this won't really work, - // but for the case of GenericMeta setting a bunch of properties - // on the class we should be fine. - if (metaclass != &PyType_Type) { - assert(bases && "non-type metaclasses require non-NULL bases"); - - PyObject *ns = PyDict_New(); - if (!ns) - goto error; - - if (bases != orig_bases) { - if (PyDict_SetItemString(ns, "__orig_bases__", orig_bases) < 0) - goto error; - } - - dummy_class = (PyTypeObject *)PyObject_CallFunctionObjArgs( - (PyObject *)metaclass, name, bases, ns, NULL); - Py_DECREF(ns); - if (!dummy_class) - goto error; - - Py_DECREF(bases); - bases = dummy_class->tp_bases; - Py_INCREF(bases); - } - - // Allocate the type and then copy the main stuff in. - t = (PyHeapTypeObject*)PyType_GenericAlloc(&PyType_Type, 0); - if (!t) - goto error; - memcpy((char *)t + sizeof(PyVarObject), - (char *)template_ + sizeof(PyVarObject), - sizeof(PyTypeObject) - sizeof(PyVarObject)); - - if (bases != orig_bases) { - if (PyObject_SetAttrString((PyObject *)t, "__orig_bases__", orig_bases) < 0) - goto error; - } - - // Having tp_base set is I think required for stuff to get - // inherited in PyType_Ready, which we needed for subclassing - // BaseException. XXX: Taking the first element is wrong I think though. - if (bases) { - t->ht_type.tp_base = (PyTypeObject *)PyTuple_GET_ITEM(bases, 0); - Py_INCREF((PyObject *)t->ht_type.tp_base); - } - - t->ht_name = name; - Py_INCREF(name); - t->ht_qualname = name; - t->ht_type.tp_bases = bases; - // references stolen so NULL these out - bases = name = NULL; - - if (PyType_Ready((PyTypeObject *)t) < 0) - goto error; - - assert(t->ht_type.tp_base != NULL); - - // XXX: This is a terrible hack to work around a cpython check on - // the mro. It was needed for mypy.stats. I need to investigate - // what is actually going on here. - Py_INCREF(metaclass); - Py_TYPE(t) = metaclass; - - if (dummy_class) { - if (PyDict_Merge(t->ht_type.tp_dict, dummy_class->tp_dict, 0) != 0) - goto error; - // This is the *really* tasteless bit. GenericMeta's __new__ - // in certain versions of typing sets _gorg to point back to - // the class. We need to override it to keep it from pointing - // to the proxy. - if (PyDict_SetItemString(t->ht_type.tp_dict, "_gorg", (PyObject *)t) < 0) - goto error; - } - - // Reject anything that would give us a nontrivial __slots__, - // because the layout will conflict - slots = PyObject_GetAttrString((PyObject *)t, "__slots__"); - if (slots) { - // don't fail on an empty __slots__ - int is_true = PyObject_IsTrue(slots); - Py_DECREF(slots); - if (is_true > 0) - PyErr_SetString(PyExc_TypeError, "mypyc classes can't have __slots__"); - if (is_true != 0) - goto error; - } else { - PyErr_Clear(); - } - - if (PyObject_SetAttrString((PyObject *)t, "__module__", modname) < 0) - goto error; - - if (init_subclass((PyTypeObject *)t, NULL)) - goto error; - - Py_XDECREF(dummy_class); - - return (PyObject *)t; - -error: - Py_XDECREF(t); - Py_XDECREF(bases); - Py_XDECREF(dummy_class); - Py_XDECREF(name); - return NULL; -} - // Get attribute value using vtable (may return an undefined value) #define CPY_GET_ATTR(obj, type, vtable_index, object_type, attr_type) \ ((attr_type (*)(object_type *))((object_type *)obj)->vtable[vtable_index])((object_type *)obj) @@ -283,11 +114,32 @@ static PyObject *CPyType_FromTemplate(PyTypeObject *template_, ((method_type)(CPy_FindTraitVtable(trait, ((object_type *)obj)->vtable)[vtable_index])) -static void CPyError_OutOfMemory(void) { - fprintf(stderr, "fatal: out of memory\n"); - fflush(stderr); - abort(); -} +// Int operations + + +CPyTagged CPyTagged_FromSsize_t(Py_ssize_t value); +CPyTagged CPyTagged_FromObject(PyObject *object); +CPyTagged CPyTagged_StealFromObject(PyObject *object); +CPyTagged CPyTagged_BorrowFromObject(PyObject *object); +PyObject *CPyTagged_AsObject(CPyTagged x); +PyObject *CPyTagged_StealAsObject(CPyTagged x); +Py_ssize_t CPyTagged_AsSsize_t(CPyTagged x); +void CPyTagged_IncRef(CPyTagged x); +void CPyTagged_DecRef(CPyTagged x); +void CPyTagged_XDecRef(CPyTagged x); +CPyTagged CPyTagged_Negate(CPyTagged num); +CPyTagged CPyTagged_Add(CPyTagged left, CPyTagged right); +CPyTagged CPyTagged_Subtract(CPyTagged left, CPyTagged right); +CPyTagged CPyTagged_Multiply(CPyTagged left, CPyTagged right); +CPyTagged CPyTagged_FloorDivide(CPyTagged left, CPyTagged right); +CPyTagged CPyTagged_Remainder(CPyTagged left, CPyTagged right); +bool CPyTagged_IsEq_(CPyTagged left, CPyTagged right); +bool CPyTagged_IsLt_(CPyTagged left, CPyTagged right); +PyObject *CPyTagged_Str(CPyTagged n); +PyObject *CPyLong_FromStrWithBase(PyObject *o, CPyTagged base); +PyObject *CPyLong_FromStr(PyObject *o); +PyObject *CPyLong_FromFloat(PyObject *o); +PyObject *CPyBool_Str(bool b); static inline int CPyTagged_CheckLong(CPyTagged x) { return x & CPY_INT_TAG; @@ -313,224 +165,25 @@ static inline bool CPyTagged_TooBig(Py_ssize_t value) { && (value >= 0 || value < CPY_TAGGED_MIN); } -static CPyTagged CPyTagged_FromSsize_t(Py_ssize_t value) { - // We use a Python object if the value shifted left by 1 is too - // large for Py_ssize_t - if (CPyTagged_TooBig(value)) { - PyObject *object = PyLong_FromSsize_t(value); - return ((CPyTagged)object) | CPY_INT_TAG; - } else { - return value << 1; - } -} - -static CPyTagged CPyTagged_FromObject(PyObject *object) { - int overflow; - // The overflow check knows about CPyTagged's width - Py_ssize_t value = CPyLong_AsSsize_tAndOverflow(object, &overflow); - if (overflow != 0) { - Py_INCREF(object); - return ((CPyTagged)object) | CPY_INT_TAG; - } else { - return value << 1; - } -} - -static CPyTagged CPyTagged_StealFromObject(PyObject *object) { - int overflow; - // The overflow check knows about CPyTagged's width - Py_ssize_t value = CPyLong_AsSsize_tAndOverflow(object, &overflow); - if (overflow != 0) { - return ((CPyTagged)object) | CPY_INT_TAG; - } else { - Py_DECREF(object); - return value << 1; - } -} - -static CPyTagged CPyTagged_BorrowFromObject(PyObject *object) { - int overflow; - // The overflow check knows about CPyTagged's width - Py_ssize_t value = CPyLong_AsSsize_tAndOverflow(object, &overflow); - if (overflow != 0) { - return ((CPyTagged)object) | CPY_INT_TAG; - } else { - return value << 1; - } -} - -static PyObject *CPyTagged_AsObject(CPyTagged x) { - PyObject *value; - if (CPyTagged_CheckLong(x)) { - value = CPyTagged_LongAsObject(x); - Py_INCREF(value); - } else { - value = PyLong_FromSsize_t(CPyTagged_ShortAsSsize_t(x)); - if (value == NULL) { - CPyError_OutOfMemory(); - } - } - return value; -} - -static PyObject *CPyTagged_StealAsObject(CPyTagged x) { - PyObject *value; - if (CPyTagged_CheckLong(x)) { - value = CPyTagged_LongAsObject(x); - } else { - value = PyLong_FromSsize_t(CPyTagged_ShortAsSsize_t(x)); - if (value == NULL) { - CPyError_OutOfMemory(); - } - } - return value; -} - -static Py_ssize_t CPyTagged_AsSsize_t(CPyTagged x) { - if (CPyTagged_CheckShort(x)) { - return CPyTagged_ShortAsSsize_t(x); - } else { - return PyLong_AsSsize_t(CPyTagged_LongAsObject(x)); - } -} - -CPy_NOINLINE -static void CPyTagged_IncRef(CPyTagged x) { - if (CPyTagged_CheckLong(x)) { - Py_INCREF(CPyTagged_LongAsObject(x)); - } -} - -CPy_NOINLINE -static void CPyTagged_DecRef(CPyTagged x) { - if (CPyTagged_CheckLong(x)) { - Py_DECREF(CPyTagged_LongAsObject(x)); - } -} - -CPy_NOINLINE -static void CPyTagged_XDecRef(CPyTagged x) { - if (CPyTagged_CheckLong(x)) { - Py_XDECREF(CPyTagged_LongAsObject(x)); - } -} - static inline bool CPyTagged_IsAddOverflow(CPyTagged sum, CPyTagged left, CPyTagged right) { // This check was copied from some of my old code I believe that it works :-) return (Py_ssize_t)(sum ^ left) < 0 && (Py_ssize_t)(sum ^ right) < 0; } -static CPyTagged CPyTagged_Negate(CPyTagged num) { - if (CPyTagged_CheckShort(num) - && num != (CPyTagged) ((Py_ssize_t)1 << (CPY_INT_BITS - 1))) { - // The only possibility of an overflow error happening when negating a short is if we - // attempt to negate the most negative number. - return -num; - } - PyObject *num_obj = CPyTagged_AsObject(num); - PyObject *result = PyNumber_Negative(num_obj); - if (result == NULL) { - CPyError_OutOfMemory(); - } - Py_DECREF(num_obj); - return CPyTagged_StealFromObject(result); -} - -static CPyTagged CPyTagged_Add(CPyTagged left, CPyTagged right) { - // TODO: Use clang/gcc extension __builtin_saddll_overflow instead. - if (CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right)) { - CPyTagged sum = left + right; - if (!CPyTagged_IsAddOverflow(sum, left, right)) { - return sum; - } - } - PyObject *left_obj = CPyTagged_AsObject(left); - PyObject *right_obj = CPyTagged_AsObject(right); - PyObject *result = PyNumber_Add(left_obj, right_obj); - if (result == NULL) { - CPyError_OutOfMemory(); - } - Py_DECREF(left_obj); - Py_DECREF(right_obj); - return CPyTagged_StealFromObject(result); -} - static inline bool CPyTagged_IsSubtractOverflow(CPyTagged diff, CPyTagged left, CPyTagged right) { // This check was copied from some of my old code I believe that it works :-) return (Py_ssize_t)(diff ^ left) < 0 && (Py_ssize_t)(diff ^ right) >= 0; } -static CPyTagged CPyTagged_Subtract(CPyTagged left, CPyTagged right) { - // TODO: Use clang/gcc extension __builtin_saddll_overflow instead. - if (CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right)) { - CPyTagged diff = left - right; - if (!CPyTagged_IsSubtractOverflow(diff, left, right)) { - return diff; - } - } - PyObject *left_obj = CPyTagged_AsObject(left); - PyObject *right_obj = CPyTagged_AsObject(right); - PyObject *result = PyNumber_Subtract(left_obj, right_obj); - if (result == NULL) { - CPyError_OutOfMemory(); - } - Py_DECREF(left_obj); - Py_DECREF(right_obj); - return CPyTagged_StealFromObject(result); -} - static inline bool CPyTagged_IsMultiplyOverflow(CPyTagged left, CPyTagged right) { // This is conservative -- return false only in a small number of all non-overflow cases return left >= (1U << (CPY_INT_BITS/2 - 1)) || right >= (1U << (CPY_INT_BITS/2 - 1)); } -static CPyTagged CPyTagged_Multiply(CPyTagged left, CPyTagged right) { - // TODO: Consider using some clang/gcc extension - if (CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right)) { - if (!CPyTagged_IsMultiplyOverflow(left, right)) { - return left * CPyTagged_ShortAsSsize_t(right); - } - } - PyObject *left_obj = CPyTagged_AsObject(left); - PyObject *right_obj = CPyTagged_AsObject(right); - PyObject *result = PyNumber_Multiply(left_obj, right_obj); - if (result == NULL) { - CPyError_OutOfMemory(); - } - Py_DECREF(left_obj); - Py_DECREF(right_obj); - return CPyTagged_StealFromObject(result); -} - static inline bool CPyTagged_MaybeFloorDivideFault(CPyTagged left, CPyTagged right) { return right == 0 || left == -((size_t)1 << (CPY_INT_BITS-1)); } -static CPyTagged CPyTagged_FloorDivide(CPyTagged left, CPyTagged right) { - if (CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right) - && !CPyTagged_MaybeFloorDivideFault(left, right)) { - Py_ssize_t result = ((Py_ssize_t)left / CPyTagged_ShortAsSsize_t(right)) & ~1; - if (((Py_ssize_t)left < 0) != (((Py_ssize_t)right) < 0)) { - if (result / 2 * right != left) { - // Round down - result -= 2; - } - } - return result; - } - PyObject *left_obj = CPyTagged_AsObject(left); - PyObject *right_obj = CPyTagged_AsObject(right); - PyObject *result = PyNumber_FloorDivide(left_obj, right_obj); - Py_DECREF(left_obj); - Py_DECREF(right_obj); - // Handle exceptions honestly because it could be ZeroDivisionError - if (result == NULL) { - return CPY_INT_TAG; - } else { - return CPyTagged_StealFromObject(result); - } -} - static inline bool CPyTagged_MaybeRemainderFault(CPyTagged left, CPyTagged right) { // Division/modulus can fault when dividing INT_MIN by -1, but we // do our mods on still-tagged integers with the low-bit clear, so @@ -539,41 +192,6 @@ static inline bool CPyTagged_MaybeRemainderFault(CPyTagged left, CPyTagged right return right == 0; } -static CPyTagged CPyTagged_Remainder(CPyTagged left, CPyTagged right) { - if (CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right) - && !CPyTagged_MaybeRemainderFault(left, right)) { - Py_ssize_t result = (Py_ssize_t)left % (Py_ssize_t)right; - if (((Py_ssize_t)right < 0) != ((Py_ssize_t)left < 0) && result != 0) { - result += right; - } - return result; - } - PyObject *left_obj = CPyTagged_AsObject(left); - PyObject *right_obj = CPyTagged_AsObject(right); - PyObject *result = PyNumber_Remainder(left_obj, right_obj); - Py_DECREF(left_obj); - Py_DECREF(right_obj); - // Handle exceptions honestly because it could be ZeroDivisionError - if (result == NULL) { - return CPY_INT_TAG; - } else { - return CPyTagged_StealFromObject(result); - } -} - -static bool CPyTagged_IsEq_(CPyTagged left, CPyTagged right) { - if (CPyTagged_CheckShort(right)) { - return false; - } else { - int result = PyObject_RichCompareBool(CPyTagged_LongAsObject(left), - CPyTagged_LongAsObject(right), Py_EQ); - if (result == -1) { - CPyError_OutOfMemory(); - } - return result; - } -} - static inline bool CPyTagged_IsEq(CPyTagged left, CPyTagged right) { if (CPyTagged_CheckShort(left)) { return left == right; @@ -590,18 +208,6 @@ static inline bool CPyTagged_IsNe(CPyTagged left, CPyTagged right) { } } -static bool CPyTagged_IsLt_(CPyTagged left, CPyTagged right) { - PyObject *left_obj = CPyTagged_AsObject(left); - PyObject *right_obj = CPyTagged_AsObject(right); - int result = PyObject_RichCompareBool(left_obj, right_obj, Py_LT); - Py_DECREF(left_obj); - Py_DECREF(right_obj); - if (result == -1) { - CPyError_OutOfMemory(); - } - return result; -} - static inline bool CPyTagged_IsLt(CPyTagged left, CPyTagged right) { if (CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right)) { return (Py_ssize_t)left < (Py_ssize_t)right; @@ -634,692 +240,147 @@ static inline bool CPyTagged_IsLe(CPyTagged left, CPyTagged right) { } } -static CPyTagged CPyTagged_Id(PyObject *o) { - return CPyTagged_FromSsize_t((Py_ssize_t)o); -} - - -#define MAX_INT_CHARS 22 -#define _PyUnicode_LENGTH(op) \ - (((PyASCIIObject *)(op))->length) - -// using snprintf or PyUnicode_FromFormat was way slower than -// boxing the int and calling PyObject_Str on it, so we implement our own -static int fmt_ssize_t(char *out, Py_ssize_t n) { - bool neg = n < 0; - if (neg) n = -n; - - // buf gets filled backward and then we copy it forward - char buf[MAX_INT_CHARS]; - int i = 0; - do { - buf[i] = (n % 10) + '0'; - n /= 10; - i++; - } while (n); - - - int len = i; - int j = 0; - if (neg) { - out[j++] = '-'; - len++; - } - - for (; j < len; j++, i--) { - out[j] = buf[i-1]; - } - out[j] = '\0'; - - return len; -} - -static PyObject *CPyTagged_ShortToStr(Py_ssize_t n) { - PyObject *obj = PyUnicode_New(MAX_INT_CHARS, 127); - if (!obj) return NULL; - int len = fmt_ssize_t((char *)PyUnicode_1BYTE_DATA(obj), n); - _PyUnicode_LENGTH(obj) = len; - return obj; -} -static PyObject *CPyTagged_Str(CPyTagged n) { - if (CPyTagged_CheckShort(n)) { - return CPyTagged_ShortToStr(CPyTagged_ShortAsSsize_t(n)); - } else { - return PyObject_Str(CPyTagged_AsObject(n)); - } -} +// Generic operations (that work with arbitrary types) -static PyObject *CPyBool_Str(bool b) { - return PyObject_Str(b ? Py_True : Py_False); -} -static PyObject *CPyList_GetItemUnsafe(PyObject *list, CPyTagged index) { - Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); - PyObject *result = PyList_GET_ITEM(list, n); - Py_INCREF(result); - return result; +/* We use intentionally non-inlined decrefs since it pretty + * substantially speeds up compile time while only causing a ~1% + * performance degradation. We have our own copies both to avoid the + * null check in Py_DecRef and to avoid making an indirect PIC + * call. */ +CPy_NOINLINE +static void CPy_DecRef(PyObject *p) { + CPy_DECREF(p); } -static PyObject *CPyList_GetItemShort(PyObject *list, CPyTagged index) { - Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); - Py_ssize_t size = PyList_GET_SIZE(list); - if (n >= 0) { - if (n >= size) { - PyErr_SetString(PyExc_IndexError, "list index out of range"); - return NULL; - } - } else { - n += size; - if (n < 0) { - PyErr_SetString(PyExc_IndexError, "list index out of range"); - return NULL; - } - } - PyObject *result = PyList_GET_ITEM(list, n); - Py_INCREF(result); - return result; +CPy_NOINLINE +static void CPy_XDecRef(PyObject *p) { + CPy_XDECREF(p); } -static PyObject *CPyList_GetItem(PyObject *list, CPyTagged index) { - if (CPyTagged_CheckShort(index)) { - Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); - Py_ssize_t size = PyList_GET_SIZE(list); - if (n >= 0) { - if (n >= size) { - PyErr_SetString(PyExc_IndexError, "list index out of range"); - return NULL; - } - } else { - n += size; - if (n < 0) { - PyErr_SetString(PyExc_IndexError, "list index out of range"); - return NULL; - } - } - PyObject *result = PyList_GET_ITEM(list, n); - Py_INCREF(result); - return result; +static inline CPyTagged CPyObject_Size(PyObject *obj) { + Py_ssize_t s = PyObject_Size(obj); + if (s < 0) { + return CPY_INT_TAG; } else { - PyErr_SetString(PyExc_IndexError, "list index out of range"); - return NULL; + // Technically __len__ could return a really big number, so we + // should allow this to produce a boxed int. In practice it + // shouldn't ever if the data structure actually contains all + // the elements, but... + return CPyTagged_FromSsize_t(s); } } -static bool CPyList_SetItem(PyObject *list, CPyTagged index, PyObject *value) { - if (CPyTagged_CheckShort(index)) { - Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); - Py_ssize_t size = PyList_GET_SIZE(list); - if (n >= 0) { - if (n >= size) { - PyErr_SetString(PyExc_IndexError, "list assignment index out of range"); - return false; - } - } else { - n += size; - if (n < 0) { - PyErr_SetString(PyExc_IndexError, "list assignment index out of range"); - return false; - } - } - // PyList_SET_ITEM doesn't decref the old element, so we do - Py_DECREF(PyList_GET_ITEM(list, n)); - // N.B: Steals reference - PyList_SET_ITEM(list, n, value); - return true; - } else { - PyErr_SetString(PyExc_IndexError, "list assignment index out of range"); - return false; +#ifdef MYPYC_LOG_GETATTR +static void CPy_LogGetAttr(const char *method, PyObject *obj, PyObject *attr) { + PyObject *module = PyImport_ImportModule("getattr_hook"); + if (module) { + PyObject *res = PyObject_CallMethod(module, method, "OO", obj, attr); + Py_XDECREF(res); + Py_DECREF(module); } + PyErr_Clear(); } +#else +#define CPy_LogGetAttr(method, obj, attr) (void)0 +#endif -static PyObject *CPyList_PopLast(PyObject *obj) -{ - // I tried a specalized version of pop_impl for just removing the - // last element and it wasn't any faster in microbenchmarks than - // the generic one so I ditched it. - return list_pop_impl((PyListObject *)obj, -1); -} +// Intercept a method call and log it. This needs to be a macro +// because there is no API that accepts va_args for making a +// call. Worse, it needs to use the comma operator to return the right +// value. +#define CPyObject_CallMethodObjArgs(obj, attr, ...) \ + (CPy_LogGetAttr("log_method", (obj), (attr)), \ + PyObject_CallMethodObjArgs((obj), (attr), __VA_ARGS__)) -static PyObject *CPyList_Pop(PyObject *obj, CPyTagged index) -{ - if (CPyTagged_CheckShort(index)) { - Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); - return list_pop_impl((PyListObject *)obj, n); - } else { - PyErr_SetString(PyExc_IndexError, "pop index out of range"); - return NULL; - } -} +// This one is a macro for consistency with the above, I guess. +#define CPyObject_GetAttr(obj, attr) \ + (CPy_LogGetAttr("log", (obj), (attr)), \ + PyObject_GetAttr((obj), (attr))) -static CPyTagged CPyList_Count(PyObject *obj, PyObject *value) -{ - return list_count((PyListObject *)obj, value); -} +CPyTagged CPyObject_Hash(PyObject *o); +PyObject *CPyObject_GetAttr3(PyObject *v, PyObject *name, PyObject *defl); +PyObject *CPyIter_Next(PyObject *iter); + + +// List operations + + +PyObject *CPyList_GetItem(PyObject *list, CPyTagged index); +PyObject *CPyList_GetItemUnsafe(PyObject *list, CPyTagged index); +PyObject *CPyList_GetItemShort(PyObject *list, CPyTagged index); +bool CPyList_SetItem(PyObject *list, CPyTagged index, PyObject *value); +PyObject *CPyList_PopLast(PyObject *obj); +PyObject *CPyList_Pop(PyObject *obj, CPyTagged index); +CPyTagged CPyList_Count(PyObject *obj, PyObject *value); +PyObject *CPyList_Extend(PyObject *o1, PyObject *o2); +PyObject *CPySequence_Multiply(PyObject *seq, CPyTagged t_size); +PyObject *CPySequence_RMultiply(CPyTagged t_size, PyObject *seq); + + +// Dict operations + + +PyObject *CPyDict_GetItem(PyObject *dict, PyObject *key); +int CPyDict_SetItem(PyObject *dict, PyObject *key, PyObject *value); +PyObject *CPyDict_Get(PyObject *dict, PyObject *key, PyObject *fallback); +PyObject *CPyDict_Build(Py_ssize_t size, ...); +int CPyDict_Update(PyObject *dict, PyObject *stuff); +int CPyDict_UpdateInDisplay(PyObject *dict, PyObject *stuff); +int CPyDict_UpdateFromAny(PyObject *dict, PyObject *stuff); +PyObject *CPyDict_FromAny(PyObject *obj); +PyObject *CPyDict_KeysView(PyObject *dict); +PyObject *CPyDict_ValuesView(PyObject *dict); +PyObject *CPyDict_ItemsView(PyObject *dict); +PyObject *CPyDict_Keys(PyObject *dict); +PyObject *CPyDict_Values(PyObject *dict); +PyObject *CPyDict_Items(PyObject *dict); +PyObject *CPyDict_GetKeysIter(PyObject *dict); +PyObject *CPyDict_GetItemsIter(PyObject *dict); +PyObject *CPyDict_GetValuesIter(PyObject *dict); +tuple_T3CIO CPyDict_NextKey(PyObject *dict_or_iter, CPyTagged offset); +tuple_T3CIO CPyDict_NextValue(PyObject *dict_or_iter, CPyTagged offset); +tuple_T4CIOO CPyDict_NextItem(PyObject *dict_or_iter, CPyTagged offset); -static bool CPySet_Remove(PyObject *set, PyObject *key) { - int success = PySet_Discard(set, key); - if (success == 1) { - return true; +// Check that dictionary didn't change size during iteration. +static inline char CPyDict_CheckSize(PyObject *dict, CPyTagged size) { + if (!PyDict_CheckExact(dict)) { + // Dict subclasses will be checked by Python runtime. + return 1; } - if (success == 0) { - _PyErr_SetKeyError(key); + Py_ssize_t py_size = CPyTagged_AsSsize_t(size); + Py_ssize_t dict_size = PyDict_Size(dict); + if (py_size != dict_size) { + PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); + return 0; } - return false; + return 1; } -static PyObject *CPyList_Extend(PyObject *o1, PyObject *o2) { - return _PyList_Extend((PyListObject *)o1, o2); -} -static PyObject *CPySequenceTuple_GetItem(PyObject *tuple, CPyTagged index) { - if (CPyTagged_CheckShort(index)) { - Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); - Py_ssize_t size = PyTuple_GET_SIZE(tuple); - if (n >= 0) { - if (n >= size) { - PyErr_SetString(PyExc_IndexError, "tuple index out of range"); - return NULL; - } - } else { - n += size; - if (n < 0) { - PyErr_SetString(PyExc_IndexError, "tuple index out of range"); - return NULL; - } - } - PyObject *result = PyTuple_GET_ITEM(tuple, n); - Py_INCREF(result); - return result; - } else { - PyErr_SetString(PyExc_IndexError, "tuple index out of range"); - return NULL; - } -} +// Str operations -static PyObject *CPySequence_Multiply(PyObject *seq, CPyTagged t_size) { - Py_ssize_t size = CPyTagged_AsSsize_t(t_size); - if (size == -1 && PyErr_Occurred()) { - return NULL; - } - return PySequence_Repeat(seq, size); -} -static PyObject *CPySequence_RMultiply(CPyTagged t_size, PyObject *seq) { - return CPySequence_Multiply(seq, t_size); -} - -static CPyTagged CPyObject_Hash(PyObject *o) { - Py_hash_t h = PyObject_Hash(o); - if (h == -1) { - return CPY_INT_TAG; - } else { - // This is tragically annoying. The range of hash values in - // 64-bit python covers 64-bits, and our short integers only - // cover 63. This means that half the time we are boxing the - // result for basically no good reason. To add insult to - // injury it is probably about to be immediately unboxed by a - // tp_hash wrapper. - return CPyTagged_FromSsize_t(h); - } -} - -static inline CPyTagged CPyObject_Size(PyObject *obj) { - Py_ssize_t s = PyObject_Size(obj); - if (s < 0) { - return CPY_INT_TAG; - } else { - // Technically __len__ could return a really big number, so we - // should allow this to produce a boxed int. In practice it - // shouldn't ever if the data structure actually contains all - // the elements, but... - return CPyTagged_FromSsize_t(s); - } -} - -static inline int CPy_ObjectToStatus(PyObject *obj) { - if (obj) { - Py_DECREF(obj); - return 0; - } else { - return -1; - } -} - -// dict subclasses like defaultdict override things in interesting -// ways, so we don't want to just directly use the dict methods. Not -// sure if it is actually worth doing all this stuff, but it saves -// some indirections. -static PyObject *CPyDict_GetItem(PyObject *dict, PyObject *key) { - if (PyDict_CheckExact(dict)) { - PyObject *res = PyDict_GetItemWithError(dict, key); - if (!res) { - if (!PyErr_Occurred()) { - PyErr_SetObject(PyExc_KeyError, key); - } - } else { - Py_INCREF(res); - } - return res; - } else { - return PyObject_GetItem(dict, key); - } -} - -static PyObject *CPyDict_Build(Py_ssize_t size, ...) { - Py_ssize_t i; - - PyObject *res = _PyDict_NewPresized(size); - if (res == NULL) { - return NULL; - } - - va_list args; - va_start(args, size); - - for (i = 0; i < size; i++) { - PyObject *key = va_arg(args, PyObject *); - PyObject *value = va_arg(args, PyObject *); - if (PyDict_SetItem(res, key, value)) { - Py_DECREF(res); - return NULL; - } - } - - va_end(args); - return res; -} - -static PyObject *CPyDict_Get(PyObject *dict, PyObject *key, PyObject *fallback) { - // We are dodgily assuming that get on a subclass doesn't have - // different behavior. - PyObject *res = PyDict_GetItemWithError(dict, key); - if (!res) { - if (PyErr_Occurred()) { - return NULL; - } - res = fallback; - } - Py_INCREF(res); - return res; -} - -static int CPyDict_SetItem(PyObject *dict, PyObject *key, PyObject *value) { - if (PyDict_CheckExact(dict)) { - return PyDict_SetItem(dict, key, value); - } else { - return PyObject_SetItem(dict, key, value); - } -} +PyObject *CPyStr_GetItem(PyObject *str, CPyTagged index); +PyObject *CPyStr_Split(PyObject *str, PyObject *sep, CPyTagged max_split); +PyObject *CPyStr_Append(PyObject *o1, PyObject *o2); -static int CPyDict_UpdateGeneral(PyObject *dict, PyObject *stuff) { - _Py_IDENTIFIER(update); - PyObject *res = _PyObject_CallMethodIdObjArgs(dict, &PyId_update, stuff, NULL); - return CPy_ObjectToStatus(res); -} -static int CPyDict_UpdateInDisplay(PyObject *dict, PyObject *stuff) { - // from https://github.com/python/cpython/blob/55d035113dfb1bd90495c8571758f504ae8d4802/Python/ceval.c#L2710 - int ret = PyDict_Update(dict, stuff); - if (ret < 0) { - if (PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyErr_Format(PyExc_TypeError, - "'%.200s' object is not a mapping", - stuff->ob_type->tp_name); - } - } - return ret; -} +// Set operations -static int CPyDict_Update(PyObject *dict, PyObject *stuff) { - if (PyDict_CheckExact(dict)) { - return PyDict_Update(dict, stuff); - } else { - return CPyDict_UpdateGeneral(dict, stuff); - } -} -static int CPyDict_UpdateFromAny(PyObject *dict, PyObject *stuff) { - if (PyDict_CheckExact(dict)) { - // Argh this sucks - _Py_IDENTIFIER(keys); - if (PyDict_Check(stuff) || _PyObject_HasAttrId(stuff, &PyId_keys)) { - return PyDict_Update(dict, stuff); - } else { - return PyDict_MergeFromSeq2(dict, stuff, 1); - } - } else { - return CPyDict_UpdateGeneral(dict, stuff); - } -} +bool CPySet_Remove(PyObject *set, PyObject *key); -static PyObject *CPyDict_FromAny(PyObject *obj) { - if (PyDict_Check(obj)) { - return PyDict_Copy(obj); - } else { - int res; - PyObject *dict = PyDict_New(); - if (!dict) { - return NULL; - } - _Py_IDENTIFIER(keys); - if (_PyObject_HasAttrId(obj, &PyId_keys)) { - res = PyDict_Update(dict, obj); - } else { - res = PyDict_MergeFromSeq2(dict, obj, 1); - } - if (res < 0) { - Py_DECREF(dict); - return NULL; - } - return dict; - } -} -static PyObject *CPyStr_GetItem(PyObject *str, CPyTagged index) { - if (PyUnicode_READY(str) != -1) { - if (CPyTagged_CheckShort(index)) { - Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); - Py_ssize_t size = PyUnicode_GET_LENGTH(str); - if ((n >= 0 && n >= size) || (n < 0 && n + size < 0)) { - PyErr_SetString(PyExc_IndexError, "string index out of range"); - return NULL; - } - if (n < 0) - n += size; - enum PyUnicode_Kind kind = (enum PyUnicode_Kind)PyUnicode_KIND(str); - void *data = PyUnicode_DATA(str); - Py_UCS4 ch = PyUnicode_READ(kind, data, n); - PyObject *unicode = PyUnicode_New(1, ch); - if (unicode == NULL) - return NULL; - - if (PyUnicode_KIND(unicode) == PyUnicode_1BYTE_KIND) { - PyUnicode_1BYTE_DATA(unicode)[0] = (Py_UCS1)ch; - } - else if (PyUnicode_KIND(unicode) == PyUnicode_2BYTE_KIND) { - PyUnicode_2BYTE_DATA(unicode)[0] = (Py_UCS2)ch; - } else { - assert(PyUnicode_KIND(unicode) == PyUnicode_4BYTE_KIND); - PyUnicode_4BYTE_DATA(unicode)[0] = ch; - } - return unicode; - } else { - PyErr_SetString(PyExc_IndexError, "string index out of range"); - return NULL; - } - } else { - PyObject *index_obj = CPyTagged_AsObject(index); - return PyObject_GetItem(str, index_obj); - } -} +// Tuple operations -static PyObject *CPyStr_Split(PyObject *str, PyObject *sep, CPyTagged max_split) -{ - Py_ssize_t temp_max_split = CPyTagged_AsSsize_t(max_split); - if (temp_max_split == -1 && PyErr_Occurred()) { - PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to C ssize_t"); - return NULL; - } - return PyUnicode_Split(str, sep, temp_max_split); -} -/* This does a dodgy attempt to append in place */ -static PyObject *CPyStr_Append(PyObject *o1, PyObject *o2) { - PyUnicode_Append(&o1, o2); - return o1; -} +PyObject *CPySequenceTuple_GetItem(PyObject *tuple, CPyTagged index); -static PyObject *CPyIter_Next(PyObject *iter) -{ - return (*iter->ob_type->tp_iternext)(iter); -} -static PyObject *CPy_FetchStopIterationValue(void) -{ - PyObject *val = NULL; - _PyGen_FetchStopIterationValue(&val); - return val; -} +// Exception operations -static PyObject *CPyIter_Send(PyObject *iter, PyObject *val) -{ - // Do a send, or a next if second arg is None. - // (This behavior is to match the PEP 380 spec for yield from.) - _Py_IDENTIFIER(send); - if (val == Py_None) { - return CPyIter_Next(iter); - } else { - return _PyObject_CallMethodIdObjArgs(iter, &PyId_send, val, NULL); - } -} - -static PyObject *CPy_GetCoro(PyObject *obj) -{ - // If the type has an __await__ method, call it, - // otherwise, fallback to calling __iter__. - PyAsyncMethods* async_struct = obj->ob_type->tp_as_async; - if (async_struct != NULL && async_struct->am_await != NULL) { - return (async_struct->am_await)(obj); - } else { - // TODO: We should check that the type is a generator decorated with - // asyncio.coroutine - return PyObject_GetIter(obj); - } -} - -static PyObject *CPyObject_GetAttr3(PyObject *v, PyObject *name, PyObject *defl) -{ - PyObject *result = PyObject_GetAttr(v, name); - if (!result && PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyErr_Clear(); - Py_INCREF(defl); - result = defl; - } - return result; -} - -// mypy lets ints silently coerce to floats, so a mypyc runtime float -// might be an int also -static inline bool CPyFloat_Check(PyObject *o) { - return PyFloat_Check(o) || PyLong_Check(o); -} - -static PyObject *CPyLong_FromFloat(PyObject *o) { - if (PyLong_Check(o)) { - CPy_INCREF(o); - return o; - } else { - return PyLong_FromDouble(PyFloat_AS_DOUBLE(o)); - } -} - -static PyObject *CPyLong_FromStrWithBase(PyObject *o, CPyTagged base) { - Py_ssize_t base_size_t = CPyTagged_AsSsize_t(base); - return PyLong_FromUnicodeObject(o, base_size_t); -} - -static PyObject *CPyLong_FromStr(PyObject *o) { - CPyTagged base = CPyTagged_FromSsize_t(10); - return CPyLong_FromStrWithBase(o, base); -} - -// Construct a nicely formatted type name based on __module__ and __name__. -static PyObject *CPy_GetTypeName(PyObject *type) { - PyObject *module = NULL, *name = NULL; - PyObject *full = NULL; - - module = PyObject_GetAttrString(type, "__module__"); - if (!module || !PyUnicode_Check(module)) { - goto out; - } - name = PyObject_GetAttrString(type, "__qualname__"); - if (!name || !PyUnicode_Check(name)) { - goto out; - } - - if (PyUnicode_CompareWithASCIIString(module, "builtins") == 0) { - Py_INCREF(name); - full = name; - } else { - full = PyUnicode_FromFormat("%U.%U", module, name); - } - -out: - Py_XDECREF(module); - Py_XDECREF(name); - return full; -} - - -// Get the type of a value as a string, expanding tuples to include -// all the element types. -static PyObject *CPy_FormatTypeName(PyObject *value) { - if (value == Py_None) { - return PyUnicode_FromString("None"); - } - - if (!PyTuple_CheckExact(value)) { - return CPy_GetTypeName((PyObject *)Py_TYPE(value)); - } - - if (PyTuple_GET_SIZE(value) > 10) { - return PyUnicode_FromFormat("tuple[<%d items>]", PyTuple_GET_SIZE(value)); - } - - // Most of the logic is all for tuples, which is the only interesting case - PyObject *output = PyUnicode_FromString("tuple["); - if (!output) { - return NULL; - } - /* This is quadratic but if that ever matters something is really weird. */ - int i; - for (i = 0; i < PyTuple_GET_SIZE(value); i++) { - PyObject *s = CPy_FormatTypeName(PyTuple_GET_ITEM(value, i)); - if (!s) { - Py_DECREF(output); - return NULL; - } - PyObject *next = PyUnicode_FromFormat("%U%U%s", output, s, - i + 1 == PyTuple_GET_SIZE(value) ? "]" : ", "); - Py_DECREF(output); - Py_DECREF(s); - if (!next) { - return NULL; - } - output = next; - } - return output; -} - -CPy_NOINLINE -static void CPy_TypeError(const char *expected, PyObject *value) { - PyObject *out = CPy_FormatTypeName(value); - if (out) { - PyErr_Format(PyExc_TypeError, "%s object expected; got %U", expected, out); - Py_DECREF(out); - } else { - PyErr_Format(PyExc_TypeError, "%s object expected; and errored formatting real type!", - expected); - } -} - - -#ifdef MYPYC_LOG_GETATTR -static void CPy_LogGetAttr(const char *method, PyObject *obj, PyObject *attr) { - PyObject *module = PyImport_ImportModule("getattr_hook"); - if (module) { - PyObject *res = PyObject_CallMethod(module, method, "OO", obj, attr); - Py_XDECREF(res); - Py_DECREF(module); - } - PyErr_Clear(); -} -#else -#define CPy_LogGetAttr(method, obj, attr) (void)0 -#endif - -// Intercept a method call and log it. This needs to be a macro -// because there is no API that accepts va_args for making a -// call. Worse, it needs to use the comma operator to return the right -// value. -#define CPyObject_CallMethodObjArgs(obj, attr, ...) \ - (CPy_LogGetAttr("log_method", (obj), (attr)), \ - PyObject_CallMethodObjArgs((obj), (attr), __VA_ARGS__)) - -// This one is a macro for consistency with the above, I guess. -#define CPyObject_GetAttr(obj, attr) \ - (CPy_LogGetAttr("log", (obj), (attr)), \ - PyObject_GetAttr((obj), (attr))) - - -// These functions are basically exactly PyCode_NewEmpty and -// _PyTraceback_Add which are available in all the versions we support. -// We're continuing to use them because we'll probably optimize them later. -static PyCodeObject *CPy_CreateCodeObject(const char *filename, const char *funcname, int line) { - PyObject *filename_obj = PyUnicode_FromString(filename); - PyObject *funcname_obj = PyUnicode_FromString(funcname); - PyObject *empty_bytes = PyBytes_FromStringAndSize("", 0); - PyObject *empty_tuple = PyTuple_New(0); - PyCodeObject *code_obj = NULL; - if (filename_obj == NULL || funcname_obj == NULL || empty_bytes == NULL - || empty_tuple == NULL) { - goto Error; - } - code_obj = PyCode_New(0, 0, 0, 0, 0, - empty_bytes, - empty_tuple, - empty_tuple, - empty_tuple, - empty_tuple, - empty_tuple, - filename_obj, - funcname_obj, - line, - empty_bytes); - Error: - Py_XDECREF(empty_bytes); - Py_XDECREF(empty_tuple); - Py_XDECREF(filename_obj); - Py_XDECREF(funcname_obj); - return code_obj; -} - -static void CPy_AddTraceback(const char *filename, const char *funcname, int line, - PyObject *globals) { - - PyObject *exc, *val, *tb; - PyThreadState *thread_state = PyThreadState_GET(); - PyFrameObject *frame_obj; - - // We need to save off the exception state because in 3.8, - // PyFrame_New fails if there is an error set and it fails to look - // up builtins in the globals. (_PyTraceback_Add documents that it - // needs to do it because it decodes the filename according to the - // FS encoding, which could have a decoder in Python. We don't do - // that so *that* doesn't apply to us.) - PyErr_Fetch(&exc, &val, &tb); - PyCodeObject *code_obj = CPy_CreateCodeObject(filename, funcname, line); - if (code_obj == NULL) { - goto error; - } - - frame_obj = PyFrame_New(thread_state, code_obj, globals, 0); - if (frame_obj == NULL) { - Py_DECREF(code_obj); - goto error; - } - frame_obj->f_lineno = line; - PyErr_Restore(exc, val, tb); - PyTraceBack_Here(frame_obj); - Py_DECREF(code_obj); - Py_DECREF(frame_obj); - - return; - -error: - _PyErr_ChainExceptions(exc, val, tb); -} // mypyc is not very good at dealing with refcount management of // pointers that might be NULL. As a workaround for this, the @@ -1342,90 +403,10 @@ static inline PyObject *_CPy_FromDummy(PyObject *p) { return p; } -#ifndef MYPYC_DECLARED_tuple_T3OOO -#define MYPYC_DECLARED_tuple_T3OOO -typedef struct tuple_T3OOO { - PyObject *f0; - PyObject *f1; - PyObject *f2; -} tuple_T3OOO; -static tuple_T3OOO tuple_undefined_T3OOO = { NULL, NULL, NULL }; -#endif - - -static tuple_T3OOO CPy_CatchError(void) { - // We need to return the existing sys.exc_info() information, so - // that it can be restored when we finish handling the error we - // are catching now. Grab that triple and convert NULL values to - // the ExcDummy object in order to simplify refcount handling in - // generated code. - tuple_T3OOO ret; - PyErr_GetExcInfo(&ret.f0, &ret.f1, &ret.f2); - _CPy_ToDummy(&ret.f0); - _CPy_ToDummy(&ret.f1); - _CPy_ToDummy(&ret.f2); - - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_RuntimeError, "CPy_CatchError called with no error!"); - } - - // Retrieve the error info and normalize it so that it looks like - // what python code needs it to be. - PyObject *type, *value, *traceback; - PyErr_Fetch(&type, &value, &traceback); - // Could we avoid always normalizing? - PyErr_NormalizeException(&type, &value, &traceback); - if (traceback != NULL) { - PyException_SetTraceback(value, traceback); - } - // Indicate that we are now handling this exception by stashing it - // in sys.exc_info(). mypyc routines that need access to the - // exception will read it out of there. - PyErr_SetExcInfo(type, value, traceback); - // Clear the error indicator, since the exception isn't - // propagating anymore. - PyErr_Clear(); - - return ret; -} - -static void CPy_RestoreExcInfo(tuple_T3OOO info) { - PyErr_SetExcInfo(_CPy_FromDummy(info.f0), _CPy_FromDummy(info.f1), _CPy_FromDummy(info.f2)); -} - -static void CPy_Raise(PyObject *exc) { - if (PyObject_IsInstance(exc, (PyObject *)&PyType_Type)) { - PyObject *obj = PyObject_CallFunctionObjArgs(exc, NULL); - if (!obj) - return; - PyErr_SetObject(exc, obj); - Py_DECREF(obj); - } else { - PyErr_SetObject((PyObject *)Py_TYPE(exc), exc); - } -} - -static void CPy_Reraise(void) { - PyObject *p_type, *p_value, *p_traceback; - PyErr_GetExcInfo(&p_type, &p_value, &p_traceback); - PyErr_Restore(p_type, p_value, p_traceback); -} - static int CPy_NoErrOccured(void) { return PyErr_Occurred() == NULL; } -static void CPyErr_SetObjectAndTraceback(PyObject *type, PyObject *value, PyObject *traceback) { - // Set the value and traceback of an error. Because calling - // PyErr_Restore takes away a reference to each object passed in - // as an argument, we manually increase the reference count of - // each argument before calling it. - Py_INCREF(type); - Py_INCREF(value); - Py_INCREF(traceback); - PyErr_Restore(type, value, traceback); -} - // We want to avoid the public PyErr_GetExcInfo API for these because // it requires a bunch of spurious refcount traffic on the parts of // the triple we don't care about. Unfortunately the layout of the @@ -1436,502 +417,47 @@ static void CPyErr_SetObjectAndTraceback(PyObject *type, PyObject *value, PyObje #define CPy_ExcState() PyThreadState_GET() #endif -static bool CPy_ExceptionMatches(PyObject *type) { - return PyErr_GivenExceptionMatches(CPy_ExcState()->exc_type, type); -} - -static PyObject *CPy_GetExcValue(void) { - PyObject *exc = CPy_ExcState()->exc_value; - Py_INCREF(exc); - return exc; -} - -static inline void _CPy_ToNone(PyObject **p) { - if (*p == NULL) { - Py_INCREF(Py_None); - *p = Py_None; - } -} - -static void _CPy_GetExcInfo(PyObject **p_type, PyObject **p_value, PyObject **p_traceback) { - PyErr_GetExcInfo(p_type, p_value, p_traceback); - _CPy_ToNone(p_type); - _CPy_ToNone(p_value); - _CPy_ToNone(p_traceback); -} - -static tuple_T3OOO CPy_GetExcInfo(void) { - tuple_T3OOO ret; - _CPy_GetExcInfo(&ret.f0, &ret.f1, &ret.f2); - return ret; -} - -static PyObject *CPyDict_KeysView(PyObject *dict) { - if (PyDict_CheckExact(dict)){ - return _CPyDictView_New(dict, &PyDictKeys_Type); - } - return PyObject_CallMethod(dict, "keys", NULL); -} - -static PyObject *CPyDict_ValuesView(PyObject *dict) { - if (PyDict_CheckExact(dict)){ - return _CPyDictView_New(dict, &PyDictValues_Type); - } - return PyObject_CallMethod(dict, "values", NULL); -} - -static PyObject *CPyDict_ItemsView(PyObject *dict) { - if (PyDict_CheckExact(dict)){ - return _CPyDictView_New(dict, &PyDictItems_Type); - } - return PyObject_CallMethod(dict, "items", NULL); -} - -static PyObject *CPyDict_Keys(PyObject *dict) { - if (PyDict_CheckExact(dict)) { - return PyDict_Keys(dict); - } - // Inline generic fallback logic to also return a list. - PyObject *list = PyList_New(0); - PyObject *view = PyObject_CallMethod(dict, "keys", NULL); - if (view == NULL) { - return NULL; - } - PyObject *res = _PyList_Extend((PyListObject *)list, view); - Py_DECREF(view); - if (res == NULL) { - return NULL; - } - Py_DECREF(res); - return list; -} - -static PyObject *CPyDict_Values(PyObject *dict) { - if (PyDict_CheckExact(dict)) { - return PyDict_Values(dict); - } - // Inline generic fallback logic to also return a list. - PyObject *list = PyList_New(0); - PyObject *view = PyObject_CallMethod(dict, "values", NULL); - if (view == NULL) { - return NULL; - } - PyObject *res = _PyList_Extend((PyListObject *)list, view); - Py_DECREF(view); - if (res == NULL) { - return NULL; - } - Py_DECREF(res); - return list; -} - -static PyObject *CPyDict_Items(PyObject *dict) { - if (PyDict_CheckExact(dict)) { - return PyDict_Items(dict); - } - // Inline generic fallback logic to also return a list. - PyObject *list = PyList_New(0); - PyObject *view = PyObject_CallMethod(dict, "items", NULL); - if (view == NULL) { - return NULL; - } - PyObject *res = _PyList_Extend((PyListObject *)list, view); - Py_DECREF(view); - if (res == NULL) { - return NULL; - } - Py_DECREF(res); - return list; -} - -static PyObject *CPyDict_GetKeysIter(PyObject *dict) { - if (PyDict_CheckExact(dict)) { - // Return dict itself to indicate we can use fast path instead. - Py_INCREF(dict); - return dict; - } - return PyObject_GetIter(dict); -} +void CPy_Raise(PyObject *exc); +void CPy_Reraise(void); +void CPyErr_SetObjectAndTraceback(PyObject *type, PyObject *value, PyObject *traceback); +tuple_T3OOO CPy_CatchError(void); +void CPy_RestoreExcInfo(tuple_T3OOO info); +bool CPy_ExceptionMatches(PyObject *type); +PyObject *CPy_GetExcValue(void); +tuple_T3OOO CPy_GetExcInfo(void); +void _CPy_GetExcInfo(PyObject **p_type, PyObject **p_value, PyObject **p_traceback); +void CPyError_OutOfMemory(void); +void CPy_TypeError(const char *expected, PyObject *value); +void CPy_AddTraceback(const char *filename, const char *funcname, int line, PyObject *globals); -static PyObject *CPyDict_GetItemsIter(PyObject *dict) { - if (PyDict_CheckExact(dict)) { - // Return dict itself to indicate we can use fast path instead. - Py_INCREF(dict); - return dict; - } - PyObject *view = PyObject_CallMethod(dict, "items", NULL); - if (view == NULL) { - return NULL; - } - PyObject *iter = PyObject_GetIter(view); - Py_DECREF(view); - return iter; -} -static PyObject *CPyDict_GetValuesIter(PyObject *dict) { - if (PyDict_CheckExact(dict)) { - // Return dict itself to indicate we can use fast path instead. - Py_INCREF(dict); - return dict; - } - PyObject *view = PyObject_CallMethod(dict, "values", NULL); - if (view == NULL) { - return NULL; - } - PyObject *iter = PyObject_GetIter(view); - Py_DECREF(view); - return iter; -} +// Misc operations -// Our return tuple wrapper for dictionary iteration helper. -#ifndef MYPYC_DECLARED_tuple_T3CIO -#define MYPYC_DECLARED_tuple_T3CIO -typedef struct tuple_T3CIO { - char f0; // Should continue? - CPyTagged f1; // Last dict offset - PyObject *f2; // Next dictionary key or value -} tuple_T3CIO; -static tuple_T3CIO tuple_undefined_T3CIO = { 2, CPY_INT_TAG, NULL }; -#endif - -// Same as above but for both key and value. -#ifndef MYPYC_DECLARED_tuple_T4CIOO -#define MYPYC_DECLARED_tuple_T4CIOO -typedef struct tuple_T4CIOO { - char f0; // Should continue? - CPyTagged f1; // Last dict offset - PyObject *f2; // Next dictionary key - PyObject *f3; // Next dictionary value -} tuple_T4CIOO; -static tuple_T4CIOO tuple_undefined_T4CIOO = { 2, CPY_INT_TAG, NULL, NULL }; -#endif -static void _CPyDict_FromNext(tuple_T3CIO *ret, PyObject *dict_iter) { - // Get next item from iterator and set "should continue" flag. - ret->f2 = PyIter_Next(dict_iter); - if (ret->f2 == NULL) { - ret->f0 = 0; - Py_INCREF(Py_None); - ret->f2 = Py_None; - } else { - ret->f0 = 1; - } -} - -// Helpers for fast dictionary iteration, return a single tuple -// instead of writing to multiple registers, for exact dicts use -// the fast path, and fall back to generic iterator logic for subclasses. -static tuple_T3CIO CPyDict_NextKey(PyObject *dict_or_iter, CPyTagged offset) { - tuple_T3CIO ret; - Py_ssize_t py_offset = CPyTagged_AsSsize_t(offset); - PyObject *dummy; - - if (PyDict_CheckExact(dict_or_iter)) { - ret.f0 = PyDict_Next(dict_or_iter, &py_offset, &ret.f2, &dummy); - if (ret.f0) { - ret.f1 = CPyTagged_FromSsize_t(py_offset); - } else { - // Set key to None, so mypyc can manage refcounts. - ret.f1 = 0; - ret.f2 = Py_None; - } - // PyDict_Next() returns borrowed references. - Py_INCREF(ret.f2); - } else { - // offset is dummy in this case, just use the old value. - ret.f1 = offset; - _CPyDict_FromNext(&ret, dict_or_iter); - } - return ret; -} - -static tuple_T3CIO CPyDict_NextValue(PyObject *dict_or_iter, CPyTagged offset) { - tuple_T3CIO ret; - Py_ssize_t py_offset = CPyTagged_AsSsize_t(offset); - PyObject *dummy; - - if (PyDict_CheckExact(dict_or_iter)) { - ret.f0 = PyDict_Next(dict_or_iter, &py_offset, &dummy, &ret.f2); - if (ret.f0) { - ret.f1 = CPyTagged_FromSsize_t(py_offset); - } else { - // Set value to None, so mypyc can manage refcounts. - ret.f1 = 0; - ret.f2 = Py_None; - } - // PyDict_Next() returns borrowed references. - Py_INCREF(ret.f2); - } else { - // offset is dummy in this case, just use the old value. - ret.f1 = offset; - _CPyDict_FromNext(&ret, dict_or_iter); - } - return ret; -} - -static tuple_T4CIOO CPyDict_NextItem(PyObject *dict_or_iter, CPyTagged offset) { - tuple_T4CIOO ret; - Py_ssize_t py_offset = CPyTagged_AsSsize_t(offset); - - if (PyDict_CheckExact(dict_or_iter)) { - ret.f0 = PyDict_Next(dict_or_iter, &py_offset, &ret.f2, &ret.f3); - if (ret.f0) { - ret.f1 = CPyTagged_FromSsize_t(py_offset); - } else { - // Set key and value to None, so mypyc can manage refcounts. - ret.f1 = 0; - ret.f2 = Py_None; - ret.f3 = Py_None; - } - } else { - ret.f1 = offset; - PyObject *item = PyIter_Next(dict_or_iter); - if (item == NULL || !PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 2) { - if (item != NULL) { - PyErr_SetString(PyExc_TypeError, "a tuple of length 2 expected"); - } - ret.f0 = 0; - ret.f2 = Py_None; - ret.f3 = Py_None; - } else { - ret.f0 = 1; - ret.f2 = PyTuple_GET_ITEM(item, 0); - ret.f3 = PyTuple_GET_ITEM(item, 1); - Py_DECREF(item); - } - } - // PyDict_Next() returns borrowed references. - Py_INCREF(ret.f2); - Py_INCREF(ret.f3); - return ret; -} - -// Check that dictionary didn't change size during iteration. -static inline char CPyDict_CheckSize(PyObject *dict, CPyTagged size) { - if (!PyDict_CheckExact(dict)) { - // Dict subclasses will be checked by Python runtime. - return 1; - } - Py_ssize_t py_size = CPyTagged_AsSsize_t(size); - Py_ssize_t dict_size = PyDict_Size(dict); - if (py_size != dict_size) { - PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); - return 0; - } - return 1; +// mypy lets ints silently coerce to floats, so a mypyc runtime float +// might be an int also +static inline bool CPyFloat_Check(PyObject *o) { + return PyFloat_Check(o) || PyLong_Check(o); } +PyObject *CPy_GetCoro(PyObject *obj); +PyObject *CPyIter_Send(PyObject *iter, PyObject *val); +int CPy_YieldFromErrorHandle(PyObject *iter, PyObject **outp); +PyObject *CPy_FetchStopIterationValue(void); +PyObject *CPyType_FromTemplate(PyTypeObject *template_, + PyObject *orig_bases, + PyObject *modname); +int CPyDataclass_SleightOfHand(PyObject *dataclass_dec, PyObject *tp, + PyObject *dict, PyObject *annotations); +PyObject *CPyPickle_SetState(PyObject *obj, PyObject *state); +PyObject *CPyPickle_GetState(PyObject *obj); +CPyTagged CPyTagged_Id(PyObject *o); +void CPyDebug_Print(const char *msg); void CPy_Init(void); - - -// A somewhat hairy implementation of specifically most of the error handling -// in `yield from` error handling. The point here is to reduce code size. -// -// This implements most of the bodies of the `except` blocks in the -// pseudocode in PEP 380. -// -// Returns true (1) if a StopIteration was received and we should return. -// Returns false (0) if a value should be yielded. -// In both cases the value is stored in outp. -// Signals an error (2) if the an exception should be propagated. -static int CPy_YieldFromErrorHandle(PyObject *iter, PyObject **outp) -{ - _Py_IDENTIFIER(close); - _Py_IDENTIFIER(throw); - PyObject *exc_type = CPy_ExcState()->exc_type; - PyObject *type, *value, *traceback; - PyObject *_m; - PyObject *res; - *outp = NULL; - - if (PyErr_GivenExceptionMatches(exc_type, PyExc_GeneratorExit)) { - _m = _PyObject_GetAttrId(iter, &PyId_close); - if (_m) { - res = PyObject_CallFunctionObjArgs(_m, NULL); - Py_DECREF(_m); - if (!res) - return 2; - Py_DECREF(res); - } else if (PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyErr_Clear(); - } else { - return 2; - } - } else { - _m = _PyObject_GetAttrId(iter, &PyId_throw); - if (_m) { - _CPy_GetExcInfo(&type, &value, &traceback); - res = PyObject_CallFunctionObjArgs(_m, type, value, traceback, NULL); - Py_DECREF(type); - Py_DECREF(value); - Py_DECREF(traceback); - Py_DECREF(_m); - if (res) { - *outp = res; - return 0; - } else { - res = CPy_FetchStopIterationValue(); - if (res) { - *outp = res; - return 1; - } - } - } else if (PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyErr_Clear(); - } else { - return 2; - } - } - - CPy_Reraise(); - return 2; -} - -static int _CPy_UpdateObjFromDict(PyObject *obj, PyObject *dict) -{ - Py_ssize_t pos = 0; - PyObject *key, *value; - while (PyDict_Next(dict, &pos, &key, &value)) { - if (PyObject_SetAttr(obj, key, value) != 0) { - return -1; - } - } - return 0; -} - -// Support for pickling; reusable getstate and setstate functions -static PyObject * -CPyPickle_SetState(PyObject *obj, PyObject *state) -{ - if (_CPy_UpdateObjFromDict(obj, state) != 0) { - return NULL; - } - Py_RETURN_NONE; -} - -static PyObject * -CPyPickle_GetState(PyObject *obj) -{ - PyObject *attrs = NULL, *state = NULL; - - attrs = PyObject_GetAttrString((PyObject *)Py_TYPE(obj), "__mypyc_attrs__"); - if (!attrs) { - goto fail; - } - if (!PyTuple_Check(attrs)) { - PyErr_SetString(PyExc_TypeError, "__mypyc_attrs__ is not a tuple"); - goto fail; - } - state = PyDict_New(); - if (!state) { - goto fail; - } - - // Collect all the values of attributes in __mypyc_attrs__ - // Attributes that are missing we just ignore - int i; - for (i = 0; i < PyTuple_GET_SIZE(attrs); i++) { - PyObject *key = PyTuple_GET_ITEM(attrs, i); - PyObject *value = PyObject_GetAttr(obj, key); - if (!value) { - if (PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyErr_Clear(); - continue; - } - goto fail; - } - int result = PyDict_SetItem(state, key, value); - Py_DECREF(value); - if (result != 0) { - goto fail; - } - } - - Py_DECREF(attrs); - - return state; -fail: - Py_XDECREF(attrs); - Py_XDECREF(state); - return NULL; -} - -/* Support for our partial built-in support for dataclasses. - * - * Take a class we want to make a dataclass, remove any descriptors - * for annotated attributes, swap in the actual values of the class - * variables invoke dataclass, and then restore all of the - * descriptors. - * - * The purpose of all this is that dataclasses uses the values of - * class variables to drive which attributes are required and what the - * default values/factories are for optional attributes. This means - * that the class dict needs to contain those values instead of getset - * descriptors for the attributes when we invoke dataclass. - * - * We need to remove descriptors for attributes even when there is no - * default value for them, or else dataclass will think the descriptor - * is the default value. We remove only the attributes, since we don't - * want dataclasses to try generating functions when they are already - * implemented. - * - * Args: - * dataclass_dec: The decorator to apply - * tp: The class we are making a dataclass - * dict: The dictionary containing values that dataclasses needs - * annotations: The type annotation dictionary - */ -static int -CPyDataclass_SleightOfHand(PyObject *dataclass_dec, PyObject *tp, - PyObject *dict, PyObject *annotations) { - PyTypeObject *ttp = (PyTypeObject *)tp; - Py_ssize_t pos; - PyObject *res; - - /* Make a copy of the original class __dict__ */ - PyObject *orig_dict = PyDict_Copy(ttp->tp_dict); - if (!orig_dict) { - goto fail; - } - - /* Delete anything that had an annotation */ - pos = 0; - PyObject *key; - while (PyDict_Next(annotations, &pos, &key, NULL)) { - if (PyObject_DelAttr(tp, key) != 0) { - goto fail; - } - } - - /* Copy in all the attributes that we want dataclass to see */ - if (_CPy_UpdateObjFromDict(tp, dict) != 0) { - goto fail; - } - - /* Run the @dataclass descriptor */ - res = PyObject_CallFunctionObjArgs(dataclass_dec, tp, NULL); - if (!res) { - goto fail; - } - Py_DECREF(res); - - /* Copy back the original contents of the dict */ - if (_CPy_UpdateObjFromDict(tp, orig_dict) != 0) { - goto fail; - } - - Py_DECREF(orig_dict); - return 1; - -fail: - Py_XDECREF(orig_dict); - return 0; -} - - int CPyArg_ParseTupleAndKeywords(PyObject *, PyObject *, const char *, char **, ...); + #ifdef __cplusplus } #endif diff --git a/mypyc/lib-rt/dict_ops.c b/mypyc/lib-rt/dict_ops.c new file mode 100644 index 000000000000..b201313f0c93 --- /dev/null +++ b/mypyc/lib-rt/dict_ops.c @@ -0,0 +1,362 @@ +// Dict primitive operations +// +// These are registered in mypyc.primitives.dict_ops. + +#include +#include "CPy.h" + +// Dict subclasses like defaultdict override things in interesting +// ways, so we don't want to just directly use the dict methods. Not +// sure if it is actually worth doing all this stuff, but it saves +// some indirections. +PyObject *CPyDict_GetItem(PyObject *dict, PyObject *key) { + if (PyDict_CheckExact(dict)) { + PyObject *res = PyDict_GetItemWithError(dict, key); + if (!res) { + if (!PyErr_Occurred()) { + PyErr_SetObject(PyExc_KeyError, key); + } + } else { + Py_INCREF(res); + } + return res; + } else { + return PyObject_GetItem(dict, key); + } +} + +PyObject *CPyDict_Build(Py_ssize_t size, ...) { + Py_ssize_t i; + + PyObject *res = _PyDict_NewPresized(size); + if (res == NULL) { + return NULL; + } + + va_list args; + va_start(args, size); + + for (i = 0; i < size; i++) { + PyObject *key = va_arg(args, PyObject *); + PyObject *value = va_arg(args, PyObject *); + if (PyDict_SetItem(res, key, value)) { + Py_DECREF(res); + return NULL; + } + } + + va_end(args); + return res; +} + +PyObject *CPyDict_Get(PyObject *dict, PyObject *key, PyObject *fallback) { + // We are dodgily assuming that get on a subclass doesn't have + // different behavior. + PyObject *res = PyDict_GetItemWithError(dict, key); + if (!res) { + if (PyErr_Occurred()) { + return NULL; + } + res = fallback; + } + Py_INCREF(res); + return res; +} + +int CPyDict_SetItem(PyObject *dict, PyObject *key, PyObject *value) { + if (PyDict_CheckExact(dict)) { + return PyDict_SetItem(dict, key, value); + } else { + return PyObject_SetItem(dict, key, value); + } +} + +static inline int CPy_ObjectToStatus(PyObject *obj) { + if (obj) { + Py_DECREF(obj); + return 0; + } else { + return -1; + } +} + +static int CPyDict_UpdateGeneral(PyObject *dict, PyObject *stuff) { + _Py_IDENTIFIER(update); + PyObject *res = _PyObject_CallMethodIdObjArgs(dict, &PyId_update, stuff, NULL); + return CPy_ObjectToStatus(res); +} + +int CPyDict_UpdateInDisplay(PyObject *dict, PyObject *stuff) { + // from https://github.com/python/cpython/blob/55d035113dfb1bd90495c8571758f504ae8d4802/Python/ceval.c#L2710 + int ret = PyDict_Update(dict, stuff); + if (ret < 0) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Format(PyExc_TypeError, + "'%.200s' object is not a mapping", + stuff->ob_type->tp_name); + } + } + return ret; +} + +int CPyDict_Update(PyObject *dict, PyObject *stuff) { + if (PyDict_CheckExact(dict)) { + return PyDict_Update(dict, stuff); + } else { + return CPyDict_UpdateGeneral(dict, stuff); + } +} + +int CPyDict_UpdateFromAny(PyObject *dict, PyObject *stuff) { + if (PyDict_CheckExact(dict)) { + // Argh this sucks + _Py_IDENTIFIER(keys); + if (PyDict_Check(stuff) || _PyObject_HasAttrId(stuff, &PyId_keys)) { + return PyDict_Update(dict, stuff); + } else { + return PyDict_MergeFromSeq2(dict, stuff, 1); + } + } else { + return CPyDict_UpdateGeneral(dict, stuff); + } +} + +PyObject *CPyDict_FromAny(PyObject *obj) { + if (PyDict_Check(obj)) { + return PyDict_Copy(obj); + } else { + int res; + PyObject *dict = PyDict_New(); + if (!dict) { + return NULL; + } + _Py_IDENTIFIER(keys); + if (_PyObject_HasAttrId(obj, &PyId_keys)) { + res = PyDict_Update(dict, obj); + } else { + res = PyDict_MergeFromSeq2(dict, obj, 1); + } + if (res < 0) { + Py_DECREF(dict); + return NULL; + } + return dict; + } +} + +PyObject *CPyDict_KeysView(PyObject *dict) { + if (PyDict_CheckExact(dict)){ + return _CPyDictView_New(dict, &PyDictKeys_Type); + } + return PyObject_CallMethod(dict, "keys", NULL); +} + +PyObject *CPyDict_ValuesView(PyObject *dict) { + if (PyDict_CheckExact(dict)){ + return _CPyDictView_New(dict, &PyDictValues_Type); + } + return PyObject_CallMethod(dict, "values", NULL); +} + +PyObject *CPyDict_ItemsView(PyObject *dict) { + if (PyDict_CheckExact(dict)){ + return _CPyDictView_New(dict, &PyDictItems_Type); + } + return PyObject_CallMethod(dict, "items", NULL); +} + +PyObject *CPyDict_Keys(PyObject *dict) { + if (PyDict_CheckExact(dict)) { + return PyDict_Keys(dict); + } + // Inline generic fallback logic to also return a list. + PyObject *list = PyList_New(0); + PyObject *view = PyObject_CallMethod(dict, "keys", NULL); + if (view == NULL) { + return NULL; + } + PyObject *res = _PyList_Extend((PyListObject *)list, view); + Py_DECREF(view); + if (res == NULL) { + return NULL; + } + Py_DECREF(res); + return list; +} + +PyObject *CPyDict_Values(PyObject *dict) { + if (PyDict_CheckExact(dict)) { + return PyDict_Values(dict); + } + // Inline generic fallback logic to also return a list. + PyObject *list = PyList_New(0); + PyObject *view = PyObject_CallMethod(dict, "values", NULL); + if (view == NULL) { + return NULL; + } + PyObject *res = _PyList_Extend((PyListObject *)list, view); + Py_DECREF(view); + if (res == NULL) { + return NULL; + } + Py_DECREF(res); + return list; +} + +PyObject *CPyDict_Items(PyObject *dict) { + if (PyDict_CheckExact(dict)) { + return PyDict_Items(dict); + } + // Inline generic fallback logic to also return a list. + PyObject *list = PyList_New(0); + PyObject *view = PyObject_CallMethod(dict, "items", NULL); + if (view == NULL) { + return NULL; + } + PyObject *res = _PyList_Extend((PyListObject *)list, view); + Py_DECREF(view); + if (res == NULL) { + return NULL; + } + Py_DECREF(res); + return list; +} + +PyObject *CPyDict_GetKeysIter(PyObject *dict) { + if (PyDict_CheckExact(dict)) { + // Return dict itself to indicate we can use fast path instead. + Py_INCREF(dict); + return dict; + } + return PyObject_GetIter(dict); +} + +PyObject *CPyDict_GetItemsIter(PyObject *dict) { + if (PyDict_CheckExact(dict)) { + // Return dict itself to indicate we can use fast path instead. + Py_INCREF(dict); + return dict; + } + PyObject *view = PyObject_CallMethod(dict, "items", NULL); + if (view == NULL) { + return NULL; + } + PyObject *iter = PyObject_GetIter(view); + Py_DECREF(view); + return iter; +} + +PyObject *CPyDict_GetValuesIter(PyObject *dict) { + if (PyDict_CheckExact(dict)) { + // Return dict itself to indicate we can use fast path instead. + Py_INCREF(dict); + return dict; + } + PyObject *view = PyObject_CallMethod(dict, "values", NULL); + if (view == NULL) { + return NULL; + } + PyObject *iter = PyObject_GetIter(view); + Py_DECREF(view); + return iter; +} + +static void _CPyDict_FromNext(tuple_T3CIO *ret, PyObject *dict_iter) { + // Get next item from iterator and set "should continue" flag. + ret->f2 = PyIter_Next(dict_iter); + if (ret->f2 == NULL) { + ret->f0 = 0; + Py_INCREF(Py_None); + ret->f2 = Py_None; + } else { + ret->f0 = 1; + } +} + +// Helpers for fast dictionary iteration, return a single tuple +// instead of writing to multiple registers, for exact dicts use +// the fast path, and fall back to generic iterator logic for subclasses. +tuple_T3CIO CPyDict_NextKey(PyObject *dict_or_iter, CPyTagged offset) { + tuple_T3CIO ret; + Py_ssize_t py_offset = CPyTagged_AsSsize_t(offset); + PyObject *dummy; + + if (PyDict_CheckExact(dict_or_iter)) { + ret.f0 = PyDict_Next(dict_or_iter, &py_offset, &ret.f2, &dummy); + if (ret.f0) { + ret.f1 = CPyTagged_FromSsize_t(py_offset); + } else { + // Set key to None, so mypyc can manage refcounts. + ret.f1 = 0; + ret.f2 = Py_None; + } + // PyDict_Next() returns borrowed references. + Py_INCREF(ret.f2); + } else { + // offset is dummy in this case, just use the old value. + ret.f1 = offset; + _CPyDict_FromNext(&ret, dict_or_iter); + } + return ret; +} + +tuple_T3CIO CPyDict_NextValue(PyObject *dict_or_iter, CPyTagged offset) { + tuple_T3CIO ret; + Py_ssize_t py_offset = CPyTagged_AsSsize_t(offset); + PyObject *dummy; + + if (PyDict_CheckExact(dict_or_iter)) { + ret.f0 = PyDict_Next(dict_or_iter, &py_offset, &dummy, &ret.f2); + if (ret.f0) { + ret.f1 = CPyTagged_FromSsize_t(py_offset); + } else { + // Set value to None, so mypyc can manage refcounts. + ret.f1 = 0; + ret.f2 = Py_None; + } + // PyDict_Next() returns borrowed references. + Py_INCREF(ret.f2); + } else { + // offset is dummy in this case, just use the old value. + ret.f1 = offset; + _CPyDict_FromNext(&ret, dict_or_iter); + } + return ret; +} + +tuple_T4CIOO CPyDict_NextItem(PyObject *dict_or_iter, CPyTagged offset) { + tuple_T4CIOO ret; + Py_ssize_t py_offset = CPyTagged_AsSsize_t(offset); + + if (PyDict_CheckExact(dict_or_iter)) { + ret.f0 = PyDict_Next(dict_or_iter, &py_offset, &ret.f2, &ret.f3); + if (ret.f0) { + ret.f1 = CPyTagged_FromSsize_t(py_offset); + } else { + // Set key and value to None, so mypyc can manage refcounts. + ret.f1 = 0; + ret.f2 = Py_None; + ret.f3 = Py_None; + } + } else { + ret.f1 = offset; + PyObject *item = PyIter_Next(dict_or_iter); + if (item == NULL || !PyTuple_Check(item) || PyTuple_GET_SIZE(item) != 2) { + if (item != NULL) { + PyErr_SetString(PyExc_TypeError, "a tuple of length 2 expected"); + } + ret.f0 = 0; + ret.f2 = Py_None; + ret.f3 = Py_None; + } else { + ret.f0 = 1; + ret.f2 = PyTuple_GET_ITEM(item, 0); + ret.f3 = PyTuple_GET_ITEM(item, 1); + Py_DECREF(item); + } + } + // PyDict_Next() returns borrowed references. + Py_INCREF(ret.f2); + Py_INCREF(ret.f3); + return ret; +} diff --git a/mypyc/lib-rt/exc_ops.c b/mypyc/lib-rt/exc_ops.c new file mode 100644 index 000000000000..50f01f2e4e7e --- /dev/null +++ b/mypyc/lib-rt/exc_ops.c @@ -0,0 +1,256 @@ +// Exception related primitive operations +// +// These are registered in mypyc.primitives.exc_ops. + +#include +#include "CPy.h" + +void CPy_Raise(PyObject *exc) { + if (PyObject_IsInstance(exc, (PyObject *)&PyType_Type)) { + PyObject *obj = PyObject_CallFunctionObjArgs(exc, NULL); + if (!obj) + return; + PyErr_SetObject(exc, obj); + Py_DECREF(obj); + } else { + PyErr_SetObject((PyObject *)Py_TYPE(exc), exc); + } +} + +void CPy_Reraise(void) { + PyObject *p_type, *p_value, *p_traceback; + PyErr_GetExcInfo(&p_type, &p_value, &p_traceback); + PyErr_Restore(p_type, p_value, p_traceback); +} + +void CPyErr_SetObjectAndTraceback(PyObject *type, PyObject *value, PyObject *traceback) { + // Set the value and traceback of an error. Because calling + // PyErr_Restore takes away a reference to each object passed in + // as an argument, we manually increase the reference count of + // each argument before calling it. + Py_INCREF(type); + Py_INCREF(value); + Py_INCREF(traceback); + PyErr_Restore(type, value, traceback); +} + +tuple_T3OOO CPy_CatchError(void) { + // We need to return the existing sys.exc_info() information, so + // that it can be restored when we finish handling the error we + // are catching now. Grab that triple and convert NULL values to + // the ExcDummy object in order to simplify refcount handling in + // generated code. + tuple_T3OOO ret; + PyErr_GetExcInfo(&ret.f0, &ret.f1, &ret.f2); + _CPy_ToDummy(&ret.f0); + _CPy_ToDummy(&ret.f1); + _CPy_ToDummy(&ret.f2); + + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_RuntimeError, "CPy_CatchError called with no error!"); + } + + // Retrieve the error info and normalize it so that it looks like + // what python code needs it to be. + PyObject *type, *value, *traceback; + PyErr_Fetch(&type, &value, &traceback); + // Could we avoid always normalizing? + PyErr_NormalizeException(&type, &value, &traceback); + if (traceback != NULL) { + PyException_SetTraceback(value, traceback); + } + // Indicate that we are now handling this exception by stashing it + // in sys.exc_info(). mypyc routines that need access to the + // exception will read it out of there. + PyErr_SetExcInfo(type, value, traceback); + // Clear the error indicator, since the exception isn't + // propagating anymore. + PyErr_Clear(); + + return ret; +} + +void CPy_RestoreExcInfo(tuple_T3OOO info) { + PyErr_SetExcInfo(_CPy_FromDummy(info.f0), _CPy_FromDummy(info.f1), _CPy_FromDummy(info.f2)); +} + +bool CPy_ExceptionMatches(PyObject *type) { + return PyErr_GivenExceptionMatches(CPy_ExcState()->exc_type, type); +} + +PyObject *CPy_GetExcValue(void) { + PyObject *exc = CPy_ExcState()->exc_value; + Py_INCREF(exc); + return exc; +} + +static inline void _CPy_ToNone(PyObject **p) { + if (*p == NULL) { + Py_INCREF(Py_None); + *p = Py_None; + } +} + +void _CPy_GetExcInfo(PyObject **p_type, PyObject **p_value, PyObject **p_traceback) { + PyErr_GetExcInfo(p_type, p_value, p_traceback); + _CPy_ToNone(p_type); + _CPy_ToNone(p_value); + _CPy_ToNone(p_traceback); +} + +tuple_T3OOO CPy_GetExcInfo(void) { + tuple_T3OOO ret; + _CPy_GetExcInfo(&ret.f0, &ret.f1, &ret.f2); + return ret; +} + +void CPyError_OutOfMemory(void) { + fprintf(stderr, "fatal: out of memory\n"); + fflush(stderr); + abort(); +} + +// Construct a nicely formatted type name based on __module__ and __name__. +static PyObject *CPy_GetTypeName(PyObject *type) { + PyObject *module = NULL, *name = NULL; + PyObject *full = NULL; + + module = PyObject_GetAttrString(type, "__module__"); + if (!module || !PyUnicode_Check(module)) { + goto out; + } + name = PyObject_GetAttrString(type, "__qualname__"); + if (!name || !PyUnicode_Check(name)) { + goto out; + } + + if (PyUnicode_CompareWithASCIIString(module, "builtins") == 0) { + Py_INCREF(name); + full = name; + } else { + full = PyUnicode_FromFormat("%U.%U", module, name); + } + +out: + Py_XDECREF(module); + Py_XDECREF(name); + return full; +} + +// Get the type of a value as a string, expanding tuples to include +// all the element types. +static PyObject *CPy_FormatTypeName(PyObject *value) { + if (value == Py_None) { + return PyUnicode_FromString("None"); + } + + if (!PyTuple_CheckExact(value)) { + return CPy_GetTypeName((PyObject *)Py_TYPE(value)); + } + + if (PyTuple_GET_SIZE(value) > 10) { + return PyUnicode_FromFormat("tuple[<%d items>]", PyTuple_GET_SIZE(value)); + } + + // Most of the logic is all for tuples, which is the only interesting case + PyObject *output = PyUnicode_FromString("tuple["); + if (!output) { + return NULL; + } + /* This is quadratic but if that ever matters something is really weird. */ + int i; + for (i = 0; i < PyTuple_GET_SIZE(value); i++) { + PyObject *s = CPy_FormatTypeName(PyTuple_GET_ITEM(value, i)); + if (!s) { + Py_DECREF(output); + return NULL; + } + PyObject *next = PyUnicode_FromFormat("%U%U%s", output, s, + i + 1 == PyTuple_GET_SIZE(value) ? "]" : ", "); + Py_DECREF(output); + Py_DECREF(s); + if (!next) { + return NULL; + } + output = next; + } + return output; +} + +CPy_NOINLINE +void CPy_TypeError(const char *expected, PyObject *value) { + PyObject *out = CPy_FormatTypeName(value); + if (out) { + PyErr_Format(PyExc_TypeError, "%s object expected; got %U", expected, out); + Py_DECREF(out); + } else { + PyErr_Format(PyExc_TypeError, "%s object expected; and errored formatting real type!", + expected); + } +} + +// These functions are basically exactly PyCode_NewEmpty and +// _PyTraceback_Add which are available in all the versions we support. +// We're continuing to use them because we'll probably optimize them later. +static PyCodeObject *CPy_CreateCodeObject(const char *filename, const char *funcname, int line) { + PyObject *filename_obj = PyUnicode_FromString(filename); + PyObject *funcname_obj = PyUnicode_FromString(funcname); + PyObject *empty_bytes = PyBytes_FromStringAndSize("", 0); + PyObject *empty_tuple = PyTuple_New(0); + PyCodeObject *code_obj = NULL; + if (filename_obj == NULL || funcname_obj == NULL || empty_bytes == NULL + || empty_tuple == NULL) { + goto Error; + } + code_obj = PyCode_New(0, 0, 0, 0, 0, + empty_bytes, + empty_tuple, + empty_tuple, + empty_tuple, + empty_tuple, + empty_tuple, + filename_obj, + funcname_obj, + line, + empty_bytes); + Error: + Py_XDECREF(empty_bytes); + Py_XDECREF(empty_tuple); + Py_XDECREF(filename_obj); + Py_XDECREF(funcname_obj); + return code_obj; +} + +void CPy_AddTraceback(const char *filename, const char *funcname, int line, PyObject *globals) { + PyObject *exc, *val, *tb; + PyThreadState *thread_state = PyThreadState_GET(); + PyFrameObject *frame_obj; + + // We need to save off the exception state because in 3.8, + // PyFrame_New fails if there is an error set and it fails to look + // up builtins in the globals. (_PyTraceback_Add documents that it + // needs to do it because it decodes the filename according to the + // FS encoding, which could have a decoder in Python. We don't do + // that so *that* doesn't apply to us.) + PyErr_Fetch(&exc, &val, &tb); + PyCodeObject *code_obj = CPy_CreateCodeObject(filename, funcname, line); + if (code_obj == NULL) { + goto error; + } + + frame_obj = PyFrame_New(thread_state, code_obj, globals, 0); + if (frame_obj == NULL) { + Py_DECREF(code_obj); + goto error; + } + frame_obj->f_lineno = line; + PyErr_Restore(exc, val, tb); + PyTraceBack_Here(frame_obj); + Py_DECREF(code_obj); + Py_DECREF(frame_obj); + + return; + +error: + _PyErr_ChainExceptions(exc, val, tb); +} diff --git a/mypyc/lib-rt/generic_ops.c b/mypyc/lib-rt/generic_ops.c new file mode 100644 index 000000000000..685e214ca793 --- /dev/null +++ b/mypyc/lib-rt/generic_ops.c @@ -0,0 +1,37 @@ +// Generic primitive operations +// +// These are registered in mypyc.primitives.generic_ops. + +#include +#include "CPy.h" + +CPyTagged CPyObject_Hash(PyObject *o) { + Py_hash_t h = PyObject_Hash(o); + if (h == -1) { + return CPY_INT_TAG; + } else { + // This is tragically annoying. The range of hash values in + // 64-bit python covers 64-bits, and our short integers only + // cover 63. This means that half the time we are boxing the + // result for basically no good reason. To add insult to + // injury it is probably about to be immediately unboxed by a + // tp_hash wrapper. + return CPyTagged_FromSsize_t(h); + } +} + +PyObject *CPyObject_GetAttr3(PyObject *v, PyObject *name, PyObject *defl) +{ + PyObject *result = PyObject_GetAttr(v, name); + if (!result && PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + Py_INCREF(defl); + result = defl; + } + return result; +} + +PyObject *CPyIter_Next(PyObject *iter) +{ + return (*iter->ob_type->tp_iternext)(iter); +} diff --git a/mypyc/lib-rt/CPy.c b/mypyc/lib-rt/init.c similarity index 62% rename from mypyc/lib-rt/CPy.c rename to mypyc/lib-rt/init.c index b7c832f6a73f..01b133233489 100644 --- a/mypyc/lib-rt/CPy.c +++ b/mypyc/lib-rt/init.c @@ -1,13 +1,6 @@ -#include #include -#include -#include #include "CPy.h" -// TODO: Currently only the things that *need* to be defined a single time -// instead of copied into every module live here. This is silly, and most -// of the code in CPy.h and pythonsupport.h should move here. - struct ExcDummyStruct _CPy_ExcDummyStruct = { PyObject_HEAD_INIT(NULL) }; PyObject *_CPy_ExcDummy = (PyObject *)&_CPy_ExcDummyStruct; diff --git a/mypyc/lib-rt/int_ops.c b/mypyc/lib-rt/int_ops.c new file mode 100644 index 000000000000..371e5de065b3 --- /dev/null +++ b/mypyc/lib-rt/int_ops.c @@ -0,0 +1,275 @@ +// Int primitive operations +// +// These are registered in mypyc.primitives.int_ops. + +#include +#include "CPy.h" + +CPyTagged CPyTagged_FromSsize_t(Py_ssize_t value) { + // We use a Python object if the value shifted left by 1 is too + // large for Py_ssize_t + if (CPyTagged_TooBig(value)) { + PyObject *object = PyLong_FromSsize_t(value); + return ((CPyTagged)object) | CPY_INT_TAG; + } else { + return value << 1; + } +} + +CPyTagged CPyTagged_FromObject(PyObject *object) { + int overflow; + // The overflow check knows about CPyTagged's width + Py_ssize_t value = CPyLong_AsSsize_tAndOverflow(object, &overflow); + if (overflow != 0) { + Py_INCREF(object); + return ((CPyTagged)object) | CPY_INT_TAG; + } else { + return value << 1; + } +} + +CPyTagged CPyTagged_StealFromObject(PyObject *object) { + int overflow; + // The overflow check knows about CPyTagged's width + Py_ssize_t value = CPyLong_AsSsize_tAndOverflow(object, &overflow); + if (overflow != 0) { + return ((CPyTagged)object) | CPY_INT_TAG; + } else { + Py_DECREF(object); + return value << 1; + } +} + +CPyTagged CPyTagged_BorrowFromObject(PyObject *object) { + int overflow; + // The overflow check knows about CPyTagged's width + Py_ssize_t value = CPyLong_AsSsize_tAndOverflow(object, &overflow); + if (overflow != 0) { + return ((CPyTagged)object) | CPY_INT_TAG; + } else { + return value << 1; + } +} + +PyObject *CPyTagged_AsObject(CPyTagged x) { + PyObject *value; + if (CPyTagged_CheckLong(x)) { + value = CPyTagged_LongAsObject(x); + Py_INCREF(value); + } else { + value = PyLong_FromSsize_t(CPyTagged_ShortAsSsize_t(x)); + if (value == NULL) { + CPyError_OutOfMemory(); + } + } + return value; +} + +PyObject *CPyTagged_StealAsObject(CPyTagged x) { + PyObject *value; + if (CPyTagged_CheckLong(x)) { + value = CPyTagged_LongAsObject(x); + } else { + value = PyLong_FromSsize_t(CPyTagged_ShortAsSsize_t(x)); + if (value == NULL) { + CPyError_OutOfMemory(); + } + } + return value; +} + +Py_ssize_t CPyTagged_AsSsize_t(CPyTagged x) { + if (CPyTagged_CheckShort(x)) { + return CPyTagged_ShortAsSsize_t(x); + } else { + return PyLong_AsSsize_t(CPyTagged_LongAsObject(x)); + } +} + +CPy_NOINLINE +void CPyTagged_IncRef(CPyTagged x) { + if (CPyTagged_CheckLong(x)) { + Py_INCREF(CPyTagged_LongAsObject(x)); + } +} + +CPy_NOINLINE +void CPyTagged_DecRef(CPyTagged x) { + if (CPyTagged_CheckLong(x)) { + Py_DECREF(CPyTagged_LongAsObject(x)); + } +} + +CPy_NOINLINE +void CPyTagged_XDecRef(CPyTagged x) { + if (CPyTagged_CheckLong(x)) { + Py_XDECREF(CPyTagged_LongAsObject(x)); + } +} + +CPyTagged CPyTagged_Negate(CPyTagged num) { + if (CPyTagged_CheckShort(num) + && num != (CPyTagged) ((Py_ssize_t)1 << (CPY_INT_BITS - 1))) { + // The only possibility of an overflow error happening when negating a short is if we + // attempt to negate the most negative number. + return -num; + } + PyObject *num_obj = CPyTagged_AsObject(num); + PyObject *result = PyNumber_Negative(num_obj); + if (result == NULL) { + CPyError_OutOfMemory(); + } + Py_DECREF(num_obj); + return CPyTagged_StealFromObject(result); +} + +CPyTagged CPyTagged_Add(CPyTagged left, CPyTagged right) { + // TODO: Use clang/gcc extension __builtin_saddll_overflow instead. + if (CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right)) { + CPyTagged sum = left + right; + if (!CPyTagged_IsAddOverflow(sum, left, right)) { + return sum; + } + } + PyObject *left_obj = CPyTagged_AsObject(left); + PyObject *right_obj = CPyTagged_AsObject(right); + PyObject *result = PyNumber_Add(left_obj, right_obj); + if (result == NULL) { + CPyError_OutOfMemory(); + } + Py_DECREF(left_obj); + Py_DECREF(right_obj); + return CPyTagged_StealFromObject(result); +} + +CPyTagged CPyTagged_Subtract(CPyTagged left, CPyTagged right) { + // TODO: Use clang/gcc extension __builtin_saddll_overflow instead. + if (CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right)) { + CPyTagged diff = left - right; + if (!CPyTagged_IsSubtractOverflow(diff, left, right)) { + return diff; + } + } + PyObject *left_obj = CPyTagged_AsObject(left); + PyObject *right_obj = CPyTagged_AsObject(right); + PyObject *result = PyNumber_Subtract(left_obj, right_obj); + if (result == NULL) { + CPyError_OutOfMemory(); + } + Py_DECREF(left_obj); + Py_DECREF(right_obj); + return CPyTagged_StealFromObject(result); +} + +CPyTagged CPyTagged_Multiply(CPyTagged left, CPyTagged right) { + // TODO: Consider using some clang/gcc extension + if (CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right)) { + if (!CPyTagged_IsMultiplyOverflow(left, right)) { + return left * CPyTagged_ShortAsSsize_t(right); + } + } + PyObject *left_obj = CPyTagged_AsObject(left); + PyObject *right_obj = CPyTagged_AsObject(right); + PyObject *result = PyNumber_Multiply(left_obj, right_obj); + if (result == NULL) { + CPyError_OutOfMemory(); + } + Py_DECREF(left_obj); + Py_DECREF(right_obj); + return CPyTagged_StealFromObject(result); +} + +CPyTagged CPyTagged_FloorDivide(CPyTagged left, CPyTagged right) { + if (CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right) + && !CPyTagged_MaybeFloorDivideFault(left, right)) { + Py_ssize_t result = ((Py_ssize_t)left / CPyTagged_ShortAsSsize_t(right)) & ~1; + if (((Py_ssize_t)left < 0) != (((Py_ssize_t)right) < 0)) { + if (result / 2 * right != left) { + // Round down + result -= 2; + } + } + return result; + } + PyObject *left_obj = CPyTagged_AsObject(left); + PyObject *right_obj = CPyTagged_AsObject(right); + PyObject *result = PyNumber_FloorDivide(left_obj, right_obj); + Py_DECREF(left_obj); + Py_DECREF(right_obj); + // Handle exceptions honestly because it could be ZeroDivisionError + if (result == NULL) { + return CPY_INT_TAG; + } else { + return CPyTagged_StealFromObject(result); + } +} + +CPyTagged CPyTagged_Remainder(CPyTagged left, CPyTagged right) { + if (CPyTagged_CheckShort(left) && CPyTagged_CheckShort(right) + && !CPyTagged_MaybeRemainderFault(left, right)) { + Py_ssize_t result = (Py_ssize_t)left % (Py_ssize_t)right; + if (((Py_ssize_t)right < 0) != ((Py_ssize_t)left < 0) && result != 0) { + result += right; + } + return result; + } + PyObject *left_obj = CPyTagged_AsObject(left); + PyObject *right_obj = CPyTagged_AsObject(right); + PyObject *result = PyNumber_Remainder(left_obj, right_obj); + Py_DECREF(left_obj); + Py_DECREF(right_obj); + // Handle exceptions honestly because it could be ZeroDivisionError + if (result == NULL) { + return CPY_INT_TAG; + } else { + return CPyTagged_StealFromObject(result); + } +} + +bool CPyTagged_IsEq_(CPyTagged left, CPyTagged right) { + if (CPyTagged_CheckShort(right)) { + return false; + } else { + int result = PyObject_RichCompareBool(CPyTagged_LongAsObject(left), + CPyTagged_LongAsObject(right), Py_EQ); + if (result == -1) { + CPyError_OutOfMemory(); + } + return result; + } +} + +bool CPyTagged_IsLt_(CPyTagged left, CPyTagged right) { + PyObject *left_obj = CPyTagged_AsObject(left); + PyObject *right_obj = CPyTagged_AsObject(right); + int result = PyObject_RichCompareBool(left_obj, right_obj, Py_LT); + Py_DECREF(left_obj); + Py_DECREF(right_obj); + if (result == -1) { + CPyError_OutOfMemory(); + } + return result; +} + +PyObject *CPyLong_FromStrWithBase(PyObject *o, CPyTagged base) { + Py_ssize_t base_size_t = CPyTagged_AsSsize_t(base); + return PyLong_FromUnicodeObject(o, base_size_t); +} + +PyObject *CPyLong_FromStr(PyObject *o) { + CPyTagged base = CPyTagged_FromSsize_t(10); + return CPyLong_FromStrWithBase(o, base); +} + +PyObject *CPyLong_FromFloat(PyObject *o) { + if (PyLong_Check(o)) { + CPy_INCREF(o); + return o; + } else { + return PyLong_FromDouble(PyFloat_AS_DOUBLE(o)); + } +} + +PyObject *CPyBool_Str(bool b) { + return PyObject_Str(b ? Py_True : Py_False); +} diff --git a/mypyc/lib-rt/list_ops.c b/mypyc/lib-rt/list_ops.c new file mode 100644 index 000000000000..92fa3228d398 --- /dev/null +++ b/mypyc/lib-rt/list_ops.c @@ -0,0 +1,125 @@ +// List primitive operations +// +// These are registered in mypyc.primitives.list_ops. + +#include +#include "CPy.h" + +PyObject *CPyList_GetItemUnsafe(PyObject *list, CPyTagged index) { + Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); + PyObject *result = PyList_GET_ITEM(list, n); + Py_INCREF(result); + return result; +} + +PyObject *CPyList_GetItemShort(PyObject *list, CPyTagged index) { + Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); + Py_ssize_t size = PyList_GET_SIZE(list); + if (n >= 0) { + if (n >= size) { + PyErr_SetString(PyExc_IndexError, "list index out of range"); + return NULL; + } + } else { + n += size; + if (n < 0) { + PyErr_SetString(PyExc_IndexError, "list index out of range"); + return NULL; + } + } + PyObject *result = PyList_GET_ITEM(list, n); + Py_INCREF(result); + return result; +} + +PyObject *CPyList_GetItem(PyObject *list, CPyTagged index) { + if (CPyTagged_CheckShort(index)) { + Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); + Py_ssize_t size = PyList_GET_SIZE(list); + if (n >= 0) { + if (n >= size) { + PyErr_SetString(PyExc_IndexError, "list index out of range"); + return NULL; + } + } else { + n += size; + if (n < 0) { + PyErr_SetString(PyExc_IndexError, "list index out of range"); + return NULL; + } + } + PyObject *result = PyList_GET_ITEM(list, n); + Py_INCREF(result); + return result; + } else { + PyErr_SetString(PyExc_IndexError, "list index out of range"); + return NULL; + } +} + +bool CPyList_SetItem(PyObject *list, CPyTagged index, PyObject *value) { + if (CPyTagged_CheckShort(index)) { + Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); + Py_ssize_t size = PyList_GET_SIZE(list); + if (n >= 0) { + if (n >= size) { + PyErr_SetString(PyExc_IndexError, "list assignment index out of range"); + return false; + } + } else { + n += size; + if (n < 0) { + PyErr_SetString(PyExc_IndexError, "list assignment index out of range"); + return false; + } + } + // PyList_SET_ITEM doesn't decref the old element, so we do + Py_DECREF(PyList_GET_ITEM(list, n)); + // N.B: Steals reference + PyList_SET_ITEM(list, n, value); + return true; + } else { + PyErr_SetString(PyExc_IndexError, "list assignment index out of range"); + return false; + } +} + +PyObject *CPyList_PopLast(PyObject *obj) +{ + // I tried a specalized version of pop_impl for just removing the + // last element and it wasn't any faster in microbenchmarks than + // the generic one so I ditched it. + return list_pop_impl((PyListObject *)obj, -1); +} + +PyObject *CPyList_Pop(PyObject *obj, CPyTagged index) +{ + if (CPyTagged_CheckShort(index)) { + Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); + return list_pop_impl((PyListObject *)obj, n); + } else { + PyErr_SetString(PyExc_IndexError, "pop index out of range"); + return NULL; + } +} + +CPyTagged CPyList_Count(PyObject *obj, PyObject *value) +{ + return list_count((PyListObject *)obj, value); +} + +PyObject *CPyList_Extend(PyObject *o1, PyObject *o2) { + return _PyList_Extend((PyListObject *)o1, o2); +} + +PyObject *CPySequence_Multiply(PyObject *seq, CPyTagged t_size) { + Py_ssize_t size = CPyTagged_AsSsize_t(t_size); + if (size == -1 && PyErr_Occurred()) { + return NULL; + } + return PySequence_Repeat(seq, size); +} + +PyObject *CPySequence_RMultiply(CPyTagged t_size, PyObject *seq) { + return CPySequence_Multiply(seq, t_size); +} diff --git a/mypyc/lib-rt/misc_ops.c b/mypyc/lib-rt/misc_ops.c new file mode 100644 index 000000000000..a87668ab177d --- /dev/null +++ b/mypyc/lib-rt/misc_ops.c @@ -0,0 +1,496 @@ +// Misc primitive operations +// +// These are registered in mypyc.primitives.misc_ops. + +#include +#include "CPy.h" + +PyObject *CPy_GetCoro(PyObject *obj) +{ + // If the type has an __await__ method, call it, + // otherwise, fallback to calling __iter__. + PyAsyncMethods* async_struct = obj->ob_type->tp_as_async; + if (async_struct != NULL && async_struct->am_await != NULL) { + return (async_struct->am_await)(obj); + } else { + // TODO: We should check that the type is a generator decorated with + // asyncio.coroutine + return PyObject_GetIter(obj); + } +} + +PyObject *CPyIter_Send(PyObject *iter, PyObject *val) +{ + // Do a send, or a next if second arg is None. + // (This behavior is to match the PEP 380 spec for yield from.) + _Py_IDENTIFIER(send); + if (val == Py_None) { + return CPyIter_Next(iter); + } else { + return _PyObject_CallMethodIdObjArgs(iter, &PyId_send, val, NULL); + } +} + +// A somewhat hairy implementation of specifically most of the error handling +// in `yield from` error handling. The point here is to reduce code size. +// +// This implements most of the bodies of the `except` blocks in the +// pseudocode in PEP 380. +// +// Returns true (1) if a StopIteration was received and we should return. +// Returns false (0) if a value should be yielded. +// In both cases the value is stored in outp. +// Signals an error (2) if the an exception should be propagated. +int CPy_YieldFromErrorHandle(PyObject *iter, PyObject **outp) +{ + _Py_IDENTIFIER(close); + _Py_IDENTIFIER(throw); + PyObject *exc_type = CPy_ExcState()->exc_type; + PyObject *type, *value, *traceback; + PyObject *_m; + PyObject *res; + *outp = NULL; + + if (PyErr_GivenExceptionMatches(exc_type, PyExc_GeneratorExit)) { + _m = _PyObject_GetAttrId(iter, &PyId_close); + if (_m) { + res = PyObject_CallFunctionObjArgs(_m, NULL); + Py_DECREF(_m); + if (!res) + return 2; + Py_DECREF(res); + } else if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + } else { + return 2; + } + } else { + _m = _PyObject_GetAttrId(iter, &PyId_throw); + if (_m) { + _CPy_GetExcInfo(&type, &value, &traceback); + res = PyObject_CallFunctionObjArgs(_m, type, value, traceback, NULL); + Py_DECREF(type); + Py_DECREF(value); + Py_DECREF(traceback); + Py_DECREF(_m); + if (res) { + *outp = res; + return 0; + } else { + res = CPy_FetchStopIterationValue(); + if (res) { + *outp = res; + return 1; + } + } + } else if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + } else { + return 2; + } + } + + CPy_Reraise(); + return 2; +} + +PyObject *CPy_FetchStopIterationValue(void) +{ + PyObject *val = NULL; + _PyGen_FetchStopIterationValue(&val); + return val; +} + +static bool _CPy_IsSafeMetaClass(PyTypeObject *metaclass) { + // mypyc classes can't work with metaclasses in + // general. Through some various nasty hacks we *do* + // manage to work with TypingMeta and its friends. + if (metaclass == &PyType_Type) + return true; + PyObject *module = PyObject_GetAttrString((PyObject *)metaclass, "__module__"); + if (!module) { + PyErr_Clear(); + return false; + } + + bool matches = false; + if (PyUnicode_CompareWithASCIIString(module, "typing") == 0 && + (strcmp(metaclass->tp_name, "TypingMeta") == 0 + || strcmp(metaclass->tp_name, "GenericMeta") == 0 + || strcmp(metaclass->tp_name, "_ProtocolMeta") == 0)) { + matches = true; + } else if (PyUnicode_CompareWithASCIIString(module, "typing_extensions") == 0 && + strcmp(metaclass->tp_name, "_ProtocolMeta") == 0) { + matches = true; + } else if (PyUnicode_CompareWithASCIIString(module, "abc") == 0 && + strcmp(metaclass->tp_name, "ABCMeta") == 0) { + matches = true; + } + Py_DECREF(module); + return matches; +} + +// Create a heap type based on a template non-heap type. +// This is super hacky and maybe we should suck it up and use PyType_FromSpec instead. +// We allow bases to be NULL to represent just inheriting from object. +// We don't support NULL bases and a non-type metaclass. +PyObject *CPyType_FromTemplate(PyTypeObject *template_, + PyObject *orig_bases, + PyObject *modname) { + PyHeapTypeObject *t = NULL; + PyTypeObject *dummy_class = NULL; + PyObject *name = NULL; + PyObject *bases = NULL; + PyObject *slots; + + // If the type of the class (the metaclass) is NULL, we default it + // to being type. (This allows us to avoid needing to initialize + // it explicitly on windows.) + if (!Py_TYPE(template_)) { + Py_TYPE(template_) = &PyType_Type; + } + PyTypeObject *metaclass = Py_TYPE(template_); + + if (orig_bases) { + bases = update_bases(orig_bases); + // update_bases doesn't increment the refcount if nothing changes, + // so we do it to make sure we have distinct "references" to both + if (bases == orig_bases) + Py_INCREF(bases); + + // Find the appropriate metaclass from our base classes. We + // care about this because Generic uses a metaclass prior to + // Python 3.7. + metaclass = _PyType_CalculateMetaclass(metaclass, bases); + if (!metaclass) + goto error; + + if (!_CPy_IsSafeMetaClass(metaclass)) { + PyErr_SetString(PyExc_TypeError, "mypyc classes can't have a metaclass"); + goto error; + } + } + + name = PyUnicode_FromString(template_->tp_name); + if (!name) + goto error; + + // If there is a metaclass other than type, we would like to call + // its __new__ function. Unfortunately there doesn't seem to be a + // good way to mix a C extension class and creating it via a + // metaclass. We need to do it anyways, though, in order to + // support subclassing Generic[T] prior to Python 3.7. + // + // We solve this with a kind of atrocious hack: create a parallel + // class using the metaclass, determine the bases of the real + // class by pulling them out of the parallel class, creating the + // real class, and then merging its dict back into the original + // class. There are lots of cases where this won't really work, + // but for the case of GenericMeta setting a bunch of properties + // on the class we should be fine. + if (metaclass != &PyType_Type) { + assert(bases && "non-type metaclasses require non-NULL bases"); + + PyObject *ns = PyDict_New(); + if (!ns) + goto error; + + if (bases != orig_bases) { + if (PyDict_SetItemString(ns, "__orig_bases__", orig_bases) < 0) + goto error; + } + + dummy_class = (PyTypeObject *)PyObject_CallFunctionObjArgs( + (PyObject *)metaclass, name, bases, ns, NULL); + Py_DECREF(ns); + if (!dummy_class) + goto error; + + Py_DECREF(bases); + bases = dummy_class->tp_bases; + Py_INCREF(bases); + } + + // Allocate the type and then copy the main stuff in. + t = (PyHeapTypeObject*)PyType_GenericAlloc(&PyType_Type, 0); + if (!t) + goto error; + memcpy((char *)t + sizeof(PyVarObject), + (char *)template_ + sizeof(PyVarObject), + sizeof(PyTypeObject) - sizeof(PyVarObject)); + + if (bases != orig_bases) { + if (PyObject_SetAttrString((PyObject *)t, "__orig_bases__", orig_bases) < 0) + goto error; + } + + // Having tp_base set is I think required for stuff to get + // inherited in PyType_Ready, which we needed for subclassing + // BaseException. XXX: Taking the first element is wrong I think though. + if (bases) { + t->ht_type.tp_base = (PyTypeObject *)PyTuple_GET_ITEM(bases, 0); + Py_INCREF((PyObject *)t->ht_type.tp_base); + } + + t->ht_name = name; + Py_INCREF(name); + t->ht_qualname = name; + t->ht_type.tp_bases = bases; + // references stolen so NULL these out + bases = name = NULL; + + if (PyType_Ready((PyTypeObject *)t) < 0) + goto error; + + assert(t->ht_type.tp_base != NULL); + + // XXX: This is a terrible hack to work around a cpython check on + // the mro. It was needed for mypy.stats. I need to investigate + // what is actually going on here. + Py_INCREF(metaclass); + Py_TYPE(t) = metaclass; + + if (dummy_class) { + if (PyDict_Merge(t->ht_type.tp_dict, dummy_class->tp_dict, 0) != 0) + goto error; + // This is the *really* tasteless bit. GenericMeta's __new__ + // in certain versions of typing sets _gorg to point back to + // the class. We need to override it to keep it from pointing + // to the proxy. + if (PyDict_SetItemString(t->ht_type.tp_dict, "_gorg", (PyObject *)t) < 0) + goto error; + } + + // Reject anything that would give us a nontrivial __slots__, + // because the layout will conflict + slots = PyObject_GetAttrString((PyObject *)t, "__slots__"); + if (slots) { + // don't fail on an empty __slots__ + int is_true = PyObject_IsTrue(slots); + Py_DECREF(slots); + if (is_true > 0) + PyErr_SetString(PyExc_TypeError, "mypyc classes can't have __slots__"); + if (is_true != 0) + goto error; + } else { + PyErr_Clear(); + } + + if (PyObject_SetAttrString((PyObject *)t, "__module__", modname) < 0) + goto error; + + if (init_subclass((PyTypeObject *)t, NULL)) + goto error; + + Py_XDECREF(dummy_class); + + return (PyObject *)t; + +error: + Py_XDECREF(t); + Py_XDECREF(bases); + Py_XDECREF(dummy_class); + Py_XDECREF(name); + return NULL; +} + +static int _CPy_UpdateObjFromDict(PyObject *obj, PyObject *dict) +{ + Py_ssize_t pos = 0; + PyObject *key, *value; + while (PyDict_Next(dict, &pos, &key, &value)) { + if (PyObject_SetAttr(obj, key, value) != 0) { + return -1; + } + } + return 0; +} + +/* Support for our partial built-in support for dataclasses. + * + * Take a class we want to make a dataclass, remove any descriptors + * for annotated attributes, swap in the actual values of the class + * variables invoke dataclass, and then restore all of the + * descriptors. + * + * The purpose of all this is that dataclasses uses the values of + * class variables to drive which attributes are required and what the + * default values/factories are for optional attributes. This means + * that the class dict needs to contain those values instead of getset + * descriptors for the attributes when we invoke dataclass. + * + * We need to remove descriptors for attributes even when there is no + * default value for them, or else dataclass will think the descriptor + * is the default value. We remove only the attributes, since we don't + * want dataclasses to try generating functions when they are already + * implemented. + * + * Args: + * dataclass_dec: The decorator to apply + * tp: The class we are making a dataclass + * dict: The dictionary containing values that dataclasses needs + * annotations: The type annotation dictionary + */ +int +CPyDataclass_SleightOfHand(PyObject *dataclass_dec, PyObject *tp, + PyObject *dict, PyObject *annotations) { + PyTypeObject *ttp = (PyTypeObject *)tp; + Py_ssize_t pos; + PyObject *res; + + /* Make a copy of the original class __dict__ */ + PyObject *orig_dict = PyDict_Copy(ttp->tp_dict); + if (!orig_dict) { + goto fail; + } + + /* Delete anything that had an annotation */ + pos = 0; + PyObject *key; + while (PyDict_Next(annotations, &pos, &key, NULL)) { + if (PyObject_DelAttr(tp, key) != 0) { + goto fail; + } + } + + /* Copy in all the attributes that we want dataclass to see */ + if (_CPy_UpdateObjFromDict(tp, dict) != 0) { + goto fail; + } + + /* Run the @dataclass descriptor */ + res = PyObject_CallFunctionObjArgs(dataclass_dec, tp, NULL); + if (!res) { + goto fail; + } + Py_DECREF(res); + + /* Copy back the original contents of the dict */ + if (_CPy_UpdateObjFromDict(tp, orig_dict) != 0) { + goto fail; + } + + Py_DECREF(orig_dict); + return 1; + +fail: + Py_XDECREF(orig_dict); + return 0; +} + +// Support for pickling; reusable getstate and setstate functions +PyObject * +CPyPickle_SetState(PyObject *obj, PyObject *state) +{ + if (_CPy_UpdateObjFromDict(obj, state) != 0) { + return NULL; + } + Py_RETURN_NONE; +} + +PyObject * +CPyPickle_GetState(PyObject *obj) +{ + PyObject *attrs = NULL, *state = NULL; + + attrs = PyObject_GetAttrString((PyObject *)Py_TYPE(obj), "__mypyc_attrs__"); + if (!attrs) { + goto fail; + } + if (!PyTuple_Check(attrs)) { + PyErr_SetString(PyExc_TypeError, "__mypyc_attrs__ is not a tuple"); + goto fail; + } + state = PyDict_New(); + if (!state) { + goto fail; + } + + // Collect all the values of attributes in __mypyc_attrs__ + // Attributes that are missing we just ignore + int i; + for (i = 0; i < PyTuple_GET_SIZE(attrs); i++) { + PyObject *key = PyTuple_GET_ITEM(attrs, i); + PyObject *value = PyObject_GetAttr(obj, key); + if (!value) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + continue; + } + goto fail; + } + int result = PyDict_SetItem(state, key, value); + Py_DECREF(value); + if (result != 0) { + goto fail; + } + } + + Py_DECREF(attrs); + + return state; +fail: + Py_XDECREF(attrs); + Py_XDECREF(state); + return NULL; +} + +CPyTagged CPyTagged_Id(PyObject *o) { + return CPyTagged_FromSsize_t((Py_ssize_t)o); +} + +#define MAX_INT_CHARS 22 +#define _PyUnicode_LENGTH(op) \ + (((PyASCIIObject *)(op))->length) + +// using snprintf or PyUnicode_FromFormat was way slower than +// boxing the int and calling PyObject_Str on it, so we implement our own +static int fmt_ssize_t(char *out, Py_ssize_t n) { + bool neg = n < 0; + if (neg) n = -n; + + // buf gets filled backward and then we copy it forward + char buf[MAX_INT_CHARS]; + int i = 0; + do { + buf[i] = (n % 10) + '0'; + n /= 10; + i++; + } while (n); + + + int len = i; + int j = 0; + if (neg) { + out[j++] = '-'; + len++; + } + + for (; j < len; j++, i--) { + out[j] = buf[i-1]; + } + out[j] = '\0'; + + return len; +} + +static PyObject *CPyTagged_ShortToStr(Py_ssize_t n) { + PyObject *obj = PyUnicode_New(MAX_INT_CHARS, 127); + if (!obj) return NULL; + int len = fmt_ssize_t((char *)PyUnicode_1BYTE_DATA(obj), n); + _PyUnicode_LENGTH(obj) = len; + return obj; +} + +PyObject *CPyTagged_Str(CPyTagged n) { + if (CPyTagged_CheckShort(n)) { + return CPyTagged_ShortToStr(CPyTagged_ShortAsSsize_t(n)); + } else { + return PyObject_Str(CPyTagged_AsObject(n)); + } +} + +void CPyDebug_Print(const char *msg) { + printf("%s\n", msg); + fflush(stdout); +} diff --git a/mypyc/lib-rt/set_ops.c b/mypyc/lib-rt/set_ops.c new file mode 100644 index 000000000000..7e769674f985 --- /dev/null +++ b/mypyc/lib-rt/set_ops.c @@ -0,0 +1,17 @@ +// Set primitive operations +// +// These are registered in mypyc.primitives.set_ops. + +#include +#include "CPy.h" + +bool CPySet_Remove(PyObject *set, PyObject *key) { + int success = PySet_Discard(set, key); + if (success == 1) { + return true; + } + if (success == 0) { + _PyErr_SetKeyError(key); + } + return false; +} diff --git a/mypyc/lib-rt/setup.py b/mypyc/lib-rt/setup.py index 7270f6db7abb..5e2fb66b4f4e 100644 --- a/mypyc/lib-rt/setup.py +++ b/mypyc/lib-rt/setup.py @@ -1,13 +1,27 @@ +"""Build script for mypyc C runtime library unit tests. + +The tests are written in C++ and use the Google Test framework. +""" + from distutils.core import setup, Extension +import sys + +if sys.platform == 'darwin': + kwargs = {'language': 'c++'} + compile_args = [] +else: + kwargs = {} + compile_args = ['--std=c++11'] setup(name='test_capi', version='0.1', ext_modules=[Extension( 'test_capi', - ['test_capi.cc', 'CPy.cc'], + ['test_capi.cc', 'init.c', 'int_ops.c', 'list_ops.c', 'exc_ops.c'], depends=['CPy.h', 'mypyc_util.h', 'pythonsupport.h'], - extra_compile_args=['--std=c++11', '-Wno-unused-function', '-Wno-sign-compare'], + extra_compile_args=['-Wno-unused-function', '-Wno-sign-compare'] + compile_args, library_dirs=['../external/googletest/make'], libraries=['gtest'], include_dirs=['../external/googletest', '../external/googletest/include'], + **kwargs )]) diff --git a/mypyc/lib-rt/str_ops.c b/mypyc/lib-rt/str_ops.c new file mode 100644 index 000000000000..00835d6d81d2 --- /dev/null +++ b/mypyc/lib-rt/str_ops.c @@ -0,0 +1,60 @@ +// String primitive operations +// +// These are registered in mypyc.primitives.str_ops. + +#include +#include "CPy.h" + +PyObject *CPyStr_GetItem(PyObject *str, CPyTagged index) { + if (PyUnicode_READY(str) != -1) { + if (CPyTagged_CheckShort(index)) { + Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); + Py_ssize_t size = PyUnicode_GET_LENGTH(str); + if ((n >= 0 && n >= size) || (n < 0 && n + size < 0)) { + PyErr_SetString(PyExc_IndexError, "string index out of range"); + return NULL; + } + if (n < 0) + n += size; + enum PyUnicode_Kind kind = (enum PyUnicode_Kind)PyUnicode_KIND(str); + void *data = PyUnicode_DATA(str); + Py_UCS4 ch = PyUnicode_READ(kind, data, n); + PyObject *unicode = PyUnicode_New(1, ch); + if (unicode == NULL) + return NULL; + + if (PyUnicode_KIND(unicode) == PyUnicode_1BYTE_KIND) { + PyUnicode_1BYTE_DATA(unicode)[0] = (Py_UCS1)ch; + } + else if (PyUnicode_KIND(unicode) == PyUnicode_2BYTE_KIND) { + PyUnicode_2BYTE_DATA(unicode)[0] = (Py_UCS2)ch; + } else { + assert(PyUnicode_KIND(unicode) == PyUnicode_4BYTE_KIND); + PyUnicode_4BYTE_DATA(unicode)[0] = ch; + } + return unicode; + } else { + PyErr_SetString(PyExc_IndexError, "string index out of range"); + return NULL; + } + } else { + PyObject *index_obj = CPyTagged_AsObject(index); + return PyObject_GetItem(str, index_obj); + } +} + +PyObject *CPyStr_Split(PyObject *str, PyObject *sep, CPyTagged max_split) +{ + Py_ssize_t temp_max_split = CPyTagged_AsSsize_t(max_split); + if (temp_max_split == -1 && PyErr_Occurred()) { + PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to C ssize_t"); + return NULL; + } + return PyUnicode_Split(str, sep, temp_max_split); +} + +/* This does a dodgy attempt to append in place */ +PyObject *CPyStr_Append(PyObject *o1, PyObject *o2) { + PyUnicode_Append(&o1, o2); + return o1; +} diff --git a/mypyc/lib-rt/tuple_ops.c b/mypyc/lib-rt/tuple_ops.c new file mode 100644 index 000000000000..bd08f9bf172c --- /dev/null +++ b/mypyc/lib-rt/tuple_ops.c @@ -0,0 +1,31 @@ +// Tuple primitive operations +// +// These are registered in mypyc.primitives.tuple_ops. + +#include +#include "CPy.h" + +PyObject *CPySequenceTuple_GetItem(PyObject *tuple, CPyTagged index) { + if (CPyTagged_CheckShort(index)) { + Py_ssize_t n = CPyTagged_ShortAsSsize_t(index); + Py_ssize_t size = PyTuple_GET_SIZE(tuple); + if (n >= 0) { + if (n >= size) { + PyErr_SetString(PyExc_IndexError, "tuple index out of range"); + return NULL; + } + } else { + n += size; + if (n < 0) { + PyErr_SetString(PyExc_IndexError, "tuple index out of range"); + return NULL; + } + } + PyObject *result = PyTuple_GET_ITEM(tuple, n); + Py_INCREF(result); + return result; + } else { + PyErr_SetString(PyExc_IndexError, "tuple index out of range"); + return NULL; + } +} From 358522e28cd58d95daf36256c46eb7ffcc55eea4 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Mon, 13 Jul 2020 23:01:07 +0800 Subject: [PATCH 064/138] [mypyc] Generates inline compare statement on short ints and fix sieve performance regression (#9127) Related: mypyc/mypyc#750 Generate almost identical code as before https://github.com/python/mypy/commit/be01236bcdb9a9da66e68dd0d45ff0f9a604e44a Before: ```c CPyL1: ; cpy_r_r4 = (Py_ssize_t)cpy_r_r3 < (Py_ssize_t)cpy_r_r2; if (cpy_r_r4) { goto CPyL2; } else goto CPyL5; ``` Now with this PR: ```c CPyL1: ; cpy_r_r4 = cpy_r_r3 < cpy_r_r2; if (cpy_r_r4) { goto CPyL2; } else goto CPyL5; ``` --- mypyc/irbuild/ll_builder.py | 8 +- mypyc/primitives/int_ops.py | 9 +- mypyc/test-data/analysis.test | 92 ++++++--- mypyc/test-data/exceptions.test | 64 +++--- mypyc/test-data/irbuild-basic.test | 250 +++++++++++++++++------- mypyc/test-data/irbuild-lists.test | 2 +- mypyc/test-data/irbuild-statements.test | 16 +- mypyc/test/test_analysis.py | 8 +- mypyc/test/test_exceptions.py | 4 +- mypyc/test/test_irbuild.py | 8 +- mypyc/test/test_refcount.py | 8 +- mypyc/test/testutil.py | 7 + 12 files changed, 332 insertions(+), 144 deletions(-) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index bf9b71f83937..b3fec1aa542c 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -26,7 +26,7 @@ from mypyc.ir.rtypes import ( RType, RUnion, RInstance, optional_value_type, int_rprimitive, float_rprimitive, bool_rprimitive, list_rprimitive, str_rprimitive, is_none_rprimitive, object_rprimitive, - c_pyssize_t_rprimitive + c_pyssize_t_rprimitive, is_short_int_rprimitive ) from mypyc.ir.func_ir import FuncDecl, FuncSignature from mypyc.ir.class_ir import ClassIR, all_concrete_classes @@ -547,6 +547,12 @@ def binary_op(self, if value is not None: return value + # generate fast binary logic ops on short ints + if (is_short_int_rprimitive(lreg.type) and is_short_int_rprimitive(rreg.type) + and expr_op in int_logical_op_mapping.keys()): + return self.binary_int_op(bool_rprimitive, lreg, rreg, + int_logical_op_mapping[expr_op][0], line) + call_c_ops_candidates = c_binary_ops.get(expr_op, []) target = self.matching_call_c(call_c_ops_candidates, [lreg, rreg], line) if target: diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index b0efd642890a..0eeee5760823 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -150,8 +150,15 @@ def int_unary_op(name: str, c_function_name: str) -> CFunctionDescription: c_function_name='CPyTagged_IsEq_', error_kind=ERR_NEVER) +int_less_than_ = c_custom_op( + arg_types=[int_rprimitive, int_rprimitive], + return_type=bool_rprimitive, + c_function_name='CPyTagged_IsLt_', + error_kind=ERR_NEVER) + # provide mapping from textual op to short int's op variant and boxed int's description # note these are not complete implementations int_logical_op_mapping = { - '==': (BinaryIntOp.EQ, int_equal_) + '==': (BinaryIntOp.EQ, int_equal_), + '<': (BinaryIntOp.LT, int_less_than_) } # type: Dict[str, Tuple[int, CFunctionDescription]] diff --git a/mypyc/test-data/analysis.test b/mypyc/test-data/analysis.test index dde8430f18eb..7da0b913a07d 100644 --- a/mypyc/test-data/analysis.test +++ b/mypyc/test-data/analysis.test @@ -368,38 +368,86 @@ def f(a: int) -> None: [out] def f(a): a :: int - r0, r1 :: bool + r0 :: bool + r1, r2, r3 :: native_int + r4, r5, r6, r7 :: bool + r8, r9, r10 :: native_int + r11, r12, r13 :: bool y, x :: int - r2 :: None + r14 :: None L0: L1: - r0 = CPyTagged_IsLt(a, a) - if r0 goto L2 else goto L6 :: bool + r1 = 1 + r2 = a & r1 + r3 = 0 + r4 = r2 == r3 + if r4 goto L2 else goto L3 :: bool L2: + r5 = a < a + r0 = r5 + goto L4 L3: - r1 = CPyTagged_IsLt(a, a) - if r1 goto L4 else goto L5 :: bool + r6 = CPyTagged_IsLt_(a, a) + r0 = r6 L4: - y = a - goto L3 + if r0 goto L5 else goto L12 :: bool L5: +L6: + r8 = 1 + r9 = a & r8 + r10 = 0 + r11 = r9 == r10 + if r11 goto L7 else goto L8 :: bool +L7: + r12 = a < a + r7 = r12 + goto L9 +L8: + r13 = CPyTagged_IsLt_(a, a) + r7 = r13 +L9: + if r7 goto L10 else goto L11 :: bool +L10: + y = a + goto L6 +L11: x = a goto L1 -L6: - r2 = None - return r2 +L12: + r14 = None + return r14 (0, 0) {a} {a} -(1, 0) {a, x, y} {a, x, y} -(1, 1) {a, x, y} {a, x, y} -(2, 0) {a, x, y} {a, x, y} -(3, 0) {a, x, y} {a, x, y} -(3, 1) {a, x, y} {a, x, y} -(4, 0) {a, x, y} {a, x, y} -(4, 1) {a, x, y} {a, x, y} -(5, 0) {a, x, y} {a, x, y} -(5, 1) {a, x, y} {a, x, y} -(6, 0) {a, x, y} {a, x, y} -(6, 1) {a, x, y} {a, x, y} +(1, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} +(1, 1) {a, r0, r7, x, y} {a, r0, r7, x, y} +(1, 2) {a, r0, r7, x, y} {a, r0, r7, x, y} +(1, 3) {a, r0, r7, x, y} {a, r0, r7, x, y} +(1, 4) {a, r0, r7, x, y} {a, r0, r7, x, y} +(2, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} +(2, 1) {a, r0, r7, x, y} {a, r0, r7, x, y} +(2, 2) {a, r0, r7, x, y} {a, r0, r7, x, y} +(3, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} +(3, 1) {a, r0, r7, x, y} {a, r0, r7, x, y} +(3, 2) {a, r0, r7, x, y} {a, r0, r7, x, y} +(4, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} +(5, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} +(6, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} +(6, 1) {a, r0, r7, x, y} {a, r0, r7, x, y} +(6, 2) {a, r0, r7, x, y} {a, r0, r7, x, y} +(6, 3) {a, r0, r7, x, y} {a, r0, r7, x, y} +(6, 4) {a, r0, r7, x, y} {a, r0, r7, x, y} +(7, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} +(7, 1) {a, r0, r7, x, y} {a, r0, r7, x, y} +(7, 2) {a, r0, r7, x, y} {a, r0, r7, x, y} +(8, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} +(8, 1) {a, r0, r7, x, y} {a, r0, r7, x, y} +(8, 2) {a, r0, r7, x, y} {a, r0, r7, x, y} +(9, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} +(10, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} +(10, 1) {a, r0, r7, x, y} {a, r0, r7, x, y} +(11, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} +(11, 1) {a, r0, r7, x, y} {a, r0, r7, x, y} +(12, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} +(12, 1) {a, r0, r7, x, y} {a, r0, r7, x, y} [case testTrivial_BorrowedArgument] def f(a: int, b: int) -> int: diff --git a/mypyc/test-data/exceptions.test b/mypyc/test-data/exceptions.test index dd18356e22c4..a59bcf121b85 100644 --- a/mypyc/test-data/exceptions.test +++ b/mypyc/test-data/exceptions.test @@ -131,47 +131,61 @@ def sum(a, l): r1 :: short_int i :: int r2 :: bool - r3 :: object - r4, r5 :: int - r6 :: short_int - r7, r8 :: int + r3, r4, r5 :: native_int + r6, r7, r8 :: bool + r9 :: object + r10, r11 :: int + r12 :: short_int + r13, r14 :: int L0: r0 = 0 sum = r0 r1 = 0 i = r1 L1: - r2 = CPyTagged_IsLt(i, l) - if r2 goto L2 else goto L7 :: bool + r3 = 1 + r4 = i & r3 + r5 = 0 + r6 = r4 == r5 + if r6 goto L2 else goto L3 :: bool L2: - r3 = CPyList_GetItem(a, i) - if is_error(r3) goto L8 (error at sum:6) else goto L3 + r7 = i < l + r2 = r7 + goto L4 L3: - r4 = unbox(int, r3) - dec_ref r3 - if is_error(r4) goto L8 (error at sum:6) else goto L4 + r8 = CPyTagged_IsLt_(i, l) + r2 = r8 L4: - r5 = CPyTagged_Add(sum, r4) - dec_ref sum :: int - dec_ref r4 :: int - sum = r5 - r6 = 1 - r7 = CPyTagged_Add(i, r6) - dec_ref i :: int - i = r7 - goto L1 + if r2 goto L5 else goto L10 :: bool L5: - return sum + r9 = CPyList_GetItem(a, i) + if is_error(r9) goto L11 (error at sum:6) else goto L6 L6: - r8 = :: int - return r8 + r10 = unbox(int, r9) + dec_ref r9 + if is_error(r10) goto L11 (error at sum:6) else goto L7 L7: + r11 = CPyTagged_Add(sum, r10) + dec_ref sum :: int + dec_ref r10 :: int + sum = r11 + r12 = 1 + r13 = CPyTagged_Add(i, r12) dec_ref i :: int - goto L5 + i = r13 + goto L1 L8: + return sum +L9: + r14 = :: int + return r14 +L10: + dec_ref i :: int + goto L8 +L11: dec_ref sum :: int dec_ref i :: int - goto L6 + goto L9 [case testTryExcept] def g() -> None: diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 5eb54b6e3383..2cf2a0945461 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -93,14 +93,28 @@ def f(x: int, y: int) -> int: def f(x, y): x, y :: int r0 :: bool - r1 :: short_int + r1, r2, r3 :: native_int + r4, r5, r6 :: bool + r7 :: short_int L0: - r0 = CPyTagged_IsLt(x, y) - if r0 goto L1 else goto L2 :: bool -L1: r1 = 1 - x = r1 + r2 = x & r1 + r3 = 0 + r4 = r2 == r3 + if r4 goto L1 else goto L2 :: bool +L1: + r5 = x < y + r0 = r5 + goto L3 L2: + r6 = CPyTagged_IsLt_(x, y) + r0 = r6 +L3: + if r0 goto L4 else goto L5 :: bool +L4: + r7 = 1 + x = r7 +L5: return x [case testIfElse] @@ -114,18 +128,32 @@ def f(x: int, y: int) -> int: def f(x, y): x, y :: int r0 :: bool - r1, r2 :: short_int + r1, r2, r3 :: native_int + r4, r5, r6 :: bool + r7, r8 :: short_int L0: - r0 = CPyTagged_IsLt(x, y) - if r0 goto L1 else goto L2 :: bool -L1: r1 = 1 - x = r1 + r2 = x & r1 + r3 = 0 + r4 = r2 == r3 + if r4 goto L1 else goto L2 :: bool +L1: + r5 = x < y + r0 = r5 goto L3 L2: - r2 = 2 - x = r2 + r6 = CPyTagged_IsLt_(x, y) + r0 = r6 L3: + if r0 goto L4 else goto L5 :: bool +L4: + r7 = 1 + x = r7 + goto L6 +L5: + r8 = 2 + x = r8 +L6: return x [case testAnd1] @@ -138,22 +166,36 @@ def f(x: int, y: int) -> int: [out] def f(x, y): x, y :: int - r0, r1 :: bool - r2, r3 :: short_int + r0 :: bool + r1, r2, r3 :: native_int + r4, r5, r6, r7 :: bool + r8, r9 :: short_int L0: - r0 = CPyTagged_IsLt(x, y) - if r0 goto L1 else goto L3 :: bool + r1 = 1 + r2 = x & r1 + r3 = 0 + r4 = r2 == r3 + if r4 goto L1 else goto L2 :: bool L1: - r1 = CPyTagged_IsGt(x, y) - if r1 goto L2 else goto L3 :: bool + r5 = x < y + r0 = r5 + goto L3 L2: - r2 = 1 - x = r2 - goto L4 + r6 = CPyTagged_IsLt_(x, y) + r0 = r6 L3: - r3 = 2 - x = r3 + if r0 goto L4 else goto L6 :: bool L4: + r7 = CPyTagged_IsGt(x, y) + if r7 goto L5 else goto L6 :: bool +L5: + r8 = 1 + x = r8 + goto L7 +L6: + r9 = 2 + x = r9 +L7: return x [case testAnd2] @@ -188,22 +230,36 @@ def f(x: int, y: int) -> int: [out] def f(x, y): x, y :: int - r0, r1 :: bool - r2, r3 :: short_int + r0 :: bool + r1, r2, r3 :: native_int + r4, r5, r6, r7 :: bool + r8, r9 :: short_int L0: - r0 = CPyTagged_IsLt(x, y) - if r0 goto L2 else goto L1 :: bool + r1 = 1 + r2 = x & r1 + r3 = 0 + r4 = r2 == r3 + if r4 goto L1 else goto L2 :: bool L1: - r1 = CPyTagged_IsGt(x, y) - if r1 goto L2 else goto L3 :: bool + r5 = x < y + r0 = r5 + goto L3 L2: - r2 = 1 - x = r2 - goto L4 + r6 = CPyTagged_IsLt_(x, y) + r0 = r6 L3: - r3 = 2 - x = r3 + if r0 goto L5 else goto L4 :: bool L4: + r7 = CPyTagged_IsGt(x, y) + if r7 goto L5 else goto L6 :: bool +L5: + r8 = 1 + x = r8 + goto L7 +L6: + r9 = 2 + x = r9 +L7: return x [case testOr2] @@ -237,14 +293,28 @@ def f(x: int, y: int) -> int: def f(x, y): x, y :: int r0 :: bool - r1 :: short_int + r1, r2, r3 :: native_int + r4, r5, r6 :: bool + r7 :: short_int L0: - r0 = CPyTagged_IsLt(x, y) - if r0 goto L2 else goto L1 :: bool -L1: r1 = 1 - x = r1 + r2 = x & r1 + r3 = 0 + r4 = r2 == r3 + if r4 goto L1 else goto L2 :: bool +L1: + r5 = x < y + r0 = r5 + goto L3 L2: + r6 = CPyTagged_IsLt_(x, y) + r0 = r6 +L3: + if r0 goto L5 else goto L4 :: bool +L4: + r7 = 1 + x = r7 +L5: return x [case testNotAnd] @@ -255,18 +325,32 @@ def f(x: int, y: int) -> int: [out] def f(x, y): x, y :: int - r0, r1 :: bool - r2 :: short_int + r0 :: bool + r1, r2, r3 :: native_int + r4, r5, r6, r7 :: bool + r8 :: short_int L0: - r0 = CPyTagged_IsLt(x, y) - if r0 goto L1 else goto L2 :: bool + r1 = 1 + r2 = x & r1 + r3 = 0 + r4 = r2 == r3 + if r4 goto L1 else goto L2 :: bool L1: - r1 = CPyTagged_IsGt(x, y) - if r1 goto L3 else goto L2 :: bool + r5 = x < y + r0 = r5 + goto L3 L2: - r2 = 1 - x = r2 + r6 = CPyTagged_IsLt_(x, y) + r0 = r6 L3: + if r0 goto L4 else goto L5 :: bool +L4: + r7 = CPyTagged_IsGt(x, y) + if r7 goto L6 else goto L5 :: bool +L5: + r8 = 1 + x = r8 +L6: return x [case testWhile] @@ -349,21 +433,35 @@ def f(x: int, y: int) -> None: def f(x, y): x, y :: int r0 :: bool - r1, r2 :: short_int - r3 :: None + r1, r2, r3 :: native_int + r4, r5, r6 :: bool + r7, r8 :: short_int + r9 :: None L0: - r0 = CPyTagged_IsLt(x, y) - if r0 goto L1 else goto L2 :: bool -L1: r1 = 1 - x = r1 + r2 = x & r1 + r3 = 0 + r4 = r2 == r3 + if r4 goto L1 else goto L2 :: bool +L1: + r5 = x < y + r0 = r5 goto L3 L2: - r2 = 2 - y = r2 + r6 = CPyTagged_IsLt_(x, y) + r0 = r6 L3: - r3 = None - return r3 + if r0 goto L4 else goto L5 :: bool +L4: + r7 = 1 + x = r7 + goto L6 +L5: + r8 = 2 + y = r8 +L6: + r9 = None + return r9 [case testRecursion] def f(n: int) -> int: @@ -1894,7 +1992,7 @@ L0: r9 = r8 L1: r10 = len r7 :: list - r11 = CPyTagged_IsLt(r9, r10) + r11 = r9 < r10 if r11 goto L2 else goto L8 :: bool L2: r12 = r7[r9] :: unsafe list @@ -1958,7 +2056,7 @@ L0: r9 = r8 L1: r10 = len r7 :: list - r11 = CPyTagged_IsLt(r9, r10) + r11 = r9 < r10 if r11 goto L2 else goto L8 :: bool L2: r12 = r7[r9] :: unsafe list @@ -2019,7 +2117,7 @@ L0: r1 = r0 L1: r2 = len l :: list - r3 = CPyTagged_IsLt(r1, r2) + r3 = r1 < r2 if r3 goto L2 else goto L4 :: bool L2: r4 = l[r1] :: unsafe list @@ -2041,7 +2139,7 @@ L4: r13 = r12 L5: r14 = len l :: list - r15 = CPyTagged_IsLt(r13, r14) + r15 = r13 < r14 if r15 goto L6 else goto L8 :: bool L6: r16 = l[r13] :: unsafe list @@ -2583,21 +2681,35 @@ L0: def f(x, y, z): x, y, z, r0, r1 :: int r2, r3 :: bool - r4 :: int - r5 :: bool + r4, r5, r6 :: native_int + r7, r8, r9 :: bool + r10 :: int + r11 :: bool L0: r0 = g(x) r1 = g(y) - r3 = CPyTagged_IsLt(r0, r1) - if r3 goto L2 else goto L1 :: bool + r4 = 1 + r5 = r0 & r4 + r6 = 0 + r7 = r5 == r6 + if r7 goto L1 else goto L2 :: bool L1: - r2 = r3 + r8 = r0 < r1 + r3 = r8 goto L3 L2: - r4 = g(z) - r5 = CPyTagged_IsGt(r1, r4) - r2 = r5 + r9 = CPyTagged_IsLt_(r0, r1) + r3 = r9 L3: + if r3 goto L5 else goto L4 :: bool +L4: + r2 = r3 + goto L6 +L5: + r10 = g(z) + r11 = CPyTagged_IsGt(r1, r10) + r2 = r11 +L6: return r2 [case testEq] diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test index 2db12c6a4975..9a47f632ef55 100644 --- a/mypyc/test-data/irbuild-lists.test +++ b/mypyc/test-data/irbuild-lists.test @@ -185,7 +185,7 @@ L0: r2 = r0 i = r2 L1: - r3 = CPyTagged_IsLt(r2, r1) + r3 = r2 < r1 if r3 goto L2 else goto L4 :: bool L2: r4 = CPyList_GetItem(l, i) diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index 16660928f465..528e0a85c4e9 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -21,7 +21,7 @@ L0: r3 = r1 i = r3 L1: - r4 = CPyTagged_IsLt(r3, r2) + r4 = r3 < r2 if r4 goto L2 else goto L4 :: bool L2: r5 = CPyTagged_Add(x, i) @@ -107,7 +107,7 @@ L0: r2 = r0 n = r2 L1: - r3 = CPyTagged_IsLt(r2, r1) + r3 = r2 < r1 if r3 goto L2 else goto L4 :: bool L2: goto L4 @@ -197,7 +197,7 @@ L0: r2 = r0 n = r2 L1: - r3 = CPyTagged_IsLt(r2, r1) + r3 = r2 < r1 if r3 goto L2 else goto L4 :: bool L2: L3: @@ -271,7 +271,7 @@ L0: r2 = r1 L1: r3 = len ls :: list - r4 = CPyTagged_IsLt(r2, r3) + r4 = r2 < r3 if r4 goto L2 else goto L4 :: bool L2: r5 = ls[r2] :: unsafe list @@ -859,7 +859,7 @@ L0: r3 = r2 L1: r4 = len a :: list - r5 = CPyTagged_IsLt(r3, r4) + r5 = r3 < r4 if r5 goto L2 else goto L4 :: bool L2: r6 = a[r3] :: unsafe list @@ -942,7 +942,7 @@ L0: r2 = iter b :: object L1: r3 = len a :: list - r4 = CPyTagged_IsLt(r1, r3) + r4 = r1 < r3 if r4 goto L2 else goto L7 :: bool L2: r5 = next r2 :: object @@ -997,10 +997,10 @@ L1: if is_error(r6) goto L6 else goto L2 L2: r7 = len b :: list - r8 = CPyTagged_IsLt(r2, r7) + r8 = r2 < r7 if r8 goto L3 else goto L6 :: bool L3: - r9 = CPyTagged_IsLt(r5, r4) + r9 = r5 < r4 if r9 goto L4 else goto L6 :: bool L4: r10 = unbox(bool, r6) diff --git a/mypyc/test/test_analysis.py b/mypyc/test/test_analysis.py index 61c8356299d4..1df3efbb698b 100644 --- a/mypyc/test/test_analysis.py +++ b/mypyc/test/test_analysis.py @@ -6,13 +6,13 @@ from mypy.test.config import test_temp_dir from mypy.errors import CompileError -from mypyc.common import TOP_LEVEL_NAME, IS_32_BIT_PLATFORM +from mypyc.common import TOP_LEVEL_NAME from mypyc import analysis from mypyc.transform import exceptions from mypyc.ir.func_ir import format_func from mypyc.test.testutil import ( ICODE_GEN_BUILTINS, use_custom_builtins, MypycDataSuite, build_ir_for_single_file, - assert_test_output, + assert_test_output, replace_native_int ) files = [ @@ -29,9 +29,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: """Perform a data-flow analysis test case.""" with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase): - # replace native_int with platform specific ints - int_format_str = 'int32' if IS_32_BIT_PLATFORM else 'int64' - testcase.output = [s.replace('native_int', int_format_str) for s in testcase.output] + testcase.output = replace_native_int(testcase.output) try: ir = build_ir_for_single_file(testcase.input) except CompileError as e: diff --git a/mypyc/test/test_exceptions.py b/mypyc/test/test_exceptions.py index ecc914a1165b..877a28cb7f44 100644 --- a/mypyc/test/test_exceptions.py +++ b/mypyc/test/test_exceptions.py @@ -16,7 +16,7 @@ from mypyc.transform.refcount import insert_ref_count_opcodes from mypyc.test.testutil import ( ICODE_GEN_BUILTINS, use_custom_builtins, MypycDataSuite, build_ir_for_single_file, - assert_test_output, remove_comment_lines + assert_test_output, remove_comment_lines, replace_native_int ) files = [ @@ -32,7 +32,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: """Perform a runtime checking transformation test case.""" with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase): expected_output = remove_comment_lines(testcase.output) - + expected_output = replace_native_int(expected_output) try: ir = build_ir_for_single_file(testcase.input) except CompileError as e: diff --git a/mypyc/test/test_irbuild.py b/mypyc/test/test_irbuild.py index c8606aba059e..10adb6bed435 100644 --- a/mypyc/test/test_irbuild.py +++ b/mypyc/test/test_irbuild.py @@ -6,11 +6,11 @@ from mypy.test.data import DataDrivenTestCase from mypy.errors import CompileError -from mypyc.common import TOP_LEVEL_NAME, IS_32_BIT_PLATFORM +from mypyc.common import TOP_LEVEL_NAME from mypyc.ir.func_ir import format_func from mypyc.test.testutil import ( ICODE_GEN_BUILTINS, use_custom_builtins, MypycDataSuite, build_ir_for_single_file, - assert_test_output, remove_comment_lines + assert_test_output, remove_comment_lines, replace_native_int ) from mypyc.options import CompilerOptions @@ -42,9 +42,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: """Perform a runtime checking transformation test case.""" with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase): expected_output = remove_comment_lines(testcase.output) - # replace native_int with platform specific ints - int_format_str = 'int32' if IS_32_BIT_PLATFORM else 'int64' - expected_output = [s.replace('native_int', int_format_str) for s in expected_output] + expected_output = replace_native_int(expected_output) try: ir = build_ir_for_single_file(testcase.input, options) except CompileError as e: diff --git a/mypyc/test/test_refcount.py b/mypyc/test/test_refcount.py index 2c026ae56afc..dc73a6ffa73d 100644 --- a/mypyc/test/test_refcount.py +++ b/mypyc/test/test_refcount.py @@ -10,12 +10,12 @@ from mypy.test.data import DataDrivenTestCase from mypy.errors import CompileError -from mypyc.common import TOP_LEVEL_NAME, IS_32_BIT_PLATFORM +from mypyc.common import TOP_LEVEL_NAME from mypyc.ir.func_ir import format_func from mypyc.transform.refcount import insert_ref_count_opcodes from mypyc.test.testutil import ( ICODE_GEN_BUILTINS, use_custom_builtins, MypycDataSuite, build_ir_for_single_file, - assert_test_output, remove_comment_lines + assert_test_output, remove_comment_lines, replace_native_int ) files = [ @@ -32,9 +32,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: """Perform a runtime checking transformation test case.""" with use_custom_builtins(os.path.join(self.data_prefix, ICODE_GEN_BUILTINS), testcase): expected_output = remove_comment_lines(testcase.output) - # replace native_int with platform specific ints - int_format_str = 'int32' if IS_32_BIT_PLATFORM else 'int64' - expected_output = [s.replace('native_int', int_format_str) for s in expected_output] + expected_output = replace_native_int(expected_output) try: ir = build_ir_for_single_file(testcase.input) except CompileError as e: diff --git a/mypyc/test/testutil.py b/mypyc/test/testutil.py index 141472bb30a6..18ab39a103ad 100644 --- a/mypyc/test/testutil.py +++ b/mypyc/test/testutil.py @@ -22,6 +22,7 @@ from mypyc.irbuild.main import build_ir from mypyc.irbuild.mapper import Mapper from mypyc.test.config import test_data_prefix +from mypyc.common import IS_32_BIT_PLATFORM # The builtins stub used during icode generation test cases. ICODE_GEN_BUILTINS = os.path.join(test_data_prefix, 'fixtures/ir.py') @@ -210,3 +211,9 @@ def fudge_dir_mtimes(dir: str, delta: int) -> None: path = os.path.join(dirpath, name) new_mtime = os.stat(path).st_mtime + delta os.utime(path, times=(new_mtime, new_mtime)) + + +def replace_native_int(text: List[str]) -> List[str]: + """Replace native_int with platform specific ints""" + int_format_str = 'int32' if IS_32_BIT_PLATFORM else 'int64' + return [s.replace('native_int', int_format_str) for s in text] From b363204a9a134aaa90c331a1eb71223f428ca426 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Tue, 14 Jul 2020 01:07:10 +0300 Subject: [PATCH 065/138] Removes redundant `\` char (#9144) --- mypy/subtypes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index cecc24ed6aee..b0fc61a7c874 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -408,7 +408,7 @@ def visit_overloaded(self, left: Overloaded) -> bool: found_match = False for left_index, left_item in enumerate(left.items()): - subtype_match = self._is_subtype(left_item, right_item)\ + subtype_match = self._is_subtype(left_item, right_item) # Order matters: we need to make sure that the index of # this item is at least the index of the previous one. From e1ac8b2f35d5b36c08081fd98709f09ab23d7119 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Tue, 14 Jul 2020 19:16:27 +0800 Subject: [PATCH 066/138] [mypyc] Check both operands when op is not eq or neq (#9148) This fixes a bug I found during merging int logical ops. --- mypyc/irbuild/ll_builder.py | 9 +- mypyc/test-data/analysis.test | 128 +++++++++------- mypyc/test-data/exceptions.test | 53 ++++--- mypyc/test-data/irbuild-basic.test | 238 ++++++++++++++++++----------- 4 files changed, 261 insertions(+), 167 deletions(-) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index b3fec1aa542c..dfddf6da5bae 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -576,7 +576,14 @@ def compare_tagged(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: op_type, c_func_desc = int_logical_op_mapping[op] result = self.alloc_temp(bool_rprimitive) short_int_block, int_block, out = BasicBlock(), BasicBlock(), BasicBlock() - check = self.check_tagged_short_int(lhs, line) + check_lhs = self.check_tagged_short_int(lhs, line) + if op in ("==", "!="): + check = check_lhs + else: + # for non-equal logical ops(less than, greater than, etc.), need to check both side + check_rhs = self.check_tagged_short_int(rhs, line) + check = self.binary_int_op(bool_rprimitive, check_lhs, + check_rhs, BinaryIntOp.AND, line) branch = Branch(check, short_int_block, int_block, Branch.BOOL_EXPR) branch.negated = False self.add(branch) diff --git a/mypyc/test-data/analysis.test b/mypyc/test-data/analysis.test index 7da0b913a07d..1cbdc4365535 100644 --- a/mypyc/test-data/analysis.test +++ b/mypyc/test-data/analysis.test @@ -370,43 +370,57 @@ def f(a): a :: int r0 :: bool r1, r2, r3 :: native_int - r4, r5, r6, r7 :: bool - r8, r9, r10 :: native_int - r11, r12, r13 :: bool + r4 :: bool + r5, r6, r7 :: native_int + r8, r9, r10, r11, r12 :: bool + r13, r14, r15 :: native_int + r16 :: bool + r17, r18, r19 :: native_int + r20, r21, r22, r23 :: bool y, x :: int - r14 :: None + r24 :: None L0: L1: r1 = 1 r2 = a & r1 r3 = 0 r4 = r2 == r3 - if r4 goto L2 else goto L3 :: bool + r5 = 1 + r6 = a & r5 + r7 = 0 + r8 = r6 == r7 + r9 = r4 & r8 + if r9 goto L2 else goto L3 :: bool L2: - r5 = a < a - r0 = r5 + r10 = a < a + r0 = r10 goto L4 L3: - r6 = CPyTagged_IsLt_(a, a) - r0 = r6 + r11 = CPyTagged_IsLt_(a, a) + r0 = r11 L4: if r0 goto L5 else goto L12 :: bool L5: L6: - r8 = 1 - r9 = a & r8 - r10 = 0 - r11 = r9 == r10 - if r11 goto L7 else goto L8 :: bool + r13 = 1 + r14 = a & r13 + r15 = 0 + r16 = r14 == r15 + r17 = 1 + r18 = a & r17 + r19 = 0 + r20 = r18 == r19 + r21 = r16 & r20 + if r21 goto L7 else goto L8 :: bool L7: - r12 = a < a - r7 = r12 + r22 = a < a + r12 = r22 goto L9 L8: - r13 = CPyTagged_IsLt_(a, a) - r7 = r13 + r23 = CPyTagged_IsLt_(a, a) + r12 = r23 L9: - if r7 goto L10 else goto L11 :: bool + if r12 goto L10 else goto L11 :: bool L10: y = a goto L6 @@ -414,40 +428,50 @@ L11: x = a goto L1 L12: - r14 = None - return r14 + r24 = None + return r24 (0, 0) {a} {a} -(1, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} -(1, 1) {a, r0, r7, x, y} {a, r0, r7, x, y} -(1, 2) {a, r0, r7, x, y} {a, r0, r7, x, y} -(1, 3) {a, r0, r7, x, y} {a, r0, r7, x, y} -(1, 4) {a, r0, r7, x, y} {a, r0, r7, x, y} -(2, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} -(2, 1) {a, r0, r7, x, y} {a, r0, r7, x, y} -(2, 2) {a, r0, r7, x, y} {a, r0, r7, x, y} -(3, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} -(3, 1) {a, r0, r7, x, y} {a, r0, r7, x, y} -(3, 2) {a, r0, r7, x, y} {a, r0, r7, x, y} -(4, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} -(5, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} -(6, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} -(6, 1) {a, r0, r7, x, y} {a, r0, r7, x, y} -(6, 2) {a, r0, r7, x, y} {a, r0, r7, x, y} -(6, 3) {a, r0, r7, x, y} {a, r0, r7, x, y} -(6, 4) {a, r0, r7, x, y} {a, r0, r7, x, y} -(7, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} -(7, 1) {a, r0, r7, x, y} {a, r0, r7, x, y} -(7, 2) {a, r0, r7, x, y} {a, r0, r7, x, y} -(8, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} -(8, 1) {a, r0, r7, x, y} {a, r0, r7, x, y} -(8, 2) {a, r0, r7, x, y} {a, r0, r7, x, y} -(9, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} -(10, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} -(10, 1) {a, r0, r7, x, y} {a, r0, r7, x, y} -(11, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} -(11, 1) {a, r0, r7, x, y} {a, r0, r7, x, y} -(12, 0) {a, r0, r7, x, y} {a, r0, r7, x, y} -(12, 1) {a, r0, r7, x, y} {a, r0, r7, x, y} +(1, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} +(1, 1) {a, r0, r12, x, y} {a, r0, r12, x, y} +(1, 2) {a, r0, r12, x, y} {a, r0, r12, x, y} +(1, 3) {a, r0, r12, x, y} {a, r0, r12, x, y} +(1, 4) {a, r0, r12, x, y} {a, r0, r12, x, y} +(1, 5) {a, r0, r12, x, y} {a, r0, r12, x, y} +(1, 6) {a, r0, r12, x, y} {a, r0, r12, x, y} +(1, 7) {a, r0, r12, x, y} {a, r0, r12, x, y} +(1, 8) {a, r0, r12, x, y} {a, r0, r12, x, y} +(1, 9) {a, r0, r12, x, y} {a, r0, r12, x, y} +(2, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} +(2, 1) {a, r0, r12, x, y} {a, r0, r12, x, y} +(2, 2) {a, r0, r12, x, y} {a, r0, r12, x, y} +(3, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} +(3, 1) {a, r0, r12, x, y} {a, r0, r12, x, y} +(3, 2) {a, r0, r12, x, y} {a, r0, r12, x, y} +(4, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} +(5, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} +(6, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} +(6, 1) {a, r0, r12, x, y} {a, r0, r12, x, y} +(6, 2) {a, r0, r12, x, y} {a, r0, r12, x, y} +(6, 3) {a, r0, r12, x, y} {a, r0, r12, x, y} +(6, 4) {a, r0, r12, x, y} {a, r0, r12, x, y} +(6, 5) {a, r0, r12, x, y} {a, r0, r12, x, y} +(6, 6) {a, r0, r12, x, y} {a, r0, r12, x, y} +(6, 7) {a, r0, r12, x, y} {a, r0, r12, x, y} +(6, 8) {a, r0, r12, x, y} {a, r0, r12, x, y} +(6, 9) {a, r0, r12, x, y} {a, r0, r12, x, y} +(7, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} +(7, 1) {a, r0, r12, x, y} {a, r0, r12, x, y} +(7, 2) {a, r0, r12, x, y} {a, r0, r12, x, y} +(8, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} +(8, 1) {a, r0, r12, x, y} {a, r0, r12, x, y} +(8, 2) {a, r0, r12, x, y} {a, r0, r12, x, y} +(9, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} +(10, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} +(10, 1) {a, r0, r12, x, y} {a, r0, r12, x, y} +(11, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} +(11, 1) {a, r0, r12, x, y} {a, r0, r12, x, y} +(12, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} +(12, 1) {a, r0, r12, x, y} {a, r0, r12, x, y} [case testTrivial_BorrowedArgument] def f(a: int, b: int) -> int: diff --git a/mypyc/test-data/exceptions.test b/mypyc/test-data/exceptions.test index a59bcf121b85..f8a012c2167e 100644 --- a/mypyc/test-data/exceptions.test +++ b/mypyc/test-data/exceptions.test @@ -132,11 +132,13 @@ def sum(a, l): i :: int r2 :: bool r3, r4, r5 :: native_int - r6, r7, r8 :: bool - r9 :: object - r10, r11 :: int - r12 :: short_int - r13, r14 :: int + r6 :: bool + r7, r8, r9 :: native_int + r10, r11, r12, r13 :: bool + r14 :: object + r15, r16 :: int + r17 :: short_int + r18, r19 :: int L0: r0 = 0 sum = r0 @@ -147,38 +149,43 @@ L1: r4 = i & r3 r5 = 0 r6 = r4 == r5 - if r6 goto L2 else goto L3 :: bool + r7 = 1 + r8 = l & r7 + r9 = 0 + r10 = r8 == r9 + r11 = r6 & r10 + if r11 goto L2 else goto L3 :: bool L2: - r7 = i < l - r2 = r7 + r12 = i < l + r2 = r12 goto L4 L3: - r8 = CPyTagged_IsLt_(i, l) - r2 = r8 + r13 = CPyTagged_IsLt_(i, l) + r2 = r13 L4: if r2 goto L5 else goto L10 :: bool L5: - r9 = CPyList_GetItem(a, i) - if is_error(r9) goto L11 (error at sum:6) else goto L6 + r14 = CPyList_GetItem(a, i) + if is_error(r14) goto L11 (error at sum:6) else goto L6 L6: - r10 = unbox(int, r9) - dec_ref r9 - if is_error(r10) goto L11 (error at sum:6) else goto L7 + r15 = unbox(int, r14) + dec_ref r14 + if is_error(r15) goto L11 (error at sum:6) else goto L7 L7: - r11 = CPyTagged_Add(sum, r10) + r16 = CPyTagged_Add(sum, r15) dec_ref sum :: int - dec_ref r10 :: int - sum = r11 - r12 = 1 - r13 = CPyTagged_Add(i, r12) + dec_ref r15 :: int + sum = r16 + r17 = 1 + r18 = CPyTagged_Add(i, r17) dec_ref i :: int - i = r13 + i = r18 goto L1 L8: return sum L9: - r14 = :: int - return r14 + r19 = :: int + return r19 L10: dec_ref i :: int goto L8 diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 2cf2a0945461..09404cc42946 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -94,26 +94,33 @@ def f(x, y): x, y :: int r0 :: bool r1, r2, r3 :: native_int - r4, r5, r6 :: bool - r7 :: short_int + r4 :: bool + r5, r6, r7 :: native_int + r8, r9, r10, r11 :: bool + r12 :: short_int L0: r1 = 1 r2 = x & r1 r3 = 0 r4 = r2 == r3 - if r4 goto L1 else goto L2 :: bool + r5 = 1 + r6 = y & r5 + r7 = 0 + r8 = r6 == r7 + r9 = r4 & r8 + if r9 goto L1 else goto L2 :: bool L1: - r5 = x < y - r0 = r5 + r10 = x < y + r0 = r10 goto L3 L2: - r6 = CPyTagged_IsLt_(x, y) - r0 = r6 + r11 = CPyTagged_IsLt_(x, y) + r0 = r11 L3: if r0 goto L4 else goto L5 :: bool L4: - r7 = 1 - x = r7 + r12 = 1 + x = r12 L5: return x @@ -129,30 +136,37 @@ def f(x, y): x, y :: int r0 :: bool r1, r2, r3 :: native_int - r4, r5, r6 :: bool - r7, r8 :: short_int + r4 :: bool + r5, r6, r7 :: native_int + r8, r9, r10, r11 :: bool + r12, r13 :: short_int L0: r1 = 1 r2 = x & r1 r3 = 0 r4 = r2 == r3 - if r4 goto L1 else goto L2 :: bool + r5 = 1 + r6 = y & r5 + r7 = 0 + r8 = r6 == r7 + r9 = r4 & r8 + if r9 goto L1 else goto L2 :: bool L1: - r5 = x < y - r0 = r5 + r10 = x < y + r0 = r10 goto L3 L2: - r6 = CPyTagged_IsLt_(x, y) - r0 = r6 + r11 = CPyTagged_IsLt_(x, y) + r0 = r11 L3: if r0 goto L4 else goto L5 :: bool L4: - r7 = 1 - x = r7 + r12 = 1 + x = r12 goto L6 L5: - r8 = 2 - x = r8 + r13 = 2 + x = r13 L6: return x @@ -168,33 +182,40 @@ def f(x, y): x, y :: int r0 :: bool r1, r2, r3 :: native_int - r4, r5, r6, r7 :: bool - r8, r9 :: short_int + r4 :: bool + r5, r6, r7 :: native_int + r8, r9, r10, r11, r12 :: bool + r13, r14 :: short_int L0: r1 = 1 r2 = x & r1 r3 = 0 r4 = r2 == r3 - if r4 goto L1 else goto L2 :: bool + r5 = 1 + r6 = y & r5 + r7 = 0 + r8 = r6 == r7 + r9 = r4 & r8 + if r9 goto L1 else goto L2 :: bool L1: - r5 = x < y - r0 = r5 + r10 = x < y + r0 = r10 goto L3 L2: - r6 = CPyTagged_IsLt_(x, y) - r0 = r6 + r11 = CPyTagged_IsLt_(x, y) + r0 = r11 L3: if r0 goto L4 else goto L6 :: bool L4: - r7 = CPyTagged_IsGt(x, y) - if r7 goto L5 else goto L6 :: bool + r12 = CPyTagged_IsGt(x, y) + if r12 goto L5 else goto L6 :: bool L5: - r8 = 1 - x = r8 + r13 = 1 + x = r13 goto L7 L6: - r9 = 2 - x = r9 + r14 = 2 + x = r14 L7: return x @@ -232,33 +253,40 @@ def f(x, y): x, y :: int r0 :: bool r1, r2, r3 :: native_int - r4, r5, r6, r7 :: bool - r8, r9 :: short_int + r4 :: bool + r5, r6, r7 :: native_int + r8, r9, r10, r11, r12 :: bool + r13, r14 :: short_int L0: r1 = 1 r2 = x & r1 r3 = 0 r4 = r2 == r3 - if r4 goto L1 else goto L2 :: bool + r5 = 1 + r6 = y & r5 + r7 = 0 + r8 = r6 == r7 + r9 = r4 & r8 + if r9 goto L1 else goto L2 :: bool L1: - r5 = x < y - r0 = r5 + r10 = x < y + r0 = r10 goto L3 L2: - r6 = CPyTagged_IsLt_(x, y) - r0 = r6 + r11 = CPyTagged_IsLt_(x, y) + r0 = r11 L3: if r0 goto L5 else goto L4 :: bool L4: - r7 = CPyTagged_IsGt(x, y) - if r7 goto L5 else goto L6 :: bool + r12 = CPyTagged_IsGt(x, y) + if r12 goto L5 else goto L6 :: bool L5: - r8 = 1 - x = r8 + r13 = 1 + x = r13 goto L7 L6: - r9 = 2 - x = r9 + r14 = 2 + x = r14 L7: return x @@ -294,26 +322,33 @@ def f(x, y): x, y :: int r0 :: bool r1, r2, r3 :: native_int - r4, r5, r6 :: bool - r7 :: short_int + r4 :: bool + r5, r6, r7 :: native_int + r8, r9, r10, r11 :: bool + r12 :: short_int L0: r1 = 1 r2 = x & r1 r3 = 0 r4 = r2 == r3 - if r4 goto L1 else goto L2 :: bool + r5 = 1 + r6 = y & r5 + r7 = 0 + r8 = r6 == r7 + r9 = r4 & r8 + if r9 goto L1 else goto L2 :: bool L1: - r5 = x < y - r0 = r5 + r10 = x < y + r0 = r10 goto L3 L2: - r6 = CPyTagged_IsLt_(x, y) - r0 = r6 + r11 = CPyTagged_IsLt_(x, y) + r0 = r11 L3: if r0 goto L5 else goto L4 :: bool L4: - r7 = 1 - x = r7 + r12 = 1 + x = r12 L5: return x @@ -327,29 +362,36 @@ def f(x, y): x, y :: int r0 :: bool r1, r2, r3 :: native_int - r4, r5, r6, r7 :: bool - r8 :: short_int + r4 :: bool + r5, r6, r7 :: native_int + r8, r9, r10, r11, r12 :: bool + r13 :: short_int L0: r1 = 1 r2 = x & r1 r3 = 0 r4 = r2 == r3 - if r4 goto L1 else goto L2 :: bool + r5 = 1 + r6 = y & r5 + r7 = 0 + r8 = r6 == r7 + r9 = r4 & r8 + if r9 goto L1 else goto L2 :: bool L1: - r5 = x < y - r0 = r5 + r10 = x < y + r0 = r10 goto L3 L2: - r6 = CPyTagged_IsLt_(x, y) - r0 = r6 + r11 = CPyTagged_IsLt_(x, y) + r0 = r11 L3: if r0 goto L4 else goto L5 :: bool L4: - r7 = CPyTagged_IsGt(x, y) - if r7 goto L6 else goto L5 :: bool + r12 = CPyTagged_IsGt(x, y) + if r12 goto L6 else goto L5 :: bool L5: - r8 = 1 - x = r8 + r13 = 1 + x = r13 L6: return x @@ -434,34 +476,41 @@ def f(x, y): x, y :: int r0 :: bool r1, r2, r3 :: native_int - r4, r5, r6 :: bool - r7, r8 :: short_int - r9 :: None + r4 :: bool + r5, r6, r7 :: native_int + r8, r9, r10, r11 :: bool + r12, r13 :: short_int + r14 :: None L0: r1 = 1 r2 = x & r1 r3 = 0 r4 = r2 == r3 - if r4 goto L1 else goto L2 :: bool + r5 = 1 + r6 = y & r5 + r7 = 0 + r8 = r6 == r7 + r9 = r4 & r8 + if r9 goto L1 else goto L2 :: bool L1: - r5 = x < y - r0 = r5 + r10 = x < y + r0 = r10 goto L3 L2: - r6 = CPyTagged_IsLt_(x, y) - r0 = r6 + r11 = CPyTagged_IsLt_(x, y) + r0 = r11 L3: if r0 goto L4 else goto L5 :: bool L4: - r7 = 1 - x = r7 + r12 = 1 + x = r12 goto L6 L5: - r8 = 2 - y = r8 + r13 = 2 + y = r13 L6: - r9 = None - return r9 + r14 = None + return r14 [case testRecursion] def f(n: int) -> int: @@ -2682,9 +2731,11 @@ def f(x, y, z): x, y, z, r0, r1 :: int r2, r3 :: bool r4, r5, r6 :: native_int - r7, r8, r9 :: bool - r10 :: int - r11 :: bool + r7 :: bool + r8, r9, r10 :: native_int + r11, r12, r13, r14 :: bool + r15 :: int + r16 :: bool L0: r0 = g(x) r1 = g(y) @@ -2692,23 +2743,28 @@ L0: r5 = r0 & r4 r6 = 0 r7 = r5 == r6 - if r7 goto L1 else goto L2 :: bool + r8 = 1 + r9 = r1 & r8 + r10 = 0 + r11 = r9 == r10 + r12 = r7 & r11 + if r12 goto L1 else goto L2 :: bool L1: - r8 = r0 < r1 - r3 = r8 + r13 = r0 < r1 + r3 = r13 goto L3 L2: - r9 = CPyTagged_IsLt_(r0, r1) - r3 = r9 + r14 = CPyTagged_IsLt_(r0, r1) + r3 = r14 L3: if r3 goto L5 else goto L4 :: bool L4: r2 = r3 goto L6 L5: - r10 = g(z) - r11 = CPyTagged_IsGt(r1, r10) - r2 = r11 + r15 = g(z) + r16 = CPyTagged_IsGt(r1, r15) + r2 = r16 L6: return r2 From 53ec9f351a848cf2ada8439488e44653e4d534dc Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Wed, 15 Jul 2020 21:20:06 +0800 Subject: [PATCH 067/138] [mypyc] Support swapping operands and negating result and merge int NEQ (#9149) This PR implements support for swapping operands and negating result when building logical ops mentioned in https://github.com/python/mypy/pull/9148#issuecomment-658123381. To demonstrate, NEQ is merged. Since it has no IR test, I built one in irbuild-int.test. --- mypyc/irbuild/ll_builder.py | 15 ++++++++++++--- mypyc/primitives/int_ops.py | 21 +++++++++++++++++---- mypyc/test-data/irbuild-basic.test | 2 +- mypyc/test-data/irbuild-int.test | 25 +++++++++++++++++++++++++ mypyc/test/test_irbuild.py | 1 + 5 files changed, 56 insertions(+), 8 deletions(-) create mode 100644 mypyc/test-data/irbuild-int.test diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index dfddf6da5bae..99b1b6795e27 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -573,7 +573,7 @@ def check_tagged_short_int(self, val: Value, line: int) -> Value: def compare_tagged(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: """Compare two tagged integers using given op""" - op_type, c_func_desc = int_logical_op_mapping[op] + op_type, c_func_desc, negate_result, swap_op = int_logical_op_mapping[op] result = self.alloc_temp(bool_rprimitive) short_int_block, int_block, out = BasicBlock(), BasicBlock(), BasicBlock() check_lhs = self.check_tagged_short_int(lhs, line) @@ -592,8 +592,17 @@ def compare_tagged(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: self.add(Assign(result, eq, line)) self.goto(out) self.activate_block(int_block) - call = self.call_c(c_func_desc, [lhs, rhs], line) - self.add(Assign(result, call, line)) + if swap_op: + args = [rhs, lhs] + else: + args = [lhs, rhs] + call = self.call_c(c_func_desc, args, line) + if negate_result: + # TODO: introduce UnaryIntOp? + call_result = self.unary_op(call, "not", line) + else: + call_result = call + self.add(Assign(result, call_result, line)) self.goto_and_activate(out) return result diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index 0eeee5760823..6e38a071ba38 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -6,7 +6,7 @@ See also the documentation for mypyc.rtypes.int_rprimitive. """ -from typing import Dict, Tuple +from typing import Dict, NamedTuple from mypyc.ir.ops import ERR_NEVER, ERR_MAGIC, BinaryIntOp from mypyc.ir.rtypes import ( int_rprimitive, bool_rprimitive, float_rprimitive, object_rprimitive, short_int_rprimitive, @@ -143,6 +143,18 @@ def int_unary_op(name: str, c_function_name: str) -> CFunctionDescription: # integer comparsion operation implementation related: +# Description for building int logical ops +# For each field: +# binary_op_variant: identify which BinaryIntOp to use when operands are short integers +# c_func_description: the C function to call when operands are tagged integers +# c_func_negated: whether to negate the C function call's result +# c_func_swap_operands: whether to swap lhs and rhs when call the function +IntLogicalOpDescrption = NamedTuple( + 'IntLogicalOpDescrption', [('binary_op_variant', int), + ('c_func_description', CFunctionDescription), + ('c_func_negated', bool), + ('c_func_swap_operands', bool)]) + # description for equal operation on two boxed tagged integers int_equal_ = c_custom_op( arg_types=[int_rprimitive, int_rprimitive], @@ -159,6 +171,7 @@ def int_unary_op(name: str, c_function_name: str) -> CFunctionDescription: # provide mapping from textual op to short int's op variant and boxed int's description # note these are not complete implementations int_logical_op_mapping = { - '==': (BinaryIntOp.EQ, int_equal_), - '<': (BinaryIntOp.LT, int_less_than_) -} # type: Dict[str, Tuple[int, CFunctionDescription]] + '==': IntLogicalOpDescrption(BinaryIntOp.EQ, int_equal_, False, False), + '!=': IntLogicalOpDescrption(BinaryIntOp.NEQ, int_equal_, True, False), + '<': IntLogicalOpDescrption(BinaryIntOp.LT, int_less_than_, False, False) +} # type: Dict[str, IntLogicalOpDescrption] diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 09404cc42946..b54869b3c6af 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -1358,7 +1358,7 @@ def lst(x): L0: r0 = len x :: list r1 = 0 - r2 = CPyTagged_IsNe(r0, r1) + r2 = r0 != r1 if r2 goto L1 else goto L2 :: bool L1: r3 = 1 diff --git a/mypyc/test-data/irbuild-int.test b/mypyc/test-data/irbuild-int.test new file mode 100644 index 000000000000..74d5861713f4 --- /dev/null +++ b/mypyc/test-data/irbuild-int.test @@ -0,0 +1,25 @@ +[case testIntNeq] +def f(x: int, y: int) -> bool: + return x != y +[out] +def f(x, y): + x, y :: int + r0 :: bool + r1, r2, r3 :: native_int + r4, r5, r6, r7 :: bool +L0: + r1 = 1 + r2 = x & r1 + r3 = 0 + r4 = r2 == r3 + if r4 goto L1 else goto L2 :: bool +L1: + r5 = x != y + r0 = r5 + goto L3 +L2: + r6 = CPyTagged_IsEq_(x, y) + r7 = !r6 + r0 = r7 +L3: + return r0 diff --git a/mypyc/test/test_irbuild.py b/mypyc/test/test_irbuild.py index 10adb6bed435..ce23f3f50290 100644 --- a/mypyc/test/test_irbuild.py +++ b/mypyc/test/test_irbuild.py @@ -28,6 +28,7 @@ 'irbuild-try.test', 'irbuild-set.test', 'irbuild-strip-asserts.test', + 'irbuild-int.test', ] From 5e310192264fca9734299fabcec0211419949aab Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Fri, 17 Jul 2020 19:06:19 +0800 Subject: [PATCH 068/138] [mypyc] LoadInt store doubled value if tagged integer (#9162) Mypyc currently represents int and short_int using a tagged representation, which requires doubling the value when emitting to C. Since we are moving towards low-level IR, we change LoadInt to store the doubled value directly if the type is int/short_int, to be explicit about the tagged representation. --- mypyc/codegen/emitfunc.py | 7 +- mypyc/ir/ops.py | 5 +- mypyc/test-data/analysis.test | 60 +++---- mypyc/test-data/exceptions.test | 8 +- mypyc/test-data/irbuild-basic.test | 198 ++++++++++----------- mypyc/test-data/irbuild-classes.test | 38 ++-- mypyc/test-data/irbuild-dict.test | 14 +- mypyc/test-data/irbuild-generics.test | 8 +- mypyc/test-data/irbuild-lists.test | 24 +-- mypyc/test-data/irbuild-nested.test | 16 +- mypyc/test-data/irbuild-optional.test | 28 +-- mypyc/test-data/irbuild-set.test | 30 ++-- mypyc/test-data/irbuild-statements.test | 98 +++++----- mypyc/test-data/irbuild-strip-asserts.test | 4 +- mypyc/test-data/irbuild-tuple.test | 22 +-- mypyc/test-data/refcount.test | 82 ++++----- 16 files changed, 321 insertions(+), 321 deletions(-) diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index b94694d8039c..f7b62fd74e36 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -14,7 +14,7 @@ NAMESPACE_TYPE, NAMESPACE_MODULE, RaiseStandardError, CallC, LoadGlobal, Truncate, BinaryIntOp ) -from mypyc.ir.rtypes import RType, RTuple, is_int32_rprimitive, is_int64_rprimitive +from mypyc.ir.rtypes import RType, RTuple from mypyc.ir.func_ir import FuncIR, FuncDecl, FUNC_STATICMETHOD, FUNC_CLASSMETHOD from mypyc.ir.class_ir import ClassIR @@ -175,10 +175,7 @@ def visit_assign(self, op: Assign) -> None: def visit_load_int(self, op: LoadInt) -> None: dest = self.reg(op) - if is_int32_rprimitive(op.type) or is_int64_rprimitive(op.type): - self.emit_line('%s = %d;' % (dest, op.value)) - else: - self.emit_line('%s = %d;' % (dest, op.value * 2)) + self.emit_line('%s = %d;' % (dest, op.value)) def visit_load_error_value(self, op: LoadErrorValue) -> None: if isinstance(op.type, RTuple): diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index efcf7b011001..b09b53cc354b 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -791,7 +791,10 @@ class LoadInt(RegisterOp): def __init__(self, value: int, line: int = -1, rtype: RType = short_int_rprimitive) -> None: super().__init__(line) - self.value = value + if is_short_int_rprimitive(rtype) or is_int_rprimitive(rtype): + self.value = value * 2 + else: + self.value = value self.type = rtype def sources(self) -> List[Value]: diff --git a/mypyc/test-data/analysis.test b/mypyc/test-data/analysis.test index 1cbdc4365535..23755a94bc5d 100644 --- a/mypyc/test-data/analysis.test +++ b/mypyc/test-data/analysis.test @@ -21,7 +21,7 @@ def f(a): z :: int r10 :: None L0: - r0 = 1 + r0 = 2 x = r0 r2 = 1 r3 = x & r2 @@ -38,11 +38,11 @@ L2: L3: if r1 goto L4 else goto L5 :: bool L4: - r8 = 1 + r8 = 2 y = r8 goto L6 L5: - r9 = 1 + r9 = 2 z = r9 L6: r10 = None @@ -85,9 +85,9 @@ def f(a): r1 :: short_int r2 :: bool L0: - r0 = 1 + r0 = 2 x = r0 - r1 = 1 + r1 = 2 r2 = CPyTagged_IsEq(x, r1) if r2 goto L1 else goto L2 :: bool L1: @@ -119,11 +119,11 @@ def f(): y :: int r2 :: short_int L0: - r0 = 1 + r0 = 2 x = r0 - r1 = 1 + r1 = 2 y = r1 - r2 = 2 + r2 = 4 x = r2 return x (0, 0) {} {r0} @@ -145,11 +145,11 @@ def f(a): a :: int r0, r1, r2 :: short_int L0: - r0 = 1 + r0 = 2 a = r0 - r1 = 2 + r1 = 4 a = r1 - r2 = 3 + r2 = 6 a = r2 return a (0, 0) {} {r0} @@ -179,17 +179,17 @@ def f(a): r4 :: short_int r5 :: None L0: - r0 = 1 + r0 = 2 r1 = CPyTagged_IsEq(a, r0) if r1 goto L1 else goto L2 :: bool L1: - r2 = 1 + r2 = 2 y = r2 - r3 = 2 + r3 = 4 x = r3 goto L3 L2: - r4 = 2 + r4 = 4 x = r4 L3: r5 = None @@ -233,11 +233,11 @@ def f(n): r4 :: None L0: L1: - r0 = 5 + r0 = 10 r1 = CPyTagged_IsLt(n, r0) if r1 goto L2 else goto L3 :: bool L2: - r2 = 1 + r2 = 2 r3 = CPyTagged_Add(n, r2) n = r3 m = n @@ -280,22 +280,22 @@ def f(n): r6 :: short_int r7 :: None L0: - r0 = 1 + r0 = 2 x = r0 - r1 = 1 + r1 = 2 y = r1 L1: - r2 = 1 + r2 = 2 r3 = CPyTagged_IsLt(n, r2) if r3 goto L2 else goto L6 :: bool L2: n = y L3: - r4 = 2 + r4 = 4 r5 = CPyTagged_IsLt(n, r4) if r5 goto L4 else goto L5 :: bool L4: - r6 = 1 + r6 = 2 n = r6 n = x goto L3 @@ -335,7 +335,7 @@ def f(x): r0 :: short_int r1, a, r2, r3, r4 :: int L0: - r0 = 1 + r0 = 2 r1 = f(r0) if is_error(r1) goto L3 (error at f:2) else goto L1 L1: @@ -494,7 +494,7 @@ def f(a): r0 :: short_int L0: b = a - r0 = 1 + r0 = 2 a = r0 return a (0, 0) {a} {a} @@ -535,13 +535,13 @@ L2: L3: if r0 goto L4 else goto L5 :: bool L4: - r7 = 2 + r7 = 4 x = r7 - r8 = 1 + r8 = 2 a = r8 goto L6 L5: - r9 = 1 + r9 = 2 x = r9 L6: return x @@ -597,7 +597,7 @@ L1: L2: r3 = CPyTagged_Add(sum, i) sum = r3 - r4 = 1 + r4 = 2 r5 = CPyTagged_Add(i, r4) i = r5 goto L1 @@ -659,7 +659,7 @@ L3: r5 = CPy_ExceptionMatches(r4) if r5 goto L4 else goto L5 :: bool L4: - r6 = 1 + r6 = 2 r7 = CPyTagged_Negate(r6) CPy_RestoreExcInfo(r1) return r7 @@ -679,7 +679,7 @@ L8: L9: unreachable L10: - r9 = 1 + r9 = 2 r10 = CPyTagged_Add(st, r9) return r10 L11: diff --git a/mypyc/test-data/exceptions.test b/mypyc/test-data/exceptions.test index f8a012c2167e..6c0909683ddb 100644 --- a/mypyc/test-data/exceptions.test +++ b/mypyc/test-data/exceptions.test @@ -90,7 +90,7 @@ L0: r2 = x is r1 if r2 goto L1 else goto L2 :: bool L1: - r3 = 1 + r3 = 2 return r3 L2: inc_ref x @@ -104,10 +104,10 @@ L3: r8 = !r7 if r8 goto L4 else goto L5 :: bool L4: - r9 = 2 + r9 = 4 return r9 L5: - r10 = 3 + r10 = 6 return r10 L6: r11 = :: int @@ -176,7 +176,7 @@ L7: dec_ref sum :: int dec_ref r15 :: int sum = r16 - r17 = 1 + r17 = 2 r18 = CPyTagged_Add(i, r17) dec_ref i :: int i = r18 diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index b54869b3c6af..04257cb88b5a 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -5,7 +5,7 @@ def f() -> int: def f(): r0 :: short_int L0: - r0 = 1 + r0 = 2 return r0 [case testFunctionArgument] @@ -47,7 +47,7 @@ def f(): r0 :: short_int x, y :: int L0: - r0 = 1 + r0 = 2 x = r0 y = x return y @@ -64,7 +64,7 @@ def f(x): y :: int r1 :: None L0: - r0 = 1 + r0 = 2 y = r0 y = x r1 = None @@ -79,7 +79,7 @@ def f(x, y): r0 :: short_int r1, r2 :: int L0: - r0 = 1 + r0 = 2 r1 = CPyTagged_Add(y, r0) r2 = CPyTagged_Multiply(x, r1) return r2 @@ -119,7 +119,7 @@ L2: L3: if r0 goto L4 else goto L5 :: bool L4: - r12 = 1 + r12 = 2 x = r12 L5: return x @@ -161,11 +161,11 @@ L2: L3: if r0 goto L4 else goto L5 :: bool L4: - r12 = 1 + r12 = 2 x = r12 goto L6 L5: - r13 = 2 + r13 = 4 x = r13 L6: return x @@ -210,11 +210,11 @@ L4: r12 = CPyTagged_IsGt(x, y) if r12 goto L5 else goto L6 :: bool L5: - r13 = 1 + r13 = 2 x = r13 goto L7 L6: - r14 = 2 + r14 = 4 x = r14 L7: return x @@ -281,11 +281,11 @@ L4: r12 = CPyTagged_IsGt(x, y) if r12 goto L5 else goto L6 :: bool L5: - r13 = 1 + r13 = 2 x = r13 goto L7 L6: - r14 = 2 + r14 = 4 x = r14 L7: return x @@ -347,7 +347,7 @@ L2: L3: if r0 goto L5 else goto L4 :: bool L4: - r12 = 1 + r12 = 2 x = r12 L5: return x @@ -390,7 +390,7 @@ L4: r12 = CPyTagged_IsGt(x, y) if r12 goto L6 else goto L5 :: bool L5: - r13 = 1 + r13 = 2 x = r13 L6: return x @@ -429,7 +429,7 @@ def f(x, y): r1 :: bool r2 :: int L0: - r0 = 1 + r0 = 2 x = r0 L1: r1 = CPyTagged_IsGt(x, y) @@ -460,7 +460,7 @@ def f(): x :: int r1 :: None L0: - r0 = 1 + r0 = 2 x = r0 r1 = None return r1 @@ -502,11 +502,11 @@ L2: L3: if r0 goto L4 else goto L5 :: bool L4: - r12 = 1 + r12 = 2 x = r12 goto L6 L5: - r13 = 2 + r13 = 4 y = r13 L6: r14 = None @@ -528,17 +528,17 @@ def f(n): r6 :: short_int r7, r8, r9 :: int L0: - r0 = 1 + r0 = 2 r1 = CPyTagged_IsLe(n, r0) if r1 goto L1 else goto L2 :: bool L1: - r2 = 1 + r2 = 2 return r2 L2: - r3 = 1 + r3 = 2 r4 = CPyTagged_Subtract(n, r3) r5 = f(r4) - r6 = 2 + r6 = 4 r7 = CPyTagged_Subtract(n, r6) r8 = f(r7) r9 = CPyTagged_Add(r5, r8) @@ -582,7 +582,7 @@ L0: r1 = CPyTagged_IsLt(n, r0) if r1 goto L1 else goto L2 :: bool L1: - r2 = 1 + r2 = 2 x = r2 goto L6 L2: @@ -590,11 +590,11 @@ L2: r4 = CPyTagged_IsEq(n, r3) if r4 goto L3 else goto L4 :: bool L3: - r5 = 1 + r5 = 2 x = r5 goto L5 L4: - r6 = 2 + r6 = 4 x = r6 L5: L6: @@ -609,7 +609,7 @@ def f(n): r0 :: short_int r1 :: int L0: - r0 = 1 + r0 = 2 r1 = CPyTagged_Negate(r0) return r1 @@ -632,7 +632,7 @@ L1: r2 = r3 goto L3 L2: - r4 = 1 + r4 = 2 r2 = r4 L3: return r2 @@ -651,7 +651,7 @@ def f(): L0: r0 = 0 x = r0 - r1 = 1 + r1 = 2 r2 = CPyTagged_Add(x, r1) x = r2 return x @@ -764,7 +764,7 @@ L0: r0 = builtins :: module r1 = unicode_1 :: static ('print') r2 = getattr r0, r1 - r3 = 5 + r3 = 10 r4 = box(short_int, r3) r5 = py_call(r2, r4) r6 = None @@ -783,7 +783,7 @@ def f(x): r3, r4, r5 :: object r6 :: None L0: - r0 = 5 + r0 = 10 r1 = builtins :: module r2 = unicode_1 :: static ('print') r3 = getattr r1, r2 @@ -859,7 +859,7 @@ def g(y): r7, r8 :: None L0: r0 = g(y) - r1 = 1 + r1 = 2 r2 = box(short_int, r1) r3 = [r2] r4 = g(r3) @@ -891,13 +891,13 @@ def g(y): r12 :: short_int r13 :: object L0: - r0 = 1 + r0 = 2 r1 = box(short_int, r0) r2 = g(r1) r3 = [y] a = r3 - r4 = 1 - r5 = 2 + r4 = 2 + r5 = 4 r6 = (r4, r5) r7 = 0 r8 = box(tuple[int, int], r6) @@ -905,7 +905,7 @@ L0: r10 = True r11 = box(bool, r10) y = r11 - r12 = 3 + r12 = 6 r13 = box(short_int, r12) return r13 @@ -927,7 +927,7 @@ def f(a, o): r4 :: object r5 :: None L0: - r0 = 1 + r0 = 2 r1 = box(short_int, r0) a.x = r1; r2 = is_error r3 = a.n @@ -1135,7 +1135,7 @@ L0: d_64_bit = r5 r6 = int_7 :: static (2147483647) max_32_bit = r6 - r7 = 1073741823 + r7 = 2147483646 max_31_bit = r7 r8 = None return r8 @@ -1241,7 +1241,7 @@ def call_python_function_with_keyword_arg(x): r7 :: object r8 :: int L0: - r0 = 2 + r0 = 4 r1 = int r2 = unicode_3 :: static ('base') r3 = (x) :: tuple @@ -1284,7 +1284,7 @@ L0: r7 = box(int, first) r8 = CPyDict_Build(r6, r3, r7) r9 = py_call_with_kwargs(r2, r5, r8) - r10 = 1 + r10 = 2 r11 = unicode_4 :: static ('insert') r12 = getattr xs, r11 r13 = unicode_5 :: static ('x') @@ -1326,7 +1326,7 @@ L0: r0 = bool x :: object if r0 goto L1 else goto L2 :: bool L1: - r1 = 1 + r1 = 2 return r1 L2: r2 = 0 @@ -1343,7 +1343,7 @@ L0: r1 = CPyTagged_IsNe(x, r0) if r1 goto L1 else goto L2 :: bool L1: - r2 = 1 + r2 = 2 return r2 L2: r3 = 0 @@ -1361,7 +1361,7 @@ L0: r2 = r0 != r1 if r2 goto L1 else goto L2 :: bool L1: - r3 = 1 + r3 = 2 return r3 L2: r4 = 0 @@ -1410,7 +1410,7 @@ L1: r4 = CPyTagged_IsNe(r2, r3) if r4 goto L2 else goto L3 :: bool L2: - r5 = 1 + r5 = 2 return r5 L3: r6 = 0 @@ -1427,7 +1427,7 @@ L0: r1 = x is not r0 if r1 goto L1 else goto L2 :: bool L1: - r2 = 1 + r2 = 2 return r2 L2: r3 = 0 @@ -1450,7 +1450,7 @@ L1: r3 = bool r2 :: object if r3 goto L2 else goto L3 :: bool L2: - r4 = 1 + r4 = 2 return r4 L3: r5 = 0 @@ -1543,7 +1543,7 @@ L1: r4 = import r3 :: str builtins = r4 :: module L2: - r5 = 1 + r5 = 2 r6 = __main__.globals :: static r7 = unicode_1 :: static ('x') r8 = box(short_int, r5) @@ -1582,7 +1582,7 @@ L0: r0 = m :: module r1 = unicode_2 :: static ('f') r2 = getattr r0, r1 - r3 = 1 + r3 = 2 r4 = box(short_int, r3) r5 = py_call(r2, r4) r6 = cast(str, r5) @@ -1702,7 +1702,7 @@ L0: r0 = unicode_1 :: static ('a') r1 = 0 r2 = f(r1, r0) - r3 = 1 + r3 = 2 r4 = unicode_2 :: static ('b') r5 = f(r3, r4) r6 = None @@ -1736,7 +1736,7 @@ L0: r0 = unicode_4 :: static ('a') r1 = 0 r2 = a.f(r1, r0) - r3 = 1 + r3 = 2 r4 = unicode_5 :: static ('b') r5 = a.f(r3, r4) r6 = None @@ -1770,9 +1770,9 @@ def g(): r12 :: object r13 :: tuple[int, int, int] L0: - r0 = 1 - r1 = 2 - r2 = 3 + r0 = 2 + r1 = 4 + r2 = 6 r3 = (r0, r1, r2) r4 = __main__.globals :: static r5 = unicode_3 :: static ('f') @@ -1798,9 +1798,9 @@ def h(): r13 :: object r14 :: tuple[int, int, int] L0: - r0 = 1 - r1 = 2 - r2 = 3 + r0 = 2 + r1 = 4 + r2 = 6 r3 = (r1, r2) r4 = __main__.globals :: static r5 = unicode_3 :: static ('f') @@ -1849,11 +1849,11 @@ def g(): r18 :: tuple[int, int, int] L0: r0 = unicode_3 :: static ('a') - r1 = 1 + r1 = 2 r2 = unicode_4 :: static ('b') - r3 = 2 + r3 = 4 r4 = unicode_5 :: static ('c') - r5 = 3 + r5 = 6 r6 = 3 r7 = box(short_int, r1) r8 = box(short_int, r3) @@ -1885,11 +1885,11 @@ def h(): r16 :: object r17 :: tuple[int, int, int] L0: - r0 = 1 + r0 = 2 r1 = unicode_4 :: static ('b') - r2 = 2 + r2 = 4 r3 = unicode_5 :: static ('c') - r4 = 3 + r4 = 6 r5 = 2 r6 = box(short_int, r2) r7 = box(short_int, r4) @@ -1922,7 +1922,7 @@ def f(x, y, z): L0: if is_error(y) goto L1 else goto L2 L1: - r0 = 3 + r0 = 6 y = r0 L2: if is_error(z) goto L3 else goto L4 @@ -1941,12 +1941,12 @@ def g(): r6 :: str r7, r8 :: None L0: - r0 = 2 + r0 = 4 r1 = :: int r2 = :: str r3 = f(r0, r1, r2) - r4 = 3 - r5 = 6 + r4 = 6 + r5 = 12 r6 = :: str r7 = f(r5, r4, r6) r8 = None @@ -1972,7 +1972,7 @@ def A.f(self, x, y, z): L0: if is_error(y) goto L1 else goto L2 L1: - r0 = 3 + r0 = 6 y = r0 L2: if is_error(z) goto L3 else goto L4 @@ -1994,12 +1994,12 @@ def g(): L0: r0 = A() a = r0 - r1 = 2 + r1 = 4 r2 = :: int r3 = :: str r4 = a.f(r1, r2, r3) - r5 = 3 - r6 = 6 + r5 = 6 + r6 = 12 r7 = :: str r8 = a.f(r6, r5, r7) r9 = None @@ -2030,9 +2030,9 @@ def f(): r21, r22 :: short_int L0: r0 = [] - r1 = 1 - r2 = 2 - r3 = 3 + r1 = 2 + r2 = 4 + r3 = 6 r4 = box(short_int, r1) r5 = box(short_int, r2) r6 = box(short_int, r3) @@ -2047,13 +2047,13 @@ L2: r12 = r7[r9] :: unsafe list r13 = unbox(int, r12) x = r13 - r14 = 2 + r14 = 4 r15 = CPyTagged_IsNe(x, r14) if r15 goto L4 else goto L3 :: bool L3: goto L7 L4: - r16 = 3 + r16 = 6 r17 = CPyTagged_IsNe(x, r16) if r17 goto L6 else goto L5 :: bool L5: @@ -2063,7 +2063,7 @@ L6: r19 = box(int, r18) r20 = PyList_Append(r0, r19) L7: - r21 = 1 + r21 = 2 r22 = r9 + r21 r9 = r22 goto L1 @@ -2094,9 +2094,9 @@ def f(): r22, r23 :: short_int L0: r0 = PyDict_New() - r1 = 1 - r2 = 2 - r3 = 3 + r1 = 2 + r2 = 4 + r3 = 6 r4 = box(short_int, r1) r5 = box(short_int, r2) r6 = box(short_int, r3) @@ -2111,13 +2111,13 @@ L2: r12 = r7[r9] :: unsafe list r13 = unbox(int, r12) x = r13 - r14 = 2 + r14 = 4 r15 = CPyTagged_IsNe(x, r14) if r15 goto L4 else goto L3 :: bool L3: goto L7 L4: - r16 = 3 + r16 = 6 r17 = CPyTagged_IsNe(x, r16) if r17 goto L6 else goto L5 :: bool L5: @@ -2128,7 +2128,7 @@ L6: r20 = box(int, r18) r21 = CPyDict_SetItem(r0, r19, r20) L7: - r22 = 1 + r22 = 2 r23 = r9 + r22 r9 = r23 goto L1 @@ -2178,7 +2178,7 @@ L2: r8 = r5[2] z = r8 L3: - r9 = 1 + r9 = 2 r10 = r1 + r9 r1 = r10 goto L1 @@ -2204,7 +2204,7 @@ L6: r23 = box(int, r22) r24 = PyList_Append(r11, r23) L7: - r25 = 1 + r25 = 2 r26 = r13 + r25 r13 = r26 goto L5 @@ -2259,7 +2259,7 @@ def PropertyHolder.twice_value(self): r0 :: short_int r1, r2 :: int L0: - r0 = 2 + r0 = 4 r1 = self.value r2 = CPyTagged_Multiply(r0, r1) return r2 @@ -2333,7 +2333,7 @@ def BaseProperty.next(self): r3 :: __main__.BaseProperty L0: r0 = self._incrementer - r1 = 1 + r1 = 2 r2 = CPyTagged_Add(r0, r1) r3 = BaseProperty(r2) return r3 @@ -2486,7 +2486,7 @@ def SubclassedTrait.boxed(self): r0 :: short_int r1 :: object L0: - r0 = 3 + r0 = 6 r1 = box(short_int, r0) return r1 def DerivingObject.this(self): @@ -2502,7 +2502,7 @@ def DerivingObject.boxed(self): self :: __main__.DerivingObject r0 :: short_int L0: - r0 = 5 + r0 = 10 return r0 def DerivingObject.boxed__SubclassedTrait_glue(__mypyc_self__): __mypyc_self__ :: __main__.DerivingObject @@ -2670,7 +2670,7 @@ L4: r39 = __main__.globals :: static r40 = unicode_5 :: static ('Lol') r41 = CPyDict_SetItem(r39, r40, r38) - r42 = 1 + r42 = 2 r43 = unicode_8 :: static r44 = __main__.globals :: static r45 = unicode_5 :: static ('Lol') @@ -2700,9 +2700,9 @@ L4: r69 = __main__.globals :: static r70 = unicode_11 :: static ('Bar') r71 = CPyDict_SetItem(r69, r70, r68) - r72 = 1 - r73 = 2 - r74 = 3 + r72 = 2 + r73 = 4 + r74 = 6 r75 = box(short_int, r72) r76 = box(short_int, r73) r77 = box(short_int, r74) @@ -3308,10 +3308,10 @@ def f(a): L0: if a goto L1 else goto L2 :: bool L1: - r0 = 1 + r0 = 2 return r0 L2: - r1 = 2 + r1 = 4 return r1 L3: unreachable @@ -3387,9 +3387,9 @@ def C.__mypyc_defaults_setup(__mypyc_self__): r2 :: short_int r3, r4 :: bool L0: - r0 = 1 + r0 = 2 __mypyc_self__.x = r0; r1 = is_error - r2 = 2 + r2 = 4 __mypyc_self__.y = r2; r3 = is_error r4 = True return r4 @@ -3399,10 +3399,10 @@ def f(a): L0: if a goto L1 else goto L2 :: bool L1: - r0 = 1 + r0 = 2 return r0 L2: - r1 = 2 + r1 = 4 return r1 L3: unreachable @@ -3475,7 +3475,7 @@ L1: raise NameError('value for final name "x" was not set') unreachable L2: - r2 = 1 + r2 = 2 r3 = CPyTagged_Subtract(r0, r2) return r3 @@ -3495,7 +3495,7 @@ def foo(z): r0 :: short_int r1 :: None L0: - r0 = 10 + r0 = 20 r1 = None return r1 @@ -3534,7 +3534,7 @@ L0: r0 = x.__bool__() if r0 goto L1 else goto L2 :: bool L1: - r1 = 1 + r1 = 2 return r1 L2: r2 = 0 diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index eabe0c3f45ea..42d0e361fb9c 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -25,7 +25,7 @@ def f(a): r1 :: bool r2 :: None L0: - r0 = 1 + r0 = 2 a.x = r0; r1 = is_error r2 = None return r2 @@ -55,7 +55,7 @@ def f(): L0: r0 = C() c = r0 - r1 = 5 + r1 = 10 c.x = r1; r2 = is_error r3 = [c] a = r3 @@ -64,7 +64,7 @@ L0: r6 = cast(__main__.C, r5) d = r6 r7 = d.x - r8 = 1 + r8 = 2 r9 = CPyTagged_Add(r7, r8) return r9 @@ -83,7 +83,7 @@ def A.f(self, x, y): r0 :: short_int r1 :: int L0: - r0 = 10 + r0 = 20 r1 = CPyTagged_Add(x, r0) return r1 def g(a): @@ -93,7 +93,7 @@ def g(a): r2 :: int r3 :: None L0: - r0 = 1 + r0 = 2 r1 = unicode_4 :: static ('hi') r2 = a.f(r0, r1) r3 = None @@ -142,14 +142,14 @@ L0: r4 = !r3 if r4 goto L1 else goto L2 :: bool L1: - r5 = 1 + r5 = 2 r6 = self.next r7 = cast(__main__.Node, r6) r8 = r7.length() r9 = CPyTagged_Add(r5, r8) return r9 L2: - r10 = 1 + r10 = 2 return r10 [case testSubclass] @@ -167,7 +167,7 @@ def A.__init__(self): r1 :: bool r2 :: None L0: - r0 = 10 + r0 = 20 self.x = r0; r1 = is_error r2 = None return r2 @@ -179,9 +179,9 @@ def B.__init__(self): r3 :: bool r4 :: None L0: - r0 = 20 + r0 = 40 self.x = r0; r1 = is_error - r2 = 30 + r2 = 60 self.y = r2; r3 = is_error r4 = None return r4 @@ -201,7 +201,7 @@ def O.__init__(self): r1 :: bool r2 :: None L0: - r0 = 1 + r0 = 2 self.x = r0; r1 = is_error r2 = None return r2 @@ -213,7 +213,7 @@ def increment(o): r3 :: bool L0: r0 = o.x - r1 = 1 + r1 = 2 r2 = CPyTagged_Add(r0, r1) o.x = r2; r3 = is_error return o @@ -704,7 +704,7 @@ def C.foo(x): r0 :: short_int r1 :: int L0: - r0 = 10 + r0 = 20 r1 = CPyTagged_Add(r0, x) return r1 def C.bar(cls, x): @@ -713,7 +713,7 @@ def C.bar(cls, x): r0 :: short_int r1 :: int L0: - r0 = 10 + r0 = 20 r1 = CPyTagged_Add(r0, x) return r1 def lol(): @@ -723,10 +723,10 @@ def lol(): r3 :: short_int r4, r5 :: int L0: - r0 = 1 + r0 = 2 r1 = C.foo(r0) r2 = __main__.C :: type - r3 = 2 + r3 = 4 r4 = C.bar(r2, r3) r5 = CPyTagged_Add(r1, r4) return r5 @@ -1017,7 +1017,7 @@ def A.lol(self): r1 :: bool r2 :: None L0: - r0 = 100 + r0 = 200 self.x = r0; r1 = is_error r2 = None return r2 @@ -1026,7 +1026,7 @@ def A.__mypyc_defaults_setup(__mypyc_self__): r0 :: short_int r1, r2 :: bool L0: - r0 = 10 + r0 = 20 __mypyc_self__.x = r0; r1 = is_error r2 = True return r2 @@ -1043,7 +1043,7 @@ def B.__mypyc_defaults_setup(__mypyc_self__): r8 :: object r9, r10, r11, r12 :: bool L0: - r0 = 10 + r0 = 20 __mypyc_self__.x = r0; r1 = is_error r2 = __main__.globals :: static r3 = unicode_9 :: static ('LOL') diff --git a/mypyc/test-data/irbuild-dict.test b/mypyc/test-data/irbuild-dict.test index c4e5bfe185c5..1b62a8188a03 100644 --- a/mypyc/test-data/irbuild-dict.test +++ b/mypyc/test-data/irbuild-dict.test @@ -63,8 +63,8 @@ def f(x): r6, d :: dict r7 :: None L0: - r0 = 1 - r1 = 2 + r0 = 2 + r1 = 4 r2 = unicode_1 :: static r3 = 2 r4 = box(short_int, r0) @@ -89,7 +89,7 @@ def f(d): r2 :: int32 r3, r4, r5 :: bool L0: - r0 = 4 + r0 = 8 r1 = box(short_int, r0) r2 = PyDict_Contains(d, r1) r3 = truncate r2: int32 to builtins.bool @@ -118,7 +118,7 @@ def f(d): r2 :: int32 r3, r4, r5, r6 :: bool L0: - r0 = 4 + r0 = 8 r1 = box(short_int, r0) r2 = PyDict_Contains(d, r1) r3 = truncate r2: int32 to builtins.bool @@ -186,7 +186,7 @@ L2: r8 = cast(str, r7) k = r8 r9 = CPyDict_GetItem(d, k) - r10 = 1 + r10 = 2 r11 = box(short_int, r10) r12 = r9 += r11 r13 = CPyDict_SetItem(d, k, r12) @@ -216,9 +216,9 @@ def f(x, y): r7 :: object r8 :: int32 L0: - r0 = 2 + r0 = 4 r1 = unicode_3 :: static ('z') - r2 = 3 + r2 = 6 r3 = 1 r4 = box(short_int, r0) r5 = CPyDict_Build(r3, x, r4) diff --git a/mypyc/test-data/irbuild-generics.test b/mypyc/test-data/irbuild-generics.test index 08c90cac7bf0..eae3b058b708 100644 --- a/mypyc/test-data/irbuild-generics.test +++ b/mypyc/test-data/irbuild-generics.test @@ -62,10 +62,10 @@ def f(): L0: r0 = C() c = r0 - r1 = 1 + r1 = 2 r2 = box(short_int, r1) c.x = r2; r3 = is_error - r4 = 2 + r4 = 4 r5 = c.x r6 = unbox(int, r5) r7 = CPyTagged_Add(r4, r6) @@ -128,11 +128,11 @@ L0: r0 = x.get() r1 = unbox(int, r0) y = r1 - r2 = 1 + r2 = 2 r3 = CPyTagged_Add(y, r2) r4 = box(int, r3) r5 = x.set(r4) - r6 = 2 + r6 = 4 r7 = box(short_int, r6) r8 = C(r7) x = r8 diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test index 9a47f632ef55..dbe0d22a7fb6 100644 --- a/mypyc/test-data/irbuild-lists.test +++ b/mypyc/test-data/irbuild-lists.test @@ -47,7 +47,7 @@ L0: r0 = 0 r1 = CPyList_GetItemShort(x, r0) r2 = cast(list, r1) - r3 = 1 + r3 = 2 r4 = CPyList_GetItemShort(r2, r3) r5 = unbox(int, r4) return r5 @@ -64,7 +64,7 @@ def f(x): r3 :: bool r4 :: None L0: - r0 = 1 + r0 = 2 r1 = 0 r2 = box(short_int, r0) r3 = CPyList_SetItem(x, r1, r2) @@ -96,8 +96,8 @@ def f(): r4, x :: list r5 :: None L0: - r0 = 1 - r1 = 2 + r0 = 2 + r1 = 4 r2 = box(short_int, r0) r3 = box(short_int, r1) r4 = [r2, r3] @@ -120,11 +120,11 @@ def f(a): r5, r6 :: list r7 :: None L0: - r0 = 2 + r0 = 4 r1 = CPySequence_Multiply(a, r0) b = r1 - r2 = 3 - r3 = 4 + r2 = 6 + r3 = 8 r4 = box(short_int, r3) r5 = [r4] r6 = CPySequence_RMultiply(r2, r5) @@ -189,12 +189,12 @@ L1: if r3 goto L2 else goto L4 :: bool L2: r4 = CPyList_GetItem(l, i) - r5 = 1 + r5 = 2 r6 = box(short_int, r5) r7 = r4 += r6 r8 = CPyList_SetItem(l, i, r7) L3: - r9 = 1 + r9 = 2 r10 = r2 + r9 r2 = r10 i = r10 @@ -215,9 +215,9 @@ def f(x, y): r6, r7, r8 :: object r9 :: int32 L0: - r0 = 1 - r1 = 2 - r2 = 3 + r0 = 2 + r1 = 4 + r2 = 6 r3 = box(short_int, r0) r4 = box(short_int, r1) r5 = [r3, r4] diff --git a/mypyc/test-data/irbuild-nested.test b/mypyc/test-data/irbuild-nested.test index 711e6ea8af3d..ae3ebe9bcf42 100644 --- a/mypyc/test-data/irbuild-nested.test +++ b/mypyc/test-data/irbuild-nested.test @@ -344,9 +344,9 @@ L0: r0 = __mypyc_self__.__mypyc_env__ r1 = r0.inner inner = r1 - r2 = 4 + r2 = 8 r0.num = r2; r3 = is_error - r4 = 6 + r4 = 12 foo = r4 r5 = r0.num return r5 @@ -360,7 +360,7 @@ def b(): r8, r9, r10 :: int L0: r0 = b_env() - r1 = 3 + r1 = 6 r0.num = r1; r2 = is_error r3 = inner_b_obj() r3.__mypyc_env__ = r0; r4 = is_error @@ -517,7 +517,7 @@ L0: r2 = b_a_env() r2.__mypyc_env__ = r0; r3 = is_error r4 = r0.x - r5 = 1 + r5 = 2 r6 = CPyTagged_Add(r4, r5) r0.x = r6; r7 = is_error r8 = c_a_b_obj() @@ -537,7 +537,7 @@ def a(): r8 :: int L0: r0 = a_env() - r1 = 1 + r1 = 2 r0.x = r1; r2 = is_error r3 = b_a_obj() r3.__mypyc_env__ = r0; r4 = is_error @@ -675,7 +675,7 @@ L0: r1 = r0.foo foo = r1 r2 = r0.a - r3 = 1 + r3 = 2 r4 = CPyTagged_Add(r2, r3) return r4 def bar_f_obj.__get__(__mypyc_self__, instance, owner): @@ -739,7 +739,7 @@ L1: r4 = 0 return r4 L2: - r5 = 1 + r5 = 2 r6 = CPyTagged_Subtract(n, r5) r7 = box(int, r6) r8 = py_call(baz, r7) @@ -881,7 +881,7 @@ L1: r2 = 0 return r2 L2: - r3 = 1 + r3 = 2 r4 = CPyTagged_Subtract(n, r3) r5 = baz(r4) r6 = CPyTagged_Add(n, r5) diff --git a/mypyc/test-data/irbuild-optional.test b/mypyc/test-data/irbuild-optional.test index b6d5f5f8ccb3..7099fafc5698 100644 --- a/mypyc/test-data/irbuild-optional.test +++ b/mypyc/test-data/irbuild-optional.test @@ -20,10 +20,10 @@ L0: r2 = x is r1 if r2 goto L1 else goto L2 :: bool L1: - r3 = 1 + r3 = 2 return r3 L2: - r4 = 2 + r4 = 4 return r4 [case testIsNotNone] @@ -49,10 +49,10 @@ L0: r3 = !r2 if r3 goto L1 else goto L2 :: bool L1: - r4 = 1 + r4 = 2 return r4 L2: - r5 = 2 + r5 = 4 return r5 [case testIsTruthy] @@ -75,10 +75,10 @@ L0: r1 = x is not r0 if r1 goto L1 else goto L2 :: bool L1: - r2 = 1 + r2 = 2 return r2 L2: - r3 = 2 + r3 = 4 return r3 [case testIsTruthyOverride] @@ -118,10 +118,10 @@ L1: r3 = bool r2 :: object if r3 goto L2 else goto L3 :: bool L2: - r4 = 1 + r4 = 2 return r4 L3: - r5 = 2 + r5 = 4 return r5 [case testAssignToOptional] @@ -162,12 +162,12 @@ L0: r2 = A() x = r2 x = y - r3 = 1 + r3 = 2 r4 = box(short_int, r3) z = r4 r5 = A() a = r5 - r6 = 1 + r6 = 2 r7 = box(short_int, r6) a.a = r7; r8 = is_error r9 = None @@ -199,7 +199,7 @@ L0: r2 = box(short_int, r0) r3 = CPyList_SetItem(x, r1, r2) r4 = None - r5 = 1 + r5 = 2 r6 = box(None, r4) r7 = CPyList_SetItem(x, r5, r6) r8 = None @@ -265,7 +265,7 @@ L0: r0 = None r1 = box(None, r0) x = r1 - r2 = 1 + r2 = 2 r3 = CPyTagged_IsEq(y, r2) if r3 goto L1 else goto L2 :: bool L1: @@ -313,7 +313,7 @@ L0: if r2 goto L1 else goto L2 :: bool L1: r3 = unbox(int, x) - r4 = 1 + r4 = 2 r5 = CPyTagged_Add(r3, r4) return r5 L2: @@ -440,7 +440,7 @@ def g(o): r15, z :: object r16 :: None L0: - r0 = 1 + r0 = 2 r2 = __main__.A :: type r3 = type_is o, r2 if r3 goto L1 else goto L2 :: bool diff --git a/mypyc/test-data/irbuild-set.test b/mypyc/test-data/irbuild-set.test index 9ffee68de5b2..ccffc1af5351 100644 --- a/mypyc/test-data/irbuild-set.test +++ b/mypyc/test-data/irbuild-set.test @@ -13,9 +13,9 @@ def f(): r8 :: object r9 :: int32 L0: - r0 = 1 - r1 = 2 - r2 = 3 + r0 = 2 + r1 = 4 + r2 = 6 r3 = set r4 = box(short_int, r0) r5 = PySet_Add(r3, r4) @@ -64,9 +64,9 @@ def f(): r9 :: int32 r10 :: int L0: - r0 = 1 - r1 = 2 - r2 = 3 + r0 = 2 + r1 = 4 + r2 = 6 r3 = set r4 = box(short_int, r0) r5 = PySet_Add(r3, r4) @@ -96,15 +96,15 @@ def f(): r9 :: int32 r10 :: bool L0: - r0 = 3 - r1 = 4 + r0 = 6 + r1 = 8 r2 = set r3 = box(short_int, r0) r4 = PySet_Add(r2, r3) r5 = box(short_int, r1) r6 = PySet_Add(r2, r5) x = r2 - r7 = 5 + r7 = 10 r8 = box(short_int, r7) r9 = PySet_Contains(x, r8) r10 = truncate r9: int32 to builtins.bool @@ -126,7 +126,7 @@ def f(): L0: r0 = set x = r0 - r1 = 1 + r1 = 2 r2 = box(short_int, r1) r3 = CPySet_Remove(x, r2) r4 = None @@ -148,7 +148,7 @@ def f(): L0: r0 = set x = r0 - r1 = 1 + r1 = 2 r2 = box(short_int, r1) r3 = PySet_Discard(x, r2) r4 = None @@ -170,7 +170,7 @@ def f(): L0: r0 = set x = r0 - r1 = 1 + r1 = 2 r2 = box(short_int, r1) r3 = PySet_Add(x, r2) r4 = None @@ -240,9 +240,9 @@ def f(x, y): r10 :: object r11 :: int32 L0: - r0 = 1 - r1 = 2 - r2 = 3 + r0 = 2 + r1 = 4 + r2 = 6 r3 = set r4 = box(short_int, r0) r5 = PySet_Add(r3, r4) diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index 528e0a85c4e9..58b361a18aa3 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -17,7 +17,7 @@ L0: r0 = 0 x = r0 r1 = 0 - r2 = 5 + r2 = 10 r3 = r1 i = r3 L1: @@ -27,7 +27,7 @@ L2: r5 = CPyTagged_Add(x, i) x = r5 L3: - r6 = 1 + r6 = 2 r7 = r3 + r6 r3 = r7 i = r7 @@ -48,7 +48,7 @@ def f(): r4, r5 :: short_int r6 :: None L0: - r0 = 10 + r0 = 20 r1 = 0 r2 = r0 i = r2 @@ -57,7 +57,7 @@ L1: if r3 goto L2 else goto L4 :: bool L2: L3: - r4 = -1 + r4 = -2 r5 = r2 + r4 r2 = r5 i = r5 @@ -82,7 +82,7 @@ L0: r0 = 0 n = r0 L1: - r1 = 5 + r1 = 10 r2 = CPyTagged_IsLt(n, r1) if r2 goto L2 else goto L3 :: bool L2: @@ -103,7 +103,7 @@ def f(): r6 :: None L0: r0 = 0 - r1 = 5 + r1 = 10 r2 = r0 n = r2 L1: @@ -112,7 +112,7 @@ L1: L2: goto L4 L3: - r4 = 1 + r4 = 2 r5 = r2 + r4 r2 = r5 n = r5 @@ -141,12 +141,12 @@ L0: r0 = 0 n = r0 L1: - r1 = 5 + r1 = 10 r2 = CPyTagged_IsLt(n, r1) if r2 goto L2 else goto L6 :: bool L2: L3: - r3 = 4 + r3 = 8 r4 = CPyTagged_IsLt(n, r3) if r4 goto L4 else goto L5 :: bool L4: @@ -171,7 +171,7 @@ L0: r0 = 0 n = r0 L1: - r1 = 5 + r1 = 10 r2 = CPyTagged_IsLt(n, r1) if r2 goto L2 else goto L3 :: bool L2: @@ -193,7 +193,7 @@ def f(): r6 :: None L0: r0 = 0 - r1 = 5 + r1 = 10 r2 = r0 n = r2 L1: @@ -201,7 +201,7 @@ L1: if r3 goto L2 else goto L4 :: bool L2: L3: - r4 = 1 + r4 = 2 r5 = r2 + r4 r2 = r5 n = r5 @@ -230,12 +230,12 @@ L0: r0 = 0 n = r0 L1: - r1 = 5 + r1 = 10 r2 = CPyTagged_IsLt(n, r1) if r2 goto L2 else goto L6 :: bool L2: L3: - r3 = 4 + r3 = 8 r4 = CPyTagged_IsLt(n, r3) if r4 goto L4 else goto L5 :: bool L4: @@ -280,7 +280,7 @@ L2: r7 = CPyTagged_Add(y, x) y = r7 L3: - r8 = 1 + r8 = 2 r9 = r2 + r8 r2 = r9 goto L1 @@ -387,7 +387,7 @@ L2: r10 = box(int, key) r11 = CPyDict_GetItem(d, r10) r12 = unbox(int, r11) - r13 = 2 + r13 = 4 r14 = CPyTagged_Remainder(r12, r13) r15 = 0 r16 = CPyTagged_IsNe(r14, r15) @@ -589,7 +589,7 @@ L1: raise AssertionError unreachable L2: - r1 = 1 + r1 = 2 return r1 def literal_msg(x): x :: object @@ -602,7 +602,7 @@ L1: raise AssertionError('message') unreachable L2: - r2 = 2 + r2 = 4 return r2 def complex_msg(x, s): x :: union[str, None] @@ -651,13 +651,13 @@ def delList(): r7 :: bool r8 :: None L0: - r0 = 1 - r1 = 2 + r0 = 2 + r1 = 4 r2 = box(short_int, r0) r3 = box(short_int, r1) r4 = [r2, r3] l = r4 - r5 = 1 + r5 = 2 r6 = box(short_int, r5) r7 = l.__delitem__(r6) :: object r8 = None @@ -675,13 +675,13 @@ def delListMultiple(): r23 :: bool r24 :: None L0: - r0 = 1 - r1 = 2 - r2 = 3 - r3 = 4 - r4 = 5 - r5 = 6 - r6 = 7 + r0 = 2 + r1 = 4 + r2 = 6 + r3 = 8 + r4 = 10 + r5 = 12 + r6 = 14 r7 = box(short_int, r0) r8 = box(short_int, r1) r9 = box(short_int, r2) @@ -691,9 +691,9 @@ L0: r13 = box(short_int, r6) r14 = [r7, r8, r9, r10, r11, r12, r13] l = r14 - r15 = 1 - r16 = 2 - r17 = 3 + r15 = 2 + r16 = 4 + r17 = 6 r18 = box(short_int, r15) r19 = l.__delitem__(r18) :: object r20 = box(short_int, r16) @@ -724,9 +724,9 @@ def delDict(): r10 :: None L0: r0 = unicode_1 :: static ('one') - r1 = 1 + r1 = 2 r2 = unicode_2 :: static ('two') - r3 = 2 + r3 = 4 r4 = 2 r5 = box(short_int, r1) r6 = box(short_int, r3) @@ -753,13 +753,13 @@ def delDictMultiple(): r18 :: None L0: r0 = unicode_1 :: static ('one') - r1 = 1 + r1 = 2 r2 = unicode_2 :: static ('two') - r3 = 2 + r3 = 4 r4 = unicode_3 :: static ('three') - r5 = 3 + r5 = 6 r6 = unicode_4 :: static ('four') - r7 = 4 + r7 = 8 r8 = 4 r9 = box(short_int, r1) r10 = box(short_int, r3) @@ -803,8 +803,8 @@ def delAttribute(): r4 :: bool r5 :: None L0: - r0 = 1 - r1 = 2 + r0 = 2 + r1 = 4 r2 = Dummy(r0, r1) dummy = r2 r3 = unicode_3 :: static ('x') @@ -820,8 +820,8 @@ def delAttributeMultiple(): r6 :: bool r7 :: None L0: - r0 = 1 - r1 = 2 + r0 = 2 + r1 = 4 r2 = Dummy(r0, r1) dummy = r2 r3 = unicode_3 :: static ('x') @@ -867,11 +867,11 @@ L2: x = r7 r8 = CPyTagged_Add(i, x) L3: - r9 = 1 + r9 = 2 r10 = r1 + r9 r1 = r10 i = r10 - r11 = 1 + r11 = 2 r12 = r3 + r11 r3 = r12 goto L1 @@ -900,7 +900,7 @@ L2: r4 = unbox(int, r3) n = r4 L3: - r5 = 1 + r5 = 2 r6 = r1 + r5 r1 = r6 i = r6 @@ -956,11 +956,11 @@ L3: r9 = bool b :: object if r9 goto L4 else goto L5 :: bool L4: - r10 = 1 + r10 = 2 x = r10 L5: L6: - r11 = 1 + r11 = 2 r12 = r1 + r11 r1 = r12 goto L1 @@ -989,7 +989,7 @@ L0: r1 = 0 r2 = r1 r3 = 0 - r4 = 5 + r4 = 10 r5 = r3 z = r5 L1: @@ -1011,10 +1011,10 @@ L4: r13 = False x = r13 L5: - r14 = 1 + r14 = 2 r15 = r2 + r14 r2 = r15 - r16 = 1 + r16 = 2 r17 = r5 + r16 r5 = r17 z = r17 diff --git a/mypyc/test-data/irbuild-strip-asserts.test b/mypyc/test-data/irbuild-strip-asserts.test index bffb86ed4d3b..8ee062a8c123 100644 --- a/mypyc/test-data/irbuild-strip-asserts.test +++ b/mypyc/test-data/irbuild-strip-asserts.test @@ -9,8 +9,8 @@ def g(): r2 :: int r3, x :: object L0: - r0 = 1 - r1 = 2 + r0 = 2 + r1 = 4 r2 = CPyTagged_Add(r0, r1) r3 = box(int, r2) x = r3 diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index 477d387a369b..f4d2b4d81ff0 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -27,7 +27,7 @@ def f(): r3 :: int L0: r0 = True - r1 = 1 + r1 = 2 r2 = (r0, r1) t = r2 r3 = t[1] @@ -42,7 +42,7 @@ def f(x): x :: tuple[bool, bool, int] r0 :: short_int L0: - r0 = 3 + r0 = 6 return r0 [case testSequenceTuple] @@ -58,7 +58,7 @@ def f(x): r3 :: bool L0: r0 = PyList_AsTuple(x) - r1 = 1 + r1 = 2 r2 = CPySequenceTuple_GetItem(r0, r1) r3 = unbox(bool, r2) return r3 @@ -90,12 +90,12 @@ def f(): r5 :: object r6 :: int L0: - r0 = 1 - r1 = 2 + r0 = 2 + r1 = 4 r2 = (r0, r1) r3 = box(tuple[int, int], r2) t = r3 - r4 = 1 + r4 = 2 r5 = CPySequenceTuple_GetItem(t, r4) r6 = unbox(int, r5) return r6 @@ -114,9 +114,9 @@ def f(x, y): r9 :: int32 r10 :: tuple L0: - r0 = 1 - r1 = 2 - r2 = 3 + r0 = 2 + r1 = 4 + r2 = 6 r3 = box(short_int, r0) r4 = box(short_int, r1) r5 = [r3, r4] @@ -154,7 +154,7 @@ L2: r5 = cast(str, r4) x = r5 L3: - r6 = 1 + r6 = 2 r7 = r1 + r6 r1 = r7 goto L1 @@ -189,7 +189,7 @@ L1: r2 = unbox(int, r1) return r2 L2: - r3 = 1 + r3 = 2 r4 = CPySequenceTuple_GetItem(nt, r3) r5 = unbox(int, r4) return r5 diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index 216454e6d92c..fa957b876932 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -7,7 +7,7 @@ def f() -> int: def f(): r0 :: short_int L0: - r0 = 1 + r0 = 2 return r0 [case testReturnLocal] @@ -19,7 +19,7 @@ def f(): r0 :: short_int x :: int L0: - r0 = 1 + r0 = 2 x = r0 return x @@ -34,7 +34,7 @@ def f(): r0 :: short_int x, y :: int L0: - r0 = 1 + r0 = 2 x = r0 y = x x = y @@ -51,7 +51,7 @@ def f(): r0 :: short_int x, y, z, r1 :: int L0: - r0 = 1 + r0 = 2 x = r0 inc_ref x :: int y = x @@ -77,11 +77,11 @@ def f(): r2 :: short_int r3 :: bool L0: - r0 = 1 + r0 = 2 x = r0 - r1 = 2 + r1 = 4 y = r1 - r2 = 1 + r2 = 2 r3 = CPyTagged_IsEq(x, r2) if r3 goto L3 else goto L4 :: bool L1: @@ -106,7 +106,7 @@ def f(a, b): r0 :: short_int r1, x, r2, y :: int L0: - r0 = 1 + r0 = 2 r1 = CPyTagged_Add(a, r0) x = r1 r2 = CPyTagged_Add(x, a) @@ -131,7 +131,7 @@ L0: dec_ref x :: int inc_ref a :: int y = a - r0 = 1 + r0 = 2 x = r0 r1 = CPyTagged_Add(x, y) dec_ref x :: int @@ -149,7 +149,7 @@ def f(a): r0 :: short_int y :: int L0: - r0 = 1 + r0 = 2 a = r0 y = a return y @@ -165,13 +165,13 @@ def f(a): a :: int r0, r1, r2 :: short_int L0: - r0 = 1 + r0 = 2 a = r0 dec_ref a :: int - r1 = 2 + r1 = 4 a = r1 dec_ref a :: int - r2 = 3 + r2 = 6 a = r2 return a @@ -187,7 +187,7 @@ def f(a): r0 :: short_int x, y :: int L0: - r0 = 1 + r0 = 2 x = r0 inc_ref x :: int a = x @@ -239,16 +239,16 @@ L2: L3: if r0 goto L4 else goto L5 :: bool L4: - r7 = 1 + r7 = 2 a = r7 goto L6 L5: - r8 = 2 + r8 = 4 x = r8 dec_ref x :: int goto L7 L6: - r9 = 1 + r9 = 2 r10 = CPyTagged_Add(a, r9) dec_ref a :: int y = r10 @@ -291,15 +291,15 @@ L2: L3: if r0 goto L4 else goto L5 :: bool L4: - r7 = 2 + r7 = 4 x = r7 dec_ref x :: int goto L7 L5: - r8 = 1 + r8 = 2 a = r8 L6: - r9 = 1 + r9 = 2 r10 = CPyTagged_Add(a, r9) dec_ref a :: int y = r10 @@ -336,7 +336,7 @@ L2: L3: if r0 goto L4 else goto L6 :: bool L4: - r7 = 1 + r7 = 2 a = r7 L5: return a @@ -359,7 +359,7 @@ def f(a): L0: inc_ref a :: int a = a - r0 = 1 + r0 = 2 x = r0 inc_ref x :: int dec_ref x :: int @@ -385,12 +385,12 @@ def f(a): r3 :: short_int r4, r5 :: int L0: - r0 = 1 + r0 = 2 r1 = CPyTagged_Add(a, r0) a = r1 - r2 = 1 + r2 = 2 x = r2 - r3 = 1 + r3 = 2 r4 = CPyTagged_Add(x, r3) dec_ref x :: int x = r4 @@ -411,9 +411,9 @@ def f(): r2 :: int r3 :: None L0: - r0 = 1 + r0 = 2 x = r0 - r1 = 1 + r1 = 2 r2 = CPyTagged_Add(x, r1) dec_ref x :: int x = r2 @@ -433,9 +433,9 @@ def f(): r2, x :: int r3 :: None L0: - r0 = 1 + r0 = 2 y = r0 - r1 = 1 + r1 = 2 r2 = CPyTagged_Add(y, r1) dec_ref y :: int x = r2 @@ -492,7 +492,7 @@ L0: r0 = CPyTagged_Add(a, a) x = r0 dec_ref x :: int - r1 = 1 + r1 = 2 y = r1 r2 = CPyTagged_Add(y, y) dec_ref y :: int @@ -516,7 +516,7 @@ L0: r0 = CPyTagged_Add(a, a) a = r0 dec_ref a :: int - r1 = 1 + r1 = 2 x = r1 r2 = CPyTagged_Add(x, x) dec_ref x :: int @@ -548,11 +548,11 @@ def f(): r10 :: short_int a, r11, r12 :: int L0: - r0 = 1 + r0 = 2 x = r0 - r1 = 2 + r1 = 4 y = r1 - r2 = 3 + r2 = 6 z = r2 r4 = 1 r5 = z & r4 @@ -571,7 +571,7 @@ L3: L4: return z L5: - r10 = 1 + r10 = 2 a = r10 r11 = CPyTagged_Add(x, y) dec_ref x :: int @@ -619,7 +619,7 @@ L2: r3 = CPyTagged_Add(sum, i) dec_ref sum :: int sum = r3 - r4 = 1 + r4 = 2 r5 = CPyTagged_Add(i, r4) dec_ref i :: int i = r5 @@ -639,7 +639,7 @@ def f(a): r0 :: short_int r1, r2 :: int L0: - r0 = 1 + r0 = 2 r1 = CPyTagged_Add(a, r0) r2 = f(r1) dec_ref r1 :: int @@ -661,7 +661,7 @@ def f(): r5 :: short_int L0: r0 = 0 - r1 = 1 + r1 = 2 r2 = box(short_int, r0) r3 = box(short_int, r1) r4 = [r2, r3] @@ -769,10 +769,10 @@ def f(x): L0: if x goto L1 else goto L2 :: bool L1: - r0 = 1 + r0 = 2 return r0 L2: - r1 = 2 + r1 = 4 return r1 [case testUnicodeLiteral] @@ -802,7 +802,7 @@ def g(x): r7 :: object r8 :: int L0: - r0 = 2 + r0 = 4 r1 = int r2 = unicode_1 :: static ('base') r3 = (x) :: tuple From 46f8b5133b4d32ba9228227ee1cfdac3522296b5 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Fri, 17 Jul 2020 19:19:55 +0800 Subject: [PATCH 069/138] [mypyc] Refactor analysis package (#9164) Create new mypyc package `analysis` and move analysis.py into analysis/dataflow.py. --- mypyc/analysis/__init__.py | 0 mypyc/{analysis.py => analysis/dataflow.py} | 0 mypyc/test/test_analysis.py | 12 ++++++------ mypyc/transform/refcount.py | 2 +- mypyc/transform/uninit.py | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) create mode 100644 mypyc/analysis/__init__.py rename mypyc/{analysis.py => analysis/dataflow.py} (100%) diff --git a/mypyc/analysis/__init__.py b/mypyc/analysis/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/mypyc/analysis.py b/mypyc/analysis/dataflow.py similarity index 100% rename from mypyc/analysis.py rename to mypyc/analysis/dataflow.py diff --git a/mypyc/test/test_analysis.py b/mypyc/test/test_analysis.py index 1df3efbb698b..a903d593ffd4 100644 --- a/mypyc/test/test_analysis.py +++ b/mypyc/test/test_analysis.py @@ -7,7 +7,7 @@ from mypy.errors import CompileError from mypyc.common import TOP_LEVEL_NAME -from mypyc import analysis +from mypyc.analysis import dataflow from mypyc.transform import exceptions from mypyc.ir.func_ir import format_func from mypyc.test.testutil import ( @@ -42,25 +42,25 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: continue exceptions.insert_exception_handling(fn) actual.extend(format_func(fn)) - cfg = analysis.get_cfg(fn.blocks) + cfg = dataflow.get_cfg(fn.blocks) args = set(reg for reg, i in fn.env.indexes.items() if i < len(fn.args)) name = testcase.name if name.endswith('_MaybeDefined'): # Forward, maybe - analysis_result = analysis.analyze_maybe_defined_regs(fn.blocks, cfg, args) + analysis_result = dataflow.analyze_maybe_defined_regs(fn.blocks, cfg, args) elif name.endswith('_Liveness'): # Backward, maybe - analysis_result = analysis.analyze_live_regs(fn.blocks, cfg) + analysis_result = dataflow.analyze_live_regs(fn.blocks, cfg) elif name.endswith('_MustDefined'): # Forward, must - analysis_result = analysis.analyze_must_defined_regs( + analysis_result = dataflow.analyze_must_defined_regs( fn.blocks, cfg, args, regs=fn.env.regs()) elif name.endswith('_BorrowedArgument'): # Forward, must - analysis_result = analysis.analyze_borrowed_arguments(fn.blocks, cfg, args) + analysis_result = dataflow.analyze_borrowed_arguments(fn.blocks, cfg, args) else: assert False, 'No recognized _AnalysisName suffix in test case' diff --git a/mypyc/transform/refcount.py b/mypyc/transform/refcount.py index 7bd7efa683f7..2018cf32f800 100644 --- a/mypyc/transform/refcount.py +++ b/mypyc/transform/refcount.py @@ -18,7 +18,7 @@ from typing import Dict, Iterable, List, Set, Tuple -from mypyc.analysis import ( +from mypyc.analysis.dataflow import ( get_cfg, analyze_must_defined_regs, analyze_live_regs, diff --git a/mypyc/transform/uninit.py b/mypyc/transform/uninit.py index 8f477bf44d4b..25197400bd06 100644 --- a/mypyc/transform/uninit.py +++ b/mypyc/transform/uninit.py @@ -2,7 +2,7 @@ from typing import List -from mypyc.analysis import ( +from mypyc.analysis.dataflow import ( get_cfg, cleanup_cfg, analyze_must_defined_regs, From c2fbfe1af1c38534544a27c6811aabf5ca7d672f Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Sat, 18 Jul 2020 00:08:30 -0700 Subject: [PATCH 070/138] Fix misleading follow_imports error message in dmypy (#9167) We support normal now, so we shouldn't say that only skip and error are supported. That said, I don't think anybody uses silent (should we delete it?), so I only hit this because of the bug in #9165. --- mypy/dmypy_server.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index 62f3fdab4414..52db838d038e 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -141,10 +141,8 @@ def process_start_options(flags: List[str], allow_sources: bool) -> Options: "pass it to check/recheck instead") if not options.incremental: sys.exit("dmypy: start/restart should not disable incremental mode") - # Our file change tracking can't yet handle changes to files that aren't - # specified in the sources list. if options.follow_imports not in ('skip', 'error', 'normal'): - sys.exit("dmypy: follow-imports must be 'skip' or 'error'") + sys.exit("dmypy: follow-imports=silent not supported") return options From 28829fbb684d7535680934b401c05698db5b4d85 Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Sat, 18 Jul 2020 00:09:46 -0700 Subject: [PATCH 071/138] Validate `follow_imports` values in mypy.ini (#9165) Currently we only validate the values on the commandline, which allows bogus values to show up which will cause us to take the `else` branch anywhere we consider follow_imports, which isn't likely to do anything particularly correct. --- mypy/config_parser.py | 11 +++++++++++ test-data/unit/cmdline.test | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index 21a9c7306b91..7e1f16f56b25 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -63,6 +63,16 @@ def split_and_match_files(paths: str) -> List[str]: return expanded_paths +def check_follow_imports(choice: str) -> str: + choices = ['normal', 'silent', 'skip', 'error'] + if choice not in choices: + raise argparse.ArgumentTypeError( + "invalid choice '{}' (choose from {})".format( + choice, + ', '.join("'{}'".format(x) for x in choices))) + return choice + + # For most options, the type of the default value set in options.py is # sufficient, and we don't have to do anything here. This table # exists to specify types for values initialized to None or container @@ -79,6 +89,7 @@ def split_and_match_files(paths: str) -> List[str]: # These two are for backwards compatibility 'silent_imports': bool, 'almost_silent': bool, + 'follow_imports': check_follow_imports, 'no_site_packages': bool, 'plugins': lambda s: [p.strip() for p in s.split(',')], 'always_true': lambda s: [p.strip() for p in s.split(',')], diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index 9bcb66a78a7f..c8fbb512da01 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -452,6 +452,16 @@ main.py:6: note: Revealed type is 'builtins.int' main.py:7: note: Revealed type is 'Any' main.py:8: note: Revealed type is 'Any' +[case testConfigFollowImportsInvalid] +# cmd: mypy main.py +[file mypy.ini] +\[mypy] +follow_imports =True +[file main.py] +[out] +mypy.ini: [mypy]: follow_imports: invalid choice 'True' (choose from 'normal', 'silent', 'skip', 'error') +== Return code: 0 + [case testConfigSilentMissingImportsOff] # cmd: mypy main.py [file main.py] From 4cf246f3bb2589d343fe1fdb67a42a23a23c041b Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Mon, 20 Jul 2020 17:43:46 +0800 Subject: [PATCH 072/138] [mypyc] Fix signed integer comparison (#9163) https://github.com/python/mypy/commit/358522e28cd58d95daf36256c46eb7ffcc55eea4 generates inline comparison between short ints, explicit conversion to signed is missing, though, causing negative cases to fail. This PR adds explicit type casts (although the name truncate here is a little bit misleading). This PR will fix microbenchmark `int_list`. --- mypyc/codegen/emitfunc.py | 31 ++++++++++++++++++++-- mypyc/ir/ops.py | 34 ++++++++++++++++++------- mypyc/ir/rtypes.py | 4 +++ mypyc/primitives/int_ops.py | 2 +- mypyc/test-data/analysis.test | 4 +-- mypyc/test-data/exceptions.test | 2 +- mypyc/test-data/irbuild-basic.test | 24 ++++++++--------- mypyc/test-data/irbuild-lists.test | 2 +- mypyc/test-data/irbuild-statements.test | 16 ++++++------ mypyc/test/test_emitfunc.py | 27 ++++++++++++++++++-- 10 files changed, 108 insertions(+), 38 deletions(-) diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index f7b62fd74e36..531d4b2c0b1e 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -14,7 +14,9 @@ NAMESPACE_TYPE, NAMESPACE_MODULE, RaiseStandardError, CallC, LoadGlobal, Truncate, BinaryIntOp ) -from mypyc.ir.rtypes import RType, RTuple +from mypyc.ir.rtypes import ( + RType, RTuple, is_tagged, is_int32_rprimitive, is_int64_rprimitive +) from mypyc.ir.func_ir import FuncIR, FuncDecl, FUNC_STATICMETHOD, FUNC_CLASSMETHOD from mypyc.ir.class_ir import ClassIR @@ -438,7 +440,18 @@ def visit_binary_int_op(self, op: BinaryIntOp) -> None: dest = self.reg(op) lhs = self.reg(op.lhs) rhs = self.reg(op.rhs) - self.emit_line('%s = %s %s %s;' % (dest, lhs, op.op_str[op.op], rhs)) + lhs_cast = "" + rhs_cast = "" + signed_op = {BinaryIntOp.SLT, BinaryIntOp.SGT, BinaryIntOp.SLE, BinaryIntOp.SGE} + unsigned_op = {BinaryIntOp.ULT, BinaryIntOp.UGT, BinaryIntOp.ULE, BinaryIntOp.UGE} + if op.op in signed_op: + lhs_cast = self.emit_signed_int_cast(op.lhs.type) + rhs_cast = self.emit_signed_int_cast(op.rhs.type) + elif op.op in unsigned_op: + lhs_cast = self.emit_unsigned_int_cast(op.lhs.type) + rhs_cast = self.emit_unsigned_int_cast(op.rhs.type) + self.emit_line('%s = %s%s %s %s%s;' % (dest, lhs_cast, lhs, + op.op_str[op.op], rhs_cast, rhs)) # Helpers @@ -482,3 +495,17 @@ def emit_traceback(self, op: Branch) -> None: globals_static)) if DEBUG_ERRORS: self.emit_line('assert(PyErr_Occurred() != NULL && "failure w/o err!");') + + def emit_signed_int_cast(self, type: RType) -> str: + if is_tagged(type): + return '(Py_ssize_t)' + else: + return '' + + def emit_unsigned_int_cast(self, type: RType) -> str: + if is_int32_rprimitive(type): + return '(uint32_t)' + elif is_int64_rprimitive(type): + return '(uint64_t)' + else: + return '' diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index b09b53cc354b..f0f77d9b8a66 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -1270,12 +1270,17 @@ class BinaryIntOp(RegisterOp): DIV = 3 # type: Final MOD = 4 # type: Final # logical + # S for signed and U for unsigned EQ = 100 # type: Final NEQ = 101 # type: Final - LT = 102 # type: Final - GT = 103 # type: Final - LEQ = 104 # type: Final - GEQ = 105 # type: Final + SLT = 102 # type: Final + SGT = 103 # type: Final + SLE = 104 # type: Final + SGE = 105 # type: Final + ULT = 106 # type: Final + UGT = 107 # type: Final + ULE = 108 # type: Final + UGE = 109 # type: Final # bitwise AND = 200 # type: Final OR = 201 # type: Final @@ -1291,10 +1296,14 @@ class BinaryIntOp(RegisterOp): MOD: '%', EQ: '==', NEQ: '!=', - LT: '<', - GT: '>', - LEQ: '<=', - GEQ: '>=', + SLT: '<', + SGT: '>', + SLE: '<=', + SGE: '>=', + ULT: '<', + UGT: '>', + ULE: '<=', + UGE: '>=', AND: '&', OR: '|', XOR: '^', @@ -1313,7 +1322,14 @@ def sources(self) -> List[Value]: return [self.lhs, self.rhs] def to_str(self, env: Environment) -> str: - return env.format('%r = %r %s %r', self, self.lhs, self.op_str[self.op], self.rhs) + if self.op in (self.SLT, self.SGT, self.SLE, self.SGE): + sign_format = " :: signed" + elif self.op in (self.ULT, self.UGT, self.ULE, self.UGE): + sign_format = " :: unsigned" + else: + sign_format = "" + return env.format('%r = %r %s %r%s', self, self.lhs, + self.op_str[self.op], self.rhs, sign_format) def accept(self, visitor: 'OpVisitor[T]') -> T: return visitor.visit_binary_int_op(self) diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index c3dd6f3f8226..944ddc0a3c40 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -281,6 +281,10 @@ def __repr__(self) -> str: is_refcounted=True) # type: Final +def is_tagged(rtype: RType) -> bool: + return rtype is int_rprimitive or rtype is short_int_rprimitive + + def is_int_rprimitive(rtype: RType) -> bool: return rtype is int_rprimitive diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index 6e38a071ba38..a10dd507fdfc 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -173,5 +173,5 @@ def int_unary_op(name: str, c_function_name: str) -> CFunctionDescription: int_logical_op_mapping = { '==': IntLogicalOpDescrption(BinaryIntOp.EQ, int_equal_, False, False), '!=': IntLogicalOpDescrption(BinaryIntOp.NEQ, int_equal_, True, False), - '<': IntLogicalOpDescrption(BinaryIntOp.LT, int_less_than_, False, False) + '<': IntLogicalOpDescrption(BinaryIntOp.SLT, int_less_than_, False, False) } # type: Dict[str, IntLogicalOpDescrption] diff --git a/mypyc/test-data/analysis.test b/mypyc/test-data/analysis.test index 23755a94bc5d..2d81e5157425 100644 --- a/mypyc/test-data/analysis.test +++ b/mypyc/test-data/analysis.test @@ -392,7 +392,7 @@ L1: r9 = r4 & r8 if r9 goto L2 else goto L3 :: bool L2: - r10 = a < a + r10 = a < a :: signed r0 = r10 goto L4 L3: @@ -413,7 +413,7 @@ L6: r21 = r16 & r20 if r21 goto L7 else goto L8 :: bool L7: - r22 = a < a + r22 = a < a :: signed r12 = r22 goto L9 L8: diff --git a/mypyc/test-data/exceptions.test b/mypyc/test-data/exceptions.test index 6c0909683ddb..9e720e7abe4c 100644 --- a/mypyc/test-data/exceptions.test +++ b/mypyc/test-data/exceptions.test @@ -156,7 +156,7 @@ L1: r11 = r6 & r10 if r11 goto L2 else goto L3 :: bool L2: - r12 = i < l + r12 = i < l :: signed r2 = r12 goto L4 L3: diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 04257cb88b5a..b586371bb2d2 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -110,7 +110,7 @@ L0: r9 = r4 & r8 if r9 goto L1 else goto L2 :: bool L1: - r10 = x < y + r10 = x < y :: signed r0 = r10 goto L3 L2: @@ -152,7 +152,7 @@ L0: r9 = r4 & r8 if r9 goto L1 else goto L2 :: bool L1: - r10 = x < y + r10 = x < y :: signed r0 = r10 goto L3 L2: @@ -198,7 +198,7 @@ L0: r9 = r4 & r8 if r9 goto L1 else goto L2 :: bool L1: - r10 = x < y + r10 = x < y :: signed r0 = r10 goto L3 L2: @@ -269,7 +269,7 @@ L0: r9 = r4 & r8 if r9 goto L1 else goto L2 :: bool L1: - r10 = x < y + r10 = x < y :: signed r0 = r10 goto L3 L2: @@ -338,7 +338,7 @@ L0: r9 = r4 & r8 if r9 goto L1 else goto L2 :: bool L1: - r10 = x < y + r10 = x < y :: signed r0 = r10 goto L3 L2: @@ -378,7 +378,7 @@ L0: r9 = r4 & r8 if r9 goto L1 else goto L2 :: bool L1: - r10 = x < y + r10 = x < y :: signed r0 = r10 goto L3 L2: @@ -493,7 +493,7 @@ L0: r9 = r4 & r8 if r9 goto L1 else goto L2 :: bool L1: - r10 = x < y + r10 = x < y :: signed r0 = r10 goto L3 L2: @@ -2041,7 +2041,7 @@ L0: r9 = r8 L1: r10 = len r7 :: list - r11 = r9 < r10 + r11 = r9 < r10 :: signed if r11 goto L2 else goto L8 :: bool L2: r12 = r7[r9] :: unsafe list @@ -2105,7 +2105,7 @@ L0: r9 = r8 L1: r10 = len r7 :: list - r11 = r9 < r10 + r11 = r9 < r10 :: signed if r11 goto L2 else goto L8 :: bool L2: r12 = r7[r9] :: unsafe list @@ -2166,7 +2166,7 @@ L0: r1 = r0 L1: r2 = len l :: list - r3 = r1 < r2 + r3 = r1 < r2 :: signed if r3 goto L2 else goto L4 :: bool L2: r4 = l[r1] :: unsafe list @@ -2188,7 +2188,7 @@ L4: r13 = r12 L5: r14 = len l :: list - r15 = r13 < r14 + r15 = r13 < r14 :: signed if r15 goto L6 else goto L8 :: bool L6: r16 = l[r13] :: unsafe list @@ -2750,7 +2750,7 @@ L0: r12 = r7 & r11 if r12 goto L1 else goto L2 :: bool L1: - r13 = r0 < r1 + r13 = r0 < r1 :: signed r3 = r13 goto L3 L2: diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test index dbe0d22a7fb6..454bea233fc7 100644 --- a/mypyc/test-data/irbuild-lists.test +++ b/mypyc/test-data/irbuild-lists.test @@ -185,7 +185,7 @@ L0: r2 = r0 i = r2 L1: - r3 = r2 < r1 + r3 = r2 < r1 :: signed if r3 goto L2 else goto L4 :: bool L2: r4 = CPyList_GetItem(l, i) diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index 58b361a18aa3..988751e9cdb9 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -21,7 +21,7 @@ L0: r3 = r1 i = r3 L1: - r4 = r3 < r2 + r4 = r3 < r2 :: signed if r4 goto L2 else goto L4 :: bool L2: r5 = CPyTagged_Add(x, i) @@ -107,7 +107,7 @@ L0: r2 = r0 n = r2 L1: - r3 = r2 < r1 + r3 = r2 < r1 :: signed if r3 goto L2 else goto L4 :: bool L2: goto L4 @@ -197,7 +197,7 @@ L0: r2 = r0 n = r2 L1: - r3 = r2 < r1 + r3 = r2 < r1 :: signed if r3 goto L2 else goto L4 :: bool L2: L3: @@ -271,7 +271,7 @@ L0: r2 = r1 L1: r3 = len ls :: list - r4 = r2 < r3 + r4 = r2 < r3 :: signed if r4 goto L2 else goto L4 :: bool L2: r5 = ls[r2] :: unsafe list @@ -859,7 +859,7 @@ L0: r3 = r2 L1: r4 = len a :: list - r5 = r3 < r4 + r5 = r3 < r4 :: signed if r5 goto L2 else goto L4 :: bool L2: r6 = a[r3] :: unsafe list @@ -942,7 +942,7 @@ L0: r2 = iter b :: object L1: r3 = len a :: list - r4 = r1 < r3 + r4 = r1 < r3 :: signed if r4 goto L2 else goto L7 :: bool L2: r5 = next r2 :: object @@ -997,10 +997,10 @@ L1: if is_error(r6) goto L6 else goto L2 L2: r7 = len b :: list - r8 = r2 < r7 + r8 = r2 < r7 :: signed if r8 goto L3 else goto L6 :: bool L3: - r9 = r5 < r4 + r9 = r5 < r4 :: signed if r9 goto L4 else goto L6 :: bool L4: r10 = unbox(bool, r6) diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index e182275ae070..dd612c88a013 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -8,11 +8,12 @@ from mypyc.ir.ops import ( Environment, BasicBlock, Goto, Return, LoadInt, Assign, IncRef, DecRef, Branch, Call, Unbox, Box, TupleGet, GetAttr, PrimitiveOp, RegisterOp, - SetAttr, Op, Value, CallC + SetAttr, Op, Value, CallC, BinaryIntOp ) from mypyc.ir.rtypes import ( RTuple, RInstance, int_rprimitive, bool_rprimitive, list_rprimitive, - dict_rprimitive, object_rprimitive, c_int_rprimitive + dict_rprimitive, object_rprimitive, c_int_rprimitive, short_int_rprimitive, int32_rprimitive, + int64_rprimitive ) from mypyc.ir.func_ir import FuncIR, FuncDecl, RuntimeArg, FuncSignature from mypyc.ir.class_ir import ClassIR @@ -44,6 +45,12 @@ def setUp(self) -> None: self.o2 = self.env.add_local(Var('o2'), object_rprimitive) self.d = self.env.add_local(Var('d'), dict_rprimitive) self.b = self.env.add_local(Var('b'), bool_rprimitive) + self.s1 = self.env.add_local(Var('s1'), short_int_rprimitive) + self.s2 = self.env.add_local(Var('s2'), short_int_rprimitive) + self.i32 = self.env.add_local(Var('i32'), int32_rprimitive) + self.i32_1 = self.env.add_local(Var('i32_1'), int32_rprimitive) + self.i64 = self.env.add_local(Var('i64'), int64_rprimitive) + self.i64_1 = self.env.add_local(Var('i64_1'), int64_rprimitive) self.t = self.env.add_local(Var('t'), RTuple([int_rprimitive, bool_rprimitive])) self.tt = self.env.add_local( Var('tt'), @@ -245,6 +252,22 @@ def test_dict_contains(self) -> None: 'in', self.b, self.o, self.d, """cpy_r_r0 = PyDict_Contains(cpy_r_d, cpy_r_o);""") + def test_binary_int_op(self) -> None: + # signed + self.assert_emit(BinaryIntOp(bool_rprimitive, self.s1, self.s2, BinaryIntOp.SLT, 1), + """cpy_r_r0 = (Py_ssize_t)cpy_r_s1 < (Py_ssize_t)cpy_r_s2;""") + self.assert_emit(BinaryIntOp(bool_rprimitive, self.i32, self.i32_1, BinaryIntOp.SLT, 1), + """cpy_r_r00 = cpy_r_i32 < cpy_r_i32_1;""") + self.assert_emit(BinaryIntOp(bool_rprimitive, self.i64, self.i64_1, BinaryIntOp.SLT, 1), + """cpy_r_r01 = cpy_r_i64 < cpy_r_i64_1;""") + # unsigned + self.assert_emit(BinaryIntOp(bool_rprimitive, self.s1, self.s2, BinaryIntOp.ULT, 1), + """cpy_r_r02 = cpy_r_s1 < cpy_r_s2;""") + self.assert_emit(BinaryIntOp(bool_rprimitive, self.i32, self.i32_1, BinaryIntOp.ULT, 1), + """cpy_r_r03 = (uint32_t)cpy_r_i32 < (uint32_t)cpy_r_i32_1;""") + self.assert_emit(BinaryIntOp(bool_rprimitive, self.i64, self.i64_1, BinaryIntOp.ULT, 1), + """cpy_r_r04 = (uint64_t)cpy_r_i64 < (uint64_t)cpy_r_i64_1;""") + def assert_emit(self, op: Op, expected: str) -> None: self.emitter.fragments = [] self.declarations.fragments = [] From a5455bd9f37825d24415e46a4d278d71502c9d07 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Mon, 20 Jul 2020 22:37:26 +0800 Subject: [PATCH 073/138] [mypyc] Constant integer registers optimization (#9158) Recent changes to mypyc introduce low-level, inline integer ops, which makes both IR and generated C code verbose and potentially hurting performance. This PR introduces an on-demand optimization pass that find out all the registers with constant integer values. Related issues: mypyc/mypyc#749 mypyc/mypyc#746 --- mypyc/analysis/const_int.py | 16 ++++++++++++++++ mypyc/codegen/emitfunc.py | 32 +++++++++++++++++++++++--------- mypyc/test/test_emitfunc.py | 11 ++++++++--- 3 files changed, 47 insertions(+), 12 deletions(-) create mode 100644 mypyc/analysis/const_int.py diff --git a/mypyc/analysis/const_int.py b/mypyc/analysis/const_int.py new file mode 100644 index 000000000000..03faf842c29e --- /dev/null +++ b/mypyc/analysis/const_int.py @@ -0,0 +1,16 @@ +from typing import List, Dict + +from mypyc.ir.ops import BasicBlock, LoadInt + + +def find_constant_integer_registers(blocks: List[BasicBlock]) -> Dict[str, int]: + """Find all registers with constant integer values. + + Returns a mapping from register names to int values + """ + const_int_regs = {} # type: Dict[str, int] + for block in blocks: + for op in block.ops: + if isinstance(op, LoadInt): + const_int_regs[op.name] = op.value + return const_int_regs diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index 531d4b2c0b1e..76d5cbd09b5d 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -1,6 +1,6 @@ """Code generation for native function bodies.""" -from typing import Union +from typing import Union, Dict from typing_extensions import Final from mypyc.common import ( @@ -19,6 +19,7 @@ ) from mypyc.ir.func_ir import FuncIR, FuncDecl, FUNC_STATICMETHOD, FUNC_CLASSMETHOD from mypyc.ir.class_ir import ClassIR +from mypyc.analysis.const_int import find_constant_integer_registers # Whether to insert debug asserts for all error handling, to quickly # catch errors propagating without exceptions set. @@ -45,10 +46,15 @@ def native_function_header(fn: FuncDecl, emitter: Emitter) -> str: def generate_native_function(fn: FuncIR, emitter: Emitter, source_path: str, - module_name: str) -> None: + module_name: str, + optimize_int: bool = True) -> None: + if optimize_int: + const_int_regs = find_constant_integer_registers(fn.blocks) + else: + const_int_regs = {} declarations = Emitter(emitter.context, fn.env) body = Emitter(emitter.context, fn.env) - visitor = FunctionEmitterVisitor(body, declarations, source_path, module_name) + visitor = FunctionEmitterVisitor(body, declarations, source_path, module_name, const_int_regs) declarations.emit_line('{} {{'.format(native_function_header(fn.decl, emitter))) body.indent() @@ -62,10 +68,11 @@ def generate_native_function(fn: FuncIR, init = '' if r in fn.env.vars_needing_init: init = ' = {}'.format(declarations.c_error_value(r.type)) - declarations.emit_line('{ctype}{prefix}{name}{init};'.format(ctype=ctype, - prefix=REG_PREFIX, - name=r.name, - init=init)) + if r.name not in const_int_regs: + declarations.emit_line('{ctype}{prefix}{name}{init};'.format(ctype=ctype, + prefix=REG_PREFIX, + name=r.name, + init=init)) # Before we emit the blocks, give them all labels for i, block in enumerate(fn.blocks): @@ -87,13 +94,15 @@ def __init__(self, emitter: Emitter, declarations: Emitter, source_path: str, - module_name: str) -> None: + module_name: str, + const_int_regs: Dict[str, int]) -> None: self.emitter = emitter self.names = emitter.names self.declarations = declarations self.env = self.emitter.env self.source_path = source_path self.module_name = module_name + self.const_int_regs = const_int_regs def temp_name(self) -> str: return self.emitter.temp_name() @@ -176,6 +185,8 @@ def visit_assign(self, op: Assign) -> None: self.emit_line('%s = %s;' % (dest, src)) def visit_load_int(self, op: LoadInt) -> None: + if op.name in self.const_int_regs: + return dest = self.reg(op) self.emit_line('%s = %d;' % (dest, op.value)) @@ -459,7 +470,10 @@ def label(self, label: BasicBlock) -> str: return self.emitter.label(label) def reg(self, reg: Value) -> str: - return self.emitter.reg(reg) + if reg.name in self.const_int_regs: + return str(self.const_int_regs[reg.name]) + else: + return self.emitter.reg(reg) def ctype(self, rtype: RType) -> str: return self.emitter.ctype(rtype) diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index dd612c88a013..ab8bd816b923 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -1,5 +1,7 @@ import unittest +from typing import Dict + from mypy.ordered_dict import OrderedDict from mypy.nodes import Var @@ -64,7 +66,10 @@ def setUp(self) -> None: self.context = EmitterContext(NameGenerator([['mod']])) self.emitter = Emitter(self.context, self.env) self.declarations = Emitter(self.context, self.env) - self.visitor = FunctionEmitterVisitor(self.emitter, self.declarations, 'prog.py', 'prog') + + const_int_regs = {} # type: Dict[str, int] + self.visitor = FunctionEmitterVisitor(self.emitter, self.declarations, 'prog.py', 'prog', + const_int_regs) def test_goto(self) -> None: self.assert_emit(Goto(BasicBlock(2)), @@ -325,7 +330,7 @@ def test_simple(self) -> None: fn = FuncIR(FuncDecl('myfunc', None, 'mod', FuncSignature([self.arg], int_rprimitive)), [self.block], self.env) emitter = Emitter(EmitterContext(NameGenerator([['mod']]))) - generate_native_function(fn, emitter, 'prog.py', 'prog') + generate_native_function(fn, emitter, 'prog.py', 'prog', False) result = emitter.fragments assert_string_arrays_equal( [ @@ -344,7 +349,7 @@ def test_register(self) -> None: fn = FuncIR(FuncDecl('myfunc', None, 'mod', FuncSignature([self.arg], list_rprimitive)), [self.block], self.env) emitter = Emitter(EmitterContext(NameGenerator([['mod']]))) - generate_native_function(fn, emitter, 'prog.py', 'prog') + generate_native_function(fn, emitter, 'prog.py', 'prog', False) result = emitter.fragments assert_string_arrays_equal( [ From c2e20e91b7dd4998f074e777a5fc7e95188bfe99 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Wed, 22 Jul 2020 17:42:14 +0800 Subject: [PATCH 074/138] [mypyc] Optimize const int regs during pretty IR printing (#9181) Follow up of #9158, makes IR less verbose. --- mypyc/codegen/emitfunc.py | 2 +- mypyc/{analysis => ir}/const_int.py | 0 mypyc/ir/func_ir.py | 21 +- mypyc/ir/ops.py | 20 +- mypyc/test-data/analysis.test | 561 +++--- mypyc/test-data/exceptions.test | 141 +- mypyc/test-data/irbuild-basic.test | 1804 +++++++++----------- mypyc/test-data/irbuild-classes.test | 248 ++- mypyc/test-data/irbuild-dict.test | 334 ++-- mypyc/test-data/irbuild-generics.test | 72 +- mypyc/test-data/irbuild-int.test | 23 +- mypyc/test-data/irbuild-lists.test | 181 +- mypyc/test-data/irbuild-nested.test | 171 +- mypyc/test-data/irbuild-optional.test | 249 ++- mypyc/test-data/irbuild-set.test | 187 +- mypyc/test-data/irbuild-statements.test | 881 +++++----- mypyc/test-data/irbuild-strip-asserts.test | 13 +- mypyc/test-data/irbuild-tuple.test | 148 +- mypyc/test-data/refcount.test | 555 +++--- mypyc/test/test_emitfunc.py | 8 +- 20 files changed, 2441 insertions(+), 3178 deletions(-) rename mypyc/{analysis => ir}/const_int.py (100%) diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index 76d5cbd09b5d..52a1ac2f3f90 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -19,7 +19,7 @@ ) from mypyc.ir.func_ir import FuncIR, FuncDecl, FUNC_STATICMETHOD, FUNC_CLASSMETHOD from mypyc.ir.class_ir import ClassIR -from mypyc.analysis.const_int import find_constant_integer_registers +from mypyc.ir.const_int import find_constant_integer_registers # Whether to insert debug asserts for all error handling, to quickly # catch errors propagating without exceptions set. diff --git a/mypyc/analysis/const_int.py b/mypyc/ir/const_int.py similarity index 100% rename from mypyc/analysis/const_int.py rename to mypyc/ir/const_int.py diff --git a/mypyc/ir/func_ir.py b/mypyc/ir/func_ir.py index 4c6d51ea564b..70dd53b8ac34 100644 --- a/mypyc/ir/func_ir.py +++ b/mypyc/ir/func_ir.py @@ -1,4 +1,5 @@ """Intermediate representation of functions.""" +import re from typing import List, Optional, Sequence, Dict from typing_extensions import Final @@ -10,6 +11,7 @@ DeserMaps, Goto, Branch, Return, Unreachable, BasicBlock, Environment ) from mypyc.ir.rtypes import RType, deserialize_type +from mypyc.ir.const_int import find_constant_integer_registers from mypyc.namegen import NameGenerator @@ -218,7 +220,9 @@ def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> 'FuncIR': INVALID_FUNC_DEF = FuncDef('', [], Block([])) # type: Final -def format_blocks(blocks: List[BasicBlock], env: Environment) -> List[str]: +def format_blocks(blocks: List[BasicBlock], + env: Environment, + const_regs: Dict[str, int]) -> List[str]: """Format a list of IR basic blocks into a human-readable form.""" # First label all of the blocks for i, block in enumerate(blocks): @@ -244,9 +248,14 @@ def format_blocks(blocks: List[BasicBlock], env: Environment) -> List[str]: and ops[-1].label == blocks[i + 1]): # Hide the last goto if it just goes to the next basic block. ops = ops[:-1] + # load int registers start with 'i' + regex = re.compile(r'\bi[0-9]+\b') for op in ops: - line = ' ' + op.to_str(env) - lines.append(line) + if op.name not in const_regs: + line = ' ' + op.to_str(env) + line = regex.sub(lambda i: str(const_regs[i.group()]) if i.group() in const_regs + else i.group(), line) + lines.append(line) if not isinstance(block.ops[-1], (Goto, Branch, Return, Unreachable)): # Each basic block needs to exit somewhere. @@ -259,8 +268,10 @@ def format_func(fn: FuncIR) -> List[str]: cls_prefix = fn.class_name + '.' if fn.class_name else '' lines.append('def {}{}({}):'.format(cls_prefix, fn.name, ', '.join(arg.name for arg in fn.args))) - for line in fn.env.to_lines(): + # compute constants + const_regs = find_constant_integer_registers(fn.blocks) + for line in fn.env.to_lines(const_regs): lines.append(' ' + line) - code = format_blocks(fn.blocks, fn.env) + code = format_blocks(fn.blocks, fn.env, const_regs) lines.extend(code) return lines diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index f0f77d9b8a66..e4eeeba0a2bb 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -138,6 +138,7 @@ def __init__(self, name: Optional[str] = None) -> None: self.indexes = OrderedDict() # type: Dict[Value, int] self.symtable = OrderedDict() # type: OrderedDict[SymbolNode, AssignmentTarget] self.temp_index = 0 + self.temp_load_int_idx = 0 # All names genereted; value is the number of duplicates seen. self.names = {} # type: Dict[str, int] self.vars_needing_init = set() # type: Set[Value] @@ -198,6 +199,10 @@ def add_op(self, reg: 'RegisterOp') -> None: """Record the value of an operation.""" if reg.is_void: return + if isinstance(reg, LoadInt): + self.add(reg, "i%d" % self.temp_load_int_idx) + self.temp_load_int_idx += 1 + return self.add(reg, 'r%d' % self.temp_index) self.temp_index += 1 @@ -232,17 +237,24 @@ def format(self, fmt: str, *args: Any) -> str: i = n return ''.join(result) - def to_lines(self) -> List[str]: + def to_lines(self, const_regs: Optional[Dict[str, int]] = None) -> List[str]: result = [] i = 0 regs = list(self.regs()) - + if const_regs is None: + const_regs = {} while i < len(regs): i0 = i - group = [regs[i0].name] + if regs[i0].name not in const_regs: + group = [regs[i0].name] + else: + group = [] + i += 1 + continue while i + 1 < len(regs) and regs[i + 1].type == regs[i0].type: i += 1 - group.append(regs[i].name) + if regs[i].name not in const_regs: + group.append(regs[i].name) i += 1 result.append('%s :: %s' % (', '.join(group), regs[i0].type)) return result diff --git a/mypyc/test-data/analysis.test b/mypyc/test-data/analysis.test index 2d81e5157425..53dadee6dfd4 100644 --- a/mypyc/test-data/analysis.test +++ b/mypyc/test-data/analysis.test @@ -10,43 +10,35 @@ def f(a: int) -> None: [out] def f(a): a :: int - r0 :: short_int x :: int - r1 :: bool - r2, r3, r4 :: native_int - r5, r6, r7 :: bool - r8 :: short_int + r0 :: bool + r1 :: native_int + r2, r3, r4 :: bool y :: int - r9 :: short_int z :: int - r10 :: None + r5 :: None L0: - r0 = 2 - x = r0 - r2 = 1 - r3 = x & r2 - r4 = 0 - r5 = r3 == r4 - if r5 goto L1 else goto L2 :: bool + x = 2 + r1 = x & 1 + r2 = r1 == 0 + if r2 goto L1 else goto L2 :: bool L1: - r6 = x == a - r1 = r6 + r3 = x == a + r0 = r3 goto L3 L2: - r7 = CPyTagged_IsEq_(x, a) - r1 = r7 + r4 = CPyTagged_IsEq_(x, a) + r0 = r4 L3: - if r1 goto L4 else goto L5 :: bool + if r0 goto L4 else goto L5 :: bool L4: - r8 = 2 - y = r8 + y = 2 goto L6 L5: - r9 = 2 - z = r9 + z = 2 L6: - r10 = None - return r10 + r5 = None + return r5 (0, 0) {a} {a} (0, 1) {a} {a, x} (0, 2) {a, x} {a, x} @@ -55,20 +47,20 @@ L6: (0, 5) {a, x} {a, x} (0, 6) {a, x} {a, x} (1, 0) {a, x} {a, x} -(1, 1) {a, x} {a, r1, x} -(1, 2) {a, r1, x} {a, r1, x} +(1, 1) {a, x} {a, r0, x} +(1, 2) {a, r0, x} {a, r0, x} (2, 0) {a, x} {a, x} -(2, 1) {a, x} {a, r1, x} -(2, 2) {a, r1, x} {a, r1, x} -(3, 0) {a, r1, x} {a, r1, x} -(4, 0) {a, r1, x} {a, r1, x} -(4, 1) {a, r1, x} {a, r1, x, y} -(4, 2) {a, r1, x, y} {a, r1, x, y} -(5, 0) {a, r1, x} {a, r1, x} -(5, 1) {a, r1, x} {a, r1, x, z} -(5, 2) {a, r1, x, z} {a, r1, x, z} -(6, 0) {a, r1, x, y, z} {a, r1, x, y, z} -(6, 1) {a, r1, x, y, z} {a, r1, x, y, z} +(2, 1) {a, x} {a, r0, x} +(2, 2) {a, r0, x} {a, r0, x} +(3, 0) {a, r0, x} {a, r0, x} +(4, 0) {a, r0, x} {a, r0, x} +(4, 1) {a, r0, x} {a, r0, x, y} +(4, 2) {a, r0, x, y} {a, r0, x, y} +(5, 0) {a, r0, x} {a, r0, x} +(5, 1) {a, r0, x} {a, r0, x, z} +(5, 2) {a, r0, x, z} {a, r0, x, z} +(6, 0) {a, r0, x, y, z} {a, r0, x, y, z} +(6, 1) {a, r0, x, y, z} {a, r0, x, y, z} [case testSimple_Liveness] def f(a: int) -> int: @@ -80,27 +72,23 @@ def f(a: int) -> int: [out] def f(a): a :: int - r0 :: short_int x :: int - r1 :: short_int - r2 :: bool + r0 :: bool L0: - r0 = 2 - x = r0 - r1 = 2 - r2 = CPyTagged_IsEq(x, r1) - if r2 goto L1 else goto L2 :: bool + x = 2 + r0 = CPyTagged_IsEq(x, 2) + if r0 goto L1 else goto L2 :: bool L1: return a L2: return x L3: unreachable -(0, 0) {a} {a, r0} -(0, 1) {a, r0} {a, x} -(0, 2) {a, x} {a, r1, x} -(0, 3) {a, r1, x} {a, r2, x} -(0, 4) {a, r2, x} {a, x} +(0, 0) {a} {a, i0} +(0, 1) {a, i0} {a, x} +(0, 2) {a, x} {a, i1, x} +(0, 3) {a, i1, x} {a, r0, x} +(0, 4) {a, r0, x} {a, x} (1, 0) {a} {} (2, 0) {x} {} (3, 0) {} {} @@ -113,25 +101,19 @@ def f() -> int: return x [out] def f(): - r0 :: short_int x :: int - r1 :: short_int y :: int - r2 :: short_int L0: - r0 = 2 - x = r0 - r1 = 2 - y = r1 - r2 = 4 - x = r2 + x = 2 + y = 2 + x = 4 return x -(0, 0) {} {r0} -(0, 1) {r0} {} -(0, 2) {} {r1} -(0, 3) {r1} {} -(0, 4) {} {r2} -(0, 5) {r2} {x} +(0, 0) {} {i0} +(0, 1) {i0} {} +(0, 2) {} {i1} +(0, 3) {i1} {} +(0, 4) {} {i2} +(0, 5) {i2} {x} (0, 6) {x} {} [case testSpecial2_Liveness] @@ -143,21 +125,17 @@ def f(a: int) -> int: [out] def f(a): a :: int - r0, r1, r2 :: short_int L0: - r0 = 2 - a = r0 - r1 = 4 - a = r1 - r2 = 6 - a = r2 + a = 2 + a = 4 + a = 6 return a -(0, 0) {} {r0} -(0, 1) {r0} {} -(0, 2) {} {r1} -(0, 3) {r1} {} -(0, 4) {} {r2} -(0, 5) {r2} {a} +(0, 0) {} {i0} +(0, 1) {i0} {} +(0, 2) {} {i1} +(0, 3) {i1} {} +(0, 4) {} {i2} +(0, 5) {i2} {a} (0, 6) {a} {} [case testSimple_MustDefined] @@ -170,30 +148,22 @@ def f(a: int) -> None: [out] def f(a): a :: int - r0 :: short_int - r1 :: bool - r2 :: short_int + r0 :: bool y :: int - r3 :: short_int x :: int - r4 :: short_int - r5 :: None + r1 :: None L0: - r0 = 2 - r1 = CPyTagged_IsEq(a, r0) - if r1 goto L1 else goto L2 :: bool + r0 = CPyTagged_IsEq(a, 2) + if r0 goto L1 else goto L2 :: bool L1: - r2 = 2 - y = r2 - r3 = 4 - x = r3 + y = 2 + x = 4 goto L3 L2: - r4 = 4 - x = r4 + x = 4 L3: - r5 = None - return r5 + r1 = None + return r1 (0, 0) {a} {a} (0, 1) {a} {a} (0, 2) {a} {a} @@ -226,25 +196,21 @@ def f(n: int) -> None: [out] def f(n): n :: int - r0 :: short_int - r1 :: bool - r2 :: short_int - r3, m :: int - r4 :: None + r0 :: bool + r1, m :: int + r2 :: None L0: L1: - r0 = 10 - r1 = CPyTagged_IsLt(n, r0) - if r1 goto L2 else goto L3 :: bool + r0 = CPyTagged_IsLt(n, 10) + if r0 goto L2 else goto L3 :: bool L2: - r2 = 2 - r3 = CPyTagged_Add(n, r2) - n = r3 + r1 = CPyTagged_Add(n, 2) + n = r1 m = n goto L1 L3: - r4 = None - return r4 + r2 = None + return r2 (0, 0) {n} {n} (1, 0) {n} {n} (1, 1) {n} {n} @@ -269,61 +235,51 @@ def f(n: int) -> None: [out] def f(n): n :: int - r0 :: short_int x :: int - r1 :: short_int y :: int - r2 :: short_int - r3 :: bool - r4 :: short_int - r5 :: bool - r6 :: short_int - r7 :: None + r0 :: bool + r1 :: bool + r2 :: None L0: - r0 = 2 - x = r0 - r1 = 2 - y = r1 + x = 2 + y = 2 L1: - r2 = 2 - r3 = CPyTagged_IsLt(n, r2) - if r3 goto L2 else goto L6 :: bool + r0 = CPyTagged_IsLt(n, 2) + if r0 goto L2 else goto L6 :: bool L2: n = y L3: - r4 = 4 - r5 = CPyTagged_IsLt(n, r4) - if r5 goto L4 else goto L5 :: bool + r1 = CPyTagged_IsLt(n, 4) + if r1 goto L4 else goto L5 :: bool L4: - r6 = 2 - n = r6 + n = 2 n = x goto L3 L5: goto L1 L6: - r7 = None - return r7 -(0, 0) {n} {n, r0} -(0, 1) {n, r0} {n, x} -(0, 2) {n, x} {n, r1, x} -(0, 3) {n, r1, x} {n, x, y} + r2 = None + return r2 +(0, 0) {n} {i0, n} +(0, 1) {i0, n} {n, x} +(0, 2) {n, x} {i1, n, x} +(0, 3) {i1, n, x} {n, x, y} (0, 4) {n, x, y} {n, x, y} -(1, 0) {n, x, y} {n, r2, x, y} -(1, 1) {n, r2, x, y} {r3, x, y} -(1, 2) {r3, x, y} {x, y} +(1, 0) {n, x, y} {i2, n, x, y} +(1, 1) {i2, n, x, y} {r0, x, y} +(1, 2) {r0, x, y} {x, y} (2, 0) {x, y} {n, x, y} (2, 1) {n, x, y} {n, x, y} -(3, 0) {n, x, y} {n, r4, x, y} -(3, 1) {n, r4, x, y} {n, r5, x, y} -(3, 2) {n, r5, x, y} {n, x, y} -(4, 0) {x, y} {r6, x, y} -(4, 1) {r6, x, y} {x, y} +(3, 0) {n, x, y} {i3, n, x, y} +(3, 1) {i3, n, x, y} {n, r1, x, y} +(3, 2) {n, r1, x, y} {n, x, y} +(4, 0) {x, y} {i4, x, y} +(4, 1) {i4, x, y} {x, y} (4, 2) {x, y} {n, x, y} (4, 3) {n, x, y} {n, x, y} (5, 0) {n, x, y} {n, x, y} -(6, 0) {} {r7} -(6, 1) {r7} {} +(6, 0) {} {r2} +(6, 1) {r2} {} [case testCall_Liveness] def f(x: int) -> int: @@ -332,32 +288,30 @@ def f(x: int) -> int: [out] def f(x): x :: int - r0 :: short_int - r1, a, r2, r3, r4 :: int + r0, a, r1, r2, r3 :: int L0: - r0 = 2 - r1 = f(r0) - if is_error(r1) goto L3 (error at f:2) else goto L1 + r0 = f(2) + if is_error(r0) goto L3 (error at f:2) else goto L1 L1: - a = r1 - r2 = f(a) - if is_error(r2) goto L3 (error at f:3) else goto L2 + a = r0 + r1 = f(a) + if is_error(r1) goto L3 (error at f:3) else goto L2 L2: - r3 = CPyTagged_Add(r2, a) - return r3 + r2 = CPyTagged_Add(r1, a) + return r2 L3: - r4 = :: int - return r4 -(0, 0) {} {r0} -(0, 1) {r0} {r1} -(0, 2) {r1} {r1} -(1, 0) {r1} {a} -(1, 1) {a} {a, r2} -(1, 2) {a, r2} {a, r2} -(2, 0) {a, r2} {r3} -(2, 1) {r3} {} -(3, 0) {} {r4} -(3, 1) {r4} {} + r3 = :: int + return r3 +(0, 0) {} {i0} +(0, 1) {i0} {r0} +(0, 2) {r0} {r0} +(1, 0) {r0} {a} +(1, 1) {a} {a, r1} +(1, 2) {a, r1} {a, r1} +(2, 0) {a, r1} {r2} +(2, 1) {r2} {} +(3, 0) {} {r3} +(3, 1) {r3} {} [case testLoop_MaybeDefined] def f(a: int) -> None: @@ -369,58 +323,50 @@ def f(a: int) -> None: def f(a): a :: int r0 :: bool - r1, r2, r3 :: native_int - r4 :: bool - r5, r6, r7 :: native_int - r8, r9, r10, r11, r12 :: bool - r13, r14, r15 :: native_int - r16 :: bool - r17, r18, r19 :: native_int - r20, r21, r22, r23 :: bool + r1 :: native_int + r2 :: bool + r3 :: native_int + r4, r5, r6, r7, r8 :: bool + r9 :: native_int + r10 :: bool + r11 :: native_int + r12, r13, r14, r15 :: bool y, x :: int - r24 :: None + r16 :: None L0: L1: - r1 = 1 - r2 = a & r1 - r3 = 0 - r4 = r2 == r3 - r5 = 1 - r6 = a & r5 - r7 = 0 - r8 = r6 == r7 - r9 = r4 & r8 - if r9 goto L2 else goto L3 :: bool + r1 = a & 1 + r2 = r1 == 0 + r3 = a & 1 + r4 = r3 == 0 + r5 = r2 & r4 + if r5 goto L2 else goto L3 :: bool L2: - r10 = a < a :: signed - r0 = r10 + r6 = a < a :: signed + r0 = r6 goto L4 L3: - r11 = CPyTagged_IsLt_(a, a) - r0 = r11 + r7 = CPyTagged_IsLt_(a, a) + r0 = r7 L4: if r0 goto L5 else goto L12 :: bool L5: L6: - r13 = 1 - r14 = a & r13 - r15 = 0 - r16 = r14 == r15 - r17 = 1 - r18 = a & r17 - r19 = 0 - r20 = r18 == r19 - r21 = r16 & r20 - if r21 goto L7 else goto L8 :: bool + r9 = a & 1 + r10 = r9 == 0 + r11 = a & 1 + r12 = r11 == 0 + r13 = r10 & r12 + if r13 goto L7 else goto L8 :: bool L7: - r22 = a < a :: signed - r12 = r22 + r14 = a < a :: signed + r8 = r14 goto L9 L8: - r23 = CPyTagged_IsLt_(a, a) - r12 = r23 + r15 = CPyTagged_IsLt_(a, a) + r8 = r15 L9: - if r12 goto L10 else goto L11 :: bool + if r8 goto L10 else goto L11 :: bool L10: y = a goto L6 @@ -428,50 +374,50 @@ L11: x = a goto L1 L12: - r24 = None - return r24 + r16 = None + return r16 (0, 0) {a} {a} -(1, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} -(1, 1) {a, r0, r12, x, y} {a, r0, r12, x, y} -(1, 2) {a, r0, r12, x, y} {a, r0, r12, x, y} -(1, 3) {a, r0, r12, x, y} {a, r0, r12, x, y} -(1, 4) {a, r0, r12, x, y} {a, r0, r12, x, y} -(1, 5) {a, r0, r12, x, y} {a, r0, r12, x, y} -(1, 6) {a, r0, r12, x, y} {a, r0, r12, x, y} -(1, 7) {a, r0, r12, x, y} {a, r0, r12, x, y} -(1, 8) {a, r0, r12, x, y} {a, r0, r12, x, y} -(1, 9) {a, r0, r12, x, y} {a, r0, r12, x, y} -(2, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} -(2, 1) {a, r0, r12, x, y} {a, r0, r12, x, y} -(2, 2) {a, r0, r12, x, y} {a, r0, r12, x, y} -(3, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} -(3, 1) {a, r0, r12, x, y} {a, r0, r12, x, y} -(3, 2) {a, r0, r12, x, y} {a, r0, r12, x, y} -(4, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} -(5, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} -(6, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} -(6, 1) {a, r0, r12, x, y} {a, r0, r12, x, y} -(6, 2) {a, r0, r12, x, y} {a, r0, r12, x, y} -(6, 3) {a, r0, r12, x, y} {a, r0, r12, x, y} -(6, 4) {a, r0, r12, x, y} {a, r0, r12, x, y} -(6, 5) {a, r0, r12, x, y} {a, r0, r12, x, y} -(6, 6) {a, r0, r12, x, y} {a, r0, r12, x, y} -(6, 7) {a, r0, r12, x, y} {a, r0, r12, x, y} -(6, 8) {a, r0, r12, x, y} {a, r0, r12, x, y} -(6, 9) {a, r0, r12, x, y} {a, r0, r12, x, y} -(7, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} -(7, 1) {a, r0, r12, x, y} {a, r0, r12, x, y} -(7, 2) {a, r0, r12, x, y} {a, r0, r12, x, y} -(8, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} -(8, 1) {a, r0, r12, x, y} {a, r0, r12, x, y} -(8, 2) {a, r0, r12, x, y} {a, r0, r12, x, y} -(9, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} -(10, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} -(10, 1) {a, r0, r12, x, y} {a, r0, r12, x, y} -(11, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} -(11, 1) {a, r0, r12, x, y} {a, r0, r12, x, y} -(12, 0) {a, r0, r12, x, y} {a, r0, r12, x, y} -(12, 1) {a, r0, r12, x, y} {a, r0, r12, x, y} +(1, 0) {a, r0, r8, x, y} {a, r0, r8, x, y} +(1, 1) {a, r0, r8, x, y} {a, r0, r8, x, y} +(1, 2) {a, r0, r8, x, y} {a, r0, r8, x, y} +(1, 3) {a, r0, r8, x, y} {a, r0, r8, x, y} +(1, 4) {a, r0, r8, x, y} {a, r0, r8, x, y} +(1, 5) {a, r0, r8, x, y} {a, r0, r8, x, y} +(1, 6) {a, r0, r8, x, y} {a, r0, r8, x, y} +(1, 7) {a, r0, r8, x, y} {a, r0, r8, x, y} +(1, 8) {a, r0, r8, x, y} {a, r0, r8, x, y} +(1, 9) {a, r0, r8, x, y} {a, r0, r8, x, y} +(2, 0) {a, r0, r8, x, y} {a, r0, r8, x, y} +(2, 1) {a, r0, r8, x, y} {a, r0, r8, x, y} +(2, 2) {a, r0, r8, x, y} {a, r0, r8, x, y} +(3, 0) {a, r0, r8, x, y} {a, r0, r8, x, y} +(3, 1) {a, r0, r8, x, y} {a, r0, r8, x, y} +(3, 2) {a, r0, r8, x, y} {a, r0, r8, x, y} +(4, 0) {a, r0, r8, x, y} {a, r0, r8, x, y} +(5, 0) {a, r0, r8, x, y} {a, r0, r8, x, y} +(6, 0) {a, r0, r8, x, y} {a, r0, r8, x, y} +(6, 1) {a, r0, r8, x, y} {a, r0, r8, x, y} +(6, 2) {a, r0, r8, x, y} {a, r0, r8, x, y} +(6, 3) {a, r0, r8, x, y} {a, r0, r8, x, y} +(6, 4) {a, r0, r8, x, y} {a, r0, r8, x, y} +(6, 5) {a, r0, r8, x, y} {a, r0, r8, x, y} +(6, 6) {a, r0, r8, x, y} {a, r0, r8, x, y} +(6, 7) {a, r0, r8, x, y} {a, r0, r8, x, y} +(6, 8) {a, r0, r8, x, y} {a, r0, r8, x, y} +(6, 9) {a, r0, r8, x, y} {a, r0, r8, x, y} +(7, 0) {a, r0, r8, x, y} {a, r0, r8, x, y} +(7, 1) {a, r0, r8, x, y} {a, r0, r8, x, y} +(7, 2) {a, r0, r8, x, y} {a, r0, r8, x, y} +(8, 0) {a, r0, r8, x, y} {a, r0, r8, x, y} +(8, 1) {a, r0, r8, x, y} {a, r0, r8, x, y} +(8, 2) {a, r0, r8, x, y} {a, r0, r8, x, y} +(9, 0) {a, r0, r8, x, y} {a, r0, r8, x, y} +(10, 0) {a, r0, r8, x, y} {a, r0, r8, x, y} +(10, 1) {a, r0, r8, x, y} {a, r0, r8, x, y} +(11, 0) {a, r0, r8, x, y} {a, r0, r8, x, y} +(11, 1) {a, r0, r8, x, y} {a, r0, r8, x, y} +(12, 0) {a, r0, r8, x, y} {a, r0, r8, x, y} +(12, 1) {a, r0, r8, x, y} {a, r0, r8, x, y} [case testTrivial_BorrowedArgument] def f(a: int, b: int) -> int: @@ -491,11 +437,9 @@ def f(a: int) -> int: [out] def f(a): a, b :: int - r0 :: short_int L0: b = a - r0 = 2 - a = r0 + a = 2 return a (0, 0) {a} {a} (0, 1) {a} {a} @@ -514,35 +458,28 @@ def f(a: int) -> int: def f(a): a :: int r0 :: bool - r1, r2, r3 :: native_int - r4, r5, r6 :: bool - r7 :: short_int + r1 :: native_int + r2, r3, r4 :: bool x :: int - r8, r9 :: short_int L0: - r1 = 1 - r2 = a & r1 - r3 = 0 - r4 = r2 == r3 - if r4 goto L1 else goto L2 :: bool + r1 = a & 1 + r2 = r1 == 0 + if r2 goto L1 else goto L2 :: bool L1: - r5 = a == a - r0 = r5 + r3 = a == a + r0 = r3 goto L3 L2: - r6 = CPyTagged_IsEq_(a, a) - r0 = r6 + r4 = CPyTagged_IsEq_(a, a) + r0 = r4 L3: if r0 goto L4 else goto L5 :: bool L4: - r7 = 4 - x = r7 - r8 = 2 - a = r8 + x = 4 + a = 2 goto L6 L5: - r9 = 2 - x = r9 + x = 2 L6: return x (0, 0) {a} {a} @@ -578,28 +515,22 @@ def f(a: int) -> int: [out] def f(a): a :: int - r0 :: short_int sum :: int - r1 :: short_int i :: int - r2 :: bool - r3 :: int - r4 :: short_int - r5 :: int + r0 :: bool + r1 :: int + r2 :: int L0: - r0 = 0 - sum = r0 - r1 = 0 - i = r1 + sum = 0 + i = 0 L1: - r2 = CPyTagged_IsLe(i, a) - if r2 goto L2 else goto L3 :: bool + r0 = CPyTagged_IsLe(i, a) + if r0 goto L2 else goto L3 :: bool L2: - r3 = CPyTagged_Add(sum, i) - sum = r3 - r4 = 2 - r5 = CPyTagged_Add(i, r4) - i = r5 + r1 = CPyTagged_Add(sum, i) + sum = r1 + r2 = CPyTagged_Add(i, 2) + i = r2 goto L1 L3: return sum @@ -638,12 +569,9 @@ def lol(x): r3 :: str r4 :: object r5 :: bool - r6 :: short_int - r7 :: int - r8 :: bool - r9 :: short_int - r10, r11 :: int - r12 :: bool + r6 :: int + r7 :: bool + r8, r9 :: int L0: L1: r0 = CPyTagged_Id(x) @@ -659,14 +587,12 @@ L3: r5 = CPy_ExceptionMatches(r4) if r5 goto L4 else goto L5 :: bool L4: - r6 = 2 - r7 = CPyTagged_Negate(r6) + r6 = CPyTagged_Negate(2) CPy_RestoreExcInfo(r1) - return r7 + return r6 L5: CPy_Reraise() - r12 = 0 - if not r12 goto L8 else goto L6 :: bool + if not 0 goto L8 else goto L6 :: bool L6: unreachable L7: @@ -674,17 +600,16 @@ L7: goto L10 L8: CPy_RestoreExcInfo(r1) - r8 = keep_propagating - if not r8 goto L11 else goto L9 :: bool + r7 = keep_propagating + if not r7 goto L11 else goto L9 :: bool L9: unreachable L10: - r9 = 2 - r10 = CPyTagged_Add(st, r9) - return r10 + r8 = CPyTagged_Add(st, 2) + return r8 L11: - r11 = :: int - return r11 + r9 = :: int + return r9 (0, 0) {x} {x} (1, 0) {x} {r0} (1, 1) {r0} {st} @@ -696,23 +621,23 @@ L11: (2, 4) {r1, r4} {r1, r4} (3, 0) {r1, r4} {r1, r5} (3, 1) {r1, r5} {r1} -(4, 0) {r1} {r1, r6} -(4, 1) {r1, r6} {r1, r7} -(4, 2) {r1, r7} {r7} -(4, 3) {r7} {} +(4, 0) {r1} {i0, r1} +(4, 1) {i0, r1} {r1, r6} +(4, 2) {r1, r6} {r6} +(4, 3) {r6} {} (5, 0) {r1} {r1} -(5, 1) {r1} {r1, r12} -(5, 2) {r1, r12} {r1} +(5, 1) {r1} {i2, r1} +(5, 2) {i2, r1} {r1} (6, 0) {} {} (7, 0) {r1, st} {st} (7, 1) {st} {st} (8, 0) {r1} {} -(8, 1) {} {r8} -(8, 2) {r8} {} +(8, 1) {} {r7} +(8, 2) {r7} {} (9, 0) {} {} -(10, 0) {st} {r9, st} -(10, 1) {r9, st} {r10} -(10, 2) {r10} {} -(11, 0) {} {r11} -(11, 1) {r11} {} +(10, 0) {st} {i1, st} +(10, 1) {i1, st} {r8} +(10, 2) {r8} {} +(11, 0) {} {r9} +(11, 1) {r9} {} diff --git a/mypyc/test-data/exceptions.test b/mypyc/test-data/exceptions.test index 9e720e7abe4c..4b39cb2978b2 100644 --- a/mypyc/test-data/exceptions.test +++ b/mypyc/test-data/exceptions.test @@ -9,22 +9,20 @@ def f(x: List[int]) -> int: [out] def f(x): x :: list - r0 :: short_int - r1 :: object - r2, r3 :: int + r0 :: object + r1, r2 :: int L0: - r0 = 0 - r1 = CPyList_GetItemShort(x, r0) - if is_error(r1) goto L3 (error at f:3) else goto L1 + r0 = CPyList_GetItemShort(x, 0) + if is_error(r0) goto L3 (error at f:3) else goto L1 L1: - r2 = unbox(int, r1) - dec_ref r1 - if is_error(r2) goto L3 (error at f:3) else goto L2 + r1 = unbox(int, r0) + dec_ref r0 + if is_error(r1) goto L3 (error at f:3) else goto L2 L2: - return r2 + return r1 L3: - r3 = :: int - return r3 + r2 = :: int + return r2 [case testListAppendAndSetItemError] from typing import List @@ -77,41 +75,36 @@ def f(x): r0 :: None r1 :: object r2 :: bool - r3 :: short_int - r4 :: __main__.A - r5 :: None - r6 :: object - r7, r8 :: bool - r9, r10 :: short_int - r11 :: int + r3 :: __main__.A + r4 :: None + r5 :: object + r6, r7 :: bool + r8 :: int L0: r0 = None r1 = box(None, r0) r2 = x is r1 if r2 goto L1 else goto L2 :: bool L1: - r3 = 2 - return r3 + return 2 L2: inc_ref x - r4 = cast(__main__.A, x) - if is_error(r4) goto L6 (error at f:8) else goto L3 + r3 = cast(__main__.A, x) + if is_error(r3) goto L6 (error at f:8) else goto L3 L3: - r5 = None - r6 = box(None, r5) - r7 = r4 is r6 - dec_ref r4 - r8 = !r7 - if r8 goto L4 else goto L5 :: bool + r4 = None + r5 = box(None, r4) + r6 = r3 is r5 + dec_ref r3 + r7 = !r6 + if r7 goto L4 else goto L5 :: bool L4: - r9 = 4 - return r9 + return 4 L5: - r10 = 6 - return r10 + return 6 L6: - r11 = :: int - return r11 + r8 = :: int + return r8 [case testListSum] from typing import List @@ -126,66 +119,56 @@ def sum(a: List[int], l: int) -> int: def sum(a, l): a :: list l :: int - r0 :: short_int sum :: int - r1 :: short_int i :: int + r0 :: bool + r1 :: native_int r2 :: bool - r3, r4, r5 :: native_int - r6 :: bool - r7, r8, r9 :: native_int - r10, r11, r12, r13 :: bool - r14 :: object - r15, r16 :: int - r17 :: short_int - r18, r19 :: int + r3 :: native_int + r4, r5, r6, r7 :: bool + r8 :: object + r9, r10 :: int + r11, r12 :: int L0: - r0 = 0 - sum = r0 - r1 = 0 - i = r1 + sum = 0 + i = 0 L1: - r3 = 1 - r4 = i & r3 - r5 = 0 - r6 = r4 == r5 - r7 = 1 - r8 = l & r7 - r9 = 0 - r10 = r8 == r9 - r11 = r6 & r10 - if r11 goto L2 else goto L3 :: bool + r1 = i & 1 + r2 = r1 == 0 + r3 = l & 1 + r4 = r3 == 0 + r5 = r2 & r4 + if r5 goto L2 else goto L3 :: bool L2: - r12 = i < l :: signed - r2 = r12 + r6 = i < l :: signed + r0 = r6 goto L4 L3: - r13 = CPyTagged_IsLt_(i, l) - r2 = r13 + r7 = CPyTagged_IsLt_(i, l) + r0 = r7 L4: - if r2 goto L5 else goto L10 :: bool + if r0 goto L5 else goto L10 :: bool L5: - r14 = CPyList_GetItem(a, i) - if is_error(r14) goto L11 (error at sum:6) else goto L6 + r8 = CPyList_GetItem(a, i) + if is_error(r8) goto L11 (error at sum:6) else goto L6 L6: - r15 = unbox(int, r14) - dec_ref r14 - if is_error(r15) goto L11 (error at sum:6) else goto L7 + r9 = unbox(int, r8) + dec_ref r8 + if is_error(r9) goto L11 (error at sum:6) else goto L7 L7: - r16 = CPyTagged_Add(sum, r15) + r10 = CPyTagged_Add(sum, r9) dec_ref sum :: int - dec_ref r15 :: int - sum = r16 - r17 = 2 - r18 = CPyTagged_Add(i, r17) + dec_ref r9 :: int + sum = r10 + r11 = CPyTagged_Add(i, 2) dec_ref i :: int - i = r18 + i = r11 goto L1 L8: return sum L9: - r19 = :: int - return r19 + r12 = :: int + return r12 L10: dec_ref i :: int goto L8 @@ -281,7 +264,6 @@ def a(): r14, r15 :: object r16 :: bool r17 :: str - r18 :: bool L0: L1: r0 = builtins :: module @@ -319,8 +301,7 @@ L8: if is_error(r6) goto L11 else goto L9 L9: CPy_Reraise() - r18 = 0 - if not r18 goto L13 else goto L22 :: bool + if not 0 goto L13 else goto L22 :: bool L10: unreachable L11: diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index b586371bb2d2..65a4b66dcabb 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -3,11 +3,8 @@ def f() -> int: return 1 [out] def f(): - r0 :: short_int L0: - r0 = 2 - return r0 - + return 2 [case testFunctionArgument] def f(x: int) -> int: return x @@ -17,6 +14,7 @@ def f(x): L0: return x + [case testExplicitNoneReturn] def f() -> None: return @@ -44,11 +42,9 @@ def f() -> int: return y [out] def f(): - r0 :: short_int x, y :: int L0: - r0 = 2 - x = r0 + x = 2 y = x return y @@ -60,15 +56,13 @@ def f(x: int) -> None: [out] def f(x): x :: int - r0 :: short_int y :: int - r1 :: None + r0 :: None L0: - r0 = 2 - y = r0 + y = 2 y = x - r1 = None - return r1 + r0 = None + return r0 [case testIntArithmetic] def f(x: int, y: int) -> int: @@ -76,13 +70,11 @@ def f(x: int, y: int) -> int: [out] def f(x, y): x, y :: int - r0 :: short_int - r1, r2 :: int + r0, r1 :: int L0: - r0 = 2 - r1 = CPyTagged_Add(y, r0) - r2 = CPyTagged_Multiply(x, r1) - return r2 + r0 = CPyTagged_Add(y, 2) + r1 = CPyTagged_Multiply(x, r0) + return r1 [case testIf] def f(x: int, y: int) -> int: @@ -93,34 +85,28 @@ def f(x: int, y: int) -> int: def f(x, y): x, y :: int r0 :: bool - r1, r2, r3 :: native_int - r4 :: bool - r5, r6, r7 :: native_int - r8, r9, r10, r11 :: bool - r12 :: short_int -L0: - r1 = 1 - r2 = x & r1 - r3 = 0 - r4 = r2 == r3 - r5 = 1 - r6 = y & r5 - r7 = 0 - r8 = r6 == r7 - r9 = r4 & r8 - if r9 goto L1 else goto L2 :: bool + r1 :: native_int + r2 :: bool + r3 :: native_int + r4, r5, r6, r7 :: bool +L0: + r1 = x & 1 + r2 = r1 == 0 + r3 = y & 1 + r4 = r3 == 0 + r5 = r2 & r4 + if r5 goto L1 else goto L2 :: bool L1: - r10 = x < y :: signed - r0 = r10 + r6 = x < y :: signed + r0 = r6 goto L3 L2: - r11 = CPyTagged_IsLt_(x, y) - r0 = r11 + r7 = CPyTagged_IsLt_(x, y) + r0 = r7 L3: if r0 goto L4 else goto L5 :: bool L4: - r12 = 2 - x = r12 + x = 2 L5: return x @@ -135,38 +121,31 @@ def f(x: int, y: int) -> int: def f(x, y): x, y :: int r0 :: bool - r1, r2, r3 :: native_int - r4 :: bool - r5, r6, r7 :: native_int - r8, r9, r10, r11 :: bool - r12, r13 :: short_int -L0: - r1 = 1 - r2 = x & r1 - r3 = 0 - r4 = r2 == r3 - r5 = 1 - r6 = y & r5 - r7 = 0 - r8 = r6 == r7 - r9 = r4 & r8 - if r9 goto L1 else goto L2 :: bool + r1 :: native_int + r2 :: bool + r3 :: native_int + r4, r5, r6, r7 :: bool +L0: + r1 = x & 1 + r2 = r1 == 0 + r3 = y & 1 + r4 = r3 == 0 + r5 = r2 & r4 + if r5 goto L1 else goto L2 :: bool L1: - r10 = x < y :: signed - r0 = r10 + r6 = x < y :: signed + r0 = r6 goto L3 L2: - r11 = CPyTagged_IsLt_(x, y) - r0 = r11 + r7 = CPyTagged_IsLt_(x, y) + r0 = r7 L3: if r0 goto L4 else goto L5 :: bool L4: - r12 = 2 - x = r12 + x = 2 goto L6 L5: - r13 = 4 - x = r13 + x = 4 L6: return x @@ -181,41 +160,34 @@ def f(x: int, y: int) -> int: def f(x, y): x, y :: int r0 :: bool - r1, r2, r3 :: native_int - r4 :: bool - r5, r6, r7 :: native_int - r8, r9, r10, r11, r12 :: bool - r13, r14 :: short_int -L0: - r1 = 1 - r2 = x & r1 - r3 = 0 - r4 = r2 == r3 - r5 = 1 - r6 = y & r5 - r7 = 0 - r8 = r6 == r7 - r9 = r4 & r8 - if r9 goto L1 else goto L2 :: bool + r1 :: native_int + r2 :: bool + r3 :: native_int + r4, r5, r6, r7, r8 :: bool +L0: + r1 = x & 1 + r2 = r1 == 0 + r3 = y & 1 + r4 = r3 == 0 + r5 = r2 & r4 + if r5 goto L1 else goto L2 :: bool L1: - r10 = x < y :: signed - r0 = r10 + r6 = x < y :: signed + r0 = r6 goto L3 L2: - r11 = CPyTagged_IsLt_(x, y) - r0 = r11 + r7 = CPyTagged_IsLt_(x, y) + r0 = r7 L3: if r0 goto L4 else goto L6 :: bool L4: - r12 = CPyTagged_IsGt(x, y) - if r12 goto L5 else goto L6 :: bool + r8 = CPyTagged_IsGt(x, y) + if r8 goto L5 else goto L6 :: bool L5: - r13 = 2 - x = r13 + x = 2 goto L7 L6: - r14 = 4 - x = r14 + x = 4 L7: return x @@ -252,41 +224,34 @@ def f(x: int, y: int) -> int: def f(x, y): x, y :: int r0 :: bool - r1, r2, r3 :: native_int - r4 :: bool - r5, r6, r7 :: native_int - r8, r9, r10, r11, r12 :: bool - r13, r14 :: short_int -L0: - r1 = 1 - r2 = x & r1 - r3 = 0 - r4 = r2 == r3 - r5 = 1 - r6 = y & r5 - r7 = 0 - r8 = r6 == r7 - r9 = r4 & r8 - if r9 goto L1 else goto L2 :: bool + r1 :: native_int + r2 :: bool + r3 :: native_int + r4, r5, r6, r7, r8 :: bool +L0: + r1 = x & 1 + r2 = r1 == 0 + r3 = y & 1 + r4 = r3 == 0 + r5 = r2 & r4 + if r5 goto L1 else goto L2 :: bool L1: - r10 = x < y :: signed - r0 = r10 + r6 = x < y :: signed + r0 = r6 goto L3 L2: - r11 = CPyTagged_IsLt_(x, y) - r0 = r11 + r7 = CPyTagged_IsLt_(x, y) + r0 = r7 L3: if r0 goto L5 else goto L4 :: bool L4: - r12 = CPyTagged_IsGt(x, y) - if r12 goto L5 else goto L6 :: bool + r8 = CPyTagged_IsGt(x, y) + if r8 goto L5 else goto L6 :: bool L5: - r13 = 2 - x = r13 + x = 2 goto L7 L6: - r14 = 4 - x = r14 + x = 4 L7: return x @@ -321,34 +286,28 @@ def f(x: int, y: int) -> int: def f(x, y): x, y :: int r0 :: bool - r1, r2, r3 :: native_int - r4 :: bool - r5, r6, r7 :: native_int - r8, r9, r10, r11 :: bool - r12 :: short_int -L0: - r1 = 1 - r2 = x & r1 - r3 = 0 - r4 = r2 == r3 - r5 = 1 - r6 = y & r5 - r7 = 0 - r8 = r6 == r7 - r9 = r4 & r8 - if r9 goto L1 else goto L2 :: bool + r1 :: native_int + r2 :: bool + r3 :: native_int + r4, r5, r6, r7 :: bool +L0: + r1 = x & 1 + r2 = r1 == 0 + r3 = y & 1 + r4 = r3 == 0 + r5 = r2 & r4 + if r5 goto L1 else goto L2 :: bool L1: - r10 = x < y :: signed - r0 = r10 + r6 = x < y :: signed + r0 = r6 goto L3 L2: - r11 = CPyTagged_IsLt_(x, y) - r0 = r11 + r7 = CPyTagged_IsLt_(x, y) + r0 = r7 L3: if r0 goto L5 else goto L4 :: bool L4: - r12 = 2 - x = r12 + x = 2 L5: return x @@ -361,37 +320,31 @@ def f(x: int, y: int) -> int: def f(x, y): x, y :: int r0 :: bool - r1, r2, r3 :: native_int - r4 :: bool - r5, r6, r7 :: native_int - r8, r9, r10, r11, r12 :: bool - r13 :: short_int -L0: - r1 = 1 - r2 = x & r1 - r3 = 0 - r4 = r2 == r3 - r5 = 1 - r6 = y & r5 - r7 = 0 - r8 = r6 == r7 - r9 = r4 & r8 - if r9 goto L1 else goto L2 :: bool + r1 :: native_int + r2 :: bool + r3 :: native_int + r4, r5, r6, r7, r8 :: bool +L0: + r1 = x & 1 + r2 = r1 == 0 + r3 = y & 1 + r4 = r3 == 0 + r5 = r2 & r4 + if r5 goto L1 else goto L2 :: bool L1: - r10 = x < y :: signed - r0 = r10 + r6 = x < y :: signed + r0 = r6 goto L3 L2: - r11 = CPyTagged_IsLt_(x, y) - r0 = r11 + r7 = CPyTagged_IsLt_(x, y) + r0 = r7 L3: if r0 goto L4 else goto L5 :: bool L4: - r12 = CPyTagged_IsGt(x, y) - if r12 goto L6 else goto L5 :: bool + r8 = CPyTagged_IsGt(x, y) + if r8 goto L6 else goto L5 :: bool L5: - r13 = 2 - x = r13 + x = 2 L6: return x @@ -425,18 +378,16 @@ def f(x: int, y: int) -> int: [out] def f(x, y): x, y :: int - r0 :: short_int - r1 :: bool - r2 :: int + r0 :: bool + r1 :: int L0: - r0 = 2 - x = r0 + x = 2 L1: - r1 = CPyTagged_IsGt(x, y) - if r1 goto L2 else goto L3 :: bool + r0 = CPyTagged_IsGt(x, y) + if r0 goto L2 else goto L3 :: bool L2: - r2 = CPyTagged_Subtract(x, y) - x = r2 + r1 = CPyTagged_Subtract(x, y) + x = r1 goto L1 L3: return x @@ -456,14 +407,12 @@ def f() -> None: x = 1 [out] def f(): - r0 :: short_int x :: int - r1 :: None + r0 :: None L0: - r0 = 2 - x = r0 - r1 = None - return r1 + x = 2 + r0 = None + return r0 [case testImplicitNoneReturnAndIf] def f(x: int, y: int) -> None: @@ -475,42 +424,35 @@ def f(x: int, y: int) -> None: def f(x, y): x, y :: int r0 :: bool - r1, r2, r3 :: native_int - r4 :: bool - r5, r6, r7 :: native_int - r8, r9, r10, r11 :: bool - r12, r13 :: short_int - r14 :: None + r1 :: native_int + r2 :: bool + r3 :: native_int + r4, r5, r6, r7 :: bool + r8 :: None L0: - r1 = 1 - r2 = x & r1 - r3 = 0 - r4 = r2 == r3 - r5 = 1 - r6 = y & r5 - r7 = 0 - r8 = r6 == r7 - r9 = r4 & r8 - if r9 goto L1 else goto L2 :: bool + r1 = x & 1 + r2 = r1 == 0 + r3 = y & 1 + r4 = r3 == 0 + r5 = r2 & r4 + if r5 goto L1 else goto L2 :: bool L1: - r10 = x < y :: signed - r0 = r10 + r6 = x < y :: signed + r0 = r6 goto L3 L2: - r11 = CPyTagged_IsLt_(x, y) - r0 = r11 + r7 = CPyTagged_IsLt_(x, y) + r0 = r7 L3: if r0 goto L4 else goto L5 :: bool L4: - r12 = 2 - x = r12 + x = 2 goto L6 L5: - r13 = 4 - y = r13 + y = 4 L6: - r14 = None - return r14 + r8 = None + return r8 [case testRecursion] def f(n: int) -> int: @@ -521,28 +463,21 @@ def f(n: int) -> int: [out] def f(n): n :: int - r0 :: short_int - r1 :: bool - r2, r3 :: short_int - r4, r5 :: int - r6 :: short_int - r7, r8, r9 :: int + r0 :: bool + r1, r2 :: int + r3, r4, r5 :: int L0: - r0 = 2 - r1 = CPyTagged_IsLe(n, r0) - if r1 goto L1 else goto L2 :: bool + r0 = CPyTagged_IsLe(n, 2) + if r0 goto L1 else goto L2 :: bool L1: - r2 = 2 - return r2 + return 2 L2: - r3 = 2 - r4 = CPyTagged_Subtract(n, r3) - r5 = f(r4) - r6 = 4 - r7 = CPyTagged_Subtract(n, r6) - r8 = f(r7) - r9 = CPyTagged_Add(r5, r8) - return r9 + r1 = CPyTagged_Subtract(n, 2) + r2 = f(r1) + r3 = CPyTagged_Subtract(n, 4) + r4 = f(r3) + r5 = CPyTagged_Add(r2, r4) + return r5 L3: unreachable @@ -570,32 +505,23 @@ def f(n: int) -> int: [out] def f(n): n :: int - r0 :: short_int - r1 :: bool - r2 :: short_int + r0 :: bool x :: int - r3 :: short_int - r4 :: bool - r5, r6 :: short_int + r1 :: bool L0: - r0 = 0 - r1 = CPyTagged_IsLt(n, r0) - if r1 goto L1 else goto L2 :: bool + r0 = CPyTagged_IsLt(n, 0) + if r0 goto L1 else goto L2 :: bool L1: - r2 = 2 - x = r2 + x = 2 goto L6 L2: - r3 = 0 - r4 = CPyTagged_IsEq(n, r3) - if r4 goto L3 else goto L4 :: bool + r1 = CPyTagged_IsEq(n, 0) + if r1 goto L3 else goto L4 :: bool L3: - r5 = 2 - x = r5 + x = 2 goto L5 L4: - r6 = 4 - x = r6 + x = 4 L5: L6: return x @@ -606,12 +532,10 @@ def f(n: int) -> int: [out] def f(n): n :: int - r0 :: short_int - r1 :: int + r0 :: int L0: - r0 = 2 - r1 = CPyTagged_Negate(r0) - return r1 + r0 = CPyTagged_Negate(2) + return r0 [case testConditionalExpr] def f(n: int) -> int: @@ -619,23 +543,18 @@ def f(n: int) -> int: [out] def f(n): n :: int - r0 :: short_int - r1 :: bool - r2 :: int - r3, r4 :: short_int + r0 :: bool + r1 :: int L0: - r0 = 0 - r1 = CPyTagged_IsEq(n, r0) - if r1 goto L1 else goto L2 :: bool + r0 = CPyTagged_IsEq(n, 0) + if r0 goto L1 else goto L2 :: bool L1: - r3 = 0 - r2 = r3 + r1 = 0 goto L3 L2: - r4 = 2 - r2 = r4 + r1 = 2 L3: - return r2 + return r1 [case testOperatorAssignment] def f() -> int: @@ -644,16 +563,12 @@ def f() -> int: return x [out] def f(): - r0 :: short_int x :: int - r1 :: short_int - r2 :: int + r0 :: int L0: - r0 = 0 + x = 0 + r0 = CPyTagged_Add(x, 2) x = r0 - r1 = 2 - r2 = CPyTagged_Add(x, r1) - x = r2 return x [case testTrue] @@ -757,18 +672,16 @@ def f(x): r0 :: object r1 :: str r2 :: object - r3 :: short_int - r4, r5 :: object - r6 :: None + r3, r4 :: object + r5 :: None L0: r0 = builtins :: module r1 = unicode_1 :: static ('print') r2 = getattr r0, r1 - r3 = 10 - r4 = box(short_int, r3) - r5 = py_call(r2, r4) - r6 = None - return r6 + r3 = box(short_int, 10) + r4 = py_call(r2, r3) + r5 = None + return r5 [case testPrint] import builtins @@ -777,20 +690,18 @@ def f(x: int) -> None: [out] def f(x): x :: int - r0 :: short_int - r1 :: object - r2 :: str - r3, r4, r5 :: object - r6 :: None -L0: - r0 = 10 - r1 = builtins :: module - r2 = unicode_1 :: static ('print') - r3 = getattr r1, r2 - r4 = box(short_int, r0) - r5 = py_call(r3, r4) - r6 = None - return r6 + r0 :: object + r1 :: str + r2, r3, r4 :: object + r5 :: None +L0: + r0 = builtins :: module + r1 = unicode_1 :: static ('print') + r2 = getattr r0, r1 + r3 = box(short_int, 10) + r4 = py_call(r2, r3) + r5 = None + return r5 [case testUnicodeLiteral] def f() -> str: @@ -851,23 +762,21 @@ def g(y: object) -> None: def g(y): y :: object r0 :: None - r1 :: short_int - r2 :: object - r3 :: list - r4, r5 :: None - r6 :: object - r7, r8 :: None + r1 :: object + r2 :: list + r3, r4 :: None + r5 :: object + r6, r7 :: None L0: r0 = g(y) - r1 = 2 - r2 = box(short_int, r1) - r3 = [r2] - r4 = g(r3) - r5 = None - r6 = box(None, r5) - r7 = g(r6) - r8 = None - return r8 + r1 = box(short_int, 2) + r2 = [r1] + r3 = g(r2) + r4 = None + r5 = box(None, r4) + r6 = g(r5) + r7 = None + return r7 [case testCoerceToObject1] def g(y: object) -> object: @@ -879,35 +788,26 @@ def g(y: object) -> object: [out] def g(y): y :: object - r0 :: short_int - r1, r2 :: object - r3, a :: list - r4, r5 :: short_int - r6 :: tuple[int, int] - r7 :: short_int + r0, r1 :: object + r2, a :: list + r3 :: tuple[int, int] + r4 :: object + r5, r6 :: bool + r7 :: object r8 :: object - r9, r10 :: bool - r11 :: object - r12 :: short_int - r13 :: object L0: - r0 = 2 - r1 = box(short_int, r0) - r2 = g(r1) - r3 = [y] - a = r3 - r4 = 2 - r5 = 4 - r6 = (r4, r5) - r7 = 0 - r8 = box(tuple[int, int], r6) - r9 = CPyList_SetItem(a, r7, r8) - r10 = True - r11 = box(bool, r10) - y = r11 - r12 = 6 - r13 = box(short_int, r12) - return r13 + r0 = box(short_int, 2) + r1 = g(r0) + r2 = [y] + a = r2 + r3 = (2, 4) + r4 = box(tuple[int, int], r3) + r5 = CPyList_SetItem(a, 0, r4) + r6 = True + r7 = box(bool, r6) + y = r7 + r8 = box(short_int, 6) + return r8 [case testCoerceToObject2] class A: @@ -920,21 +820,19 @@ def f(a: A, o: object) -> None: def f(a, o): a :: __main__.A o :: object - r0 :: short_int - r1 :: object - r2 :: bool - r3 :: int - r4 :: object - r5 :: None + r0 :: object + r1 :: bool + r2 :: int + r3 :: object + r4 :: None L0: - r0 = 2 - r1 = box(short_int, r0) - a.x = r1; r2 = is_error - r3 = a.n - r4 = box(int, r3) - o = r4 - r5 = None - return r5 + r0 = box(short_int, 2) + a.x = r0; r1 = is_error + r2 = a.n + r3 = box(int, r2) + o = r3 + r4 = None + return r4 [case testDownCast] from typing import cast, List, Tuple @@ -1117,9 +1015,8 @@ def big_int() -> None: [out] def big_int(): r0, a_62_bit, r1, max_62_bit, r2, b_63_bit, r3, c_63_bit, r4, max_63_bit, r5, d_64_bit, r6, max_32_bit :: int - r7 :: short_int max_31_bit :: int - r8 :: None + r7 :: None L0: r0 = int_1 :: static (4611686018427387902) a_62_bit = r0 @@ -1135,10 +1032,9 @@ L0: d_64_bit = r5 r6 = int_7 :: static (2147483647) max_32_bit = r6 - r7 = 2147483646 - max_31_bit = r7 - r8 = None - return r8 + max_31_bit = 2147483646 + r7 = None + return r7 [case testCallableTypes] from typing import Callable @@ -1163,21 +1059,19 @@ def call_callable_type() -> float: [out] def absolute_value(x): x :: int - r0 :: short_int - r1 :: bool - r2, r3 :: int + r0 :: bool + r1, r2 :: int L0: - r0 = 0 - r1 = CPyTagged_IsGt(x, r0) - if r1 goto L1 else goto L2 :: bool + r0 = CPyTagged_IsGt(x, 0) + if r0 goto L1 else goto L2 :: bool L1: - r2 = x + r1 = x goto L3 L2: - r3 = CPyTagged_Negate(x) - r2 = r3 + r2 = CPyTagged_Negate(x) + r1 = r2 L3: - return r2 + return r1 def call_native_function(x): x, r0 :: int L0: @@ -1231,70 +1125,58 @@ def call_python_method_with_keyword_args(xs: List[int], first: int, second: int) [out] def call_python_function_with_keyword_arg(x): x :: str - r0 :: short_int - r1 :: object - r2 :: str - r3 :: tuple - r4 :: native_int + r0 :: object + r1 :: str + r2 :: tuple + r3 :: object + r4 :: dict r5 :: object - r6 :: dict - r7 :: object - r8 :: int -L0: - r0 = 4 - r1 = int - r2 = unicode_3 :: static ('base') - r3 = (x) :: tuple - r4 = 1 - r5 = box(short_int, r0) - r6 = CPyDict_Build(r4, r2, r5) - r7 = py_call_with_kwargs(r1, r3, r6) - r8 = unbox(int, r7) - return r8 + r6 :: int +L0: + r0 = int + r1 = unicode_3 :: static ('base') + r2 = (x) :: tuple + r3 = box(short_int, 4) + r4 = CPyDict_Build(1, r1, r3) + r5 = py_call_with_kwargs(r0, r2, r4) + r6 = unbox(int, r5) + return r6 def call_python_method_with_keyword_args(xs, first, second): xs :: list first, second :: int - r0 :: short_int - r1 :: str - r2 :: object - r3 :: str - r4 :: object - r5 :: tuple - r6 :: native_int + r0 :: str + r1 :: object + r2 :: str + r3 :: object + r4 :: tuple + r5 :: object + r6 :: dict r7 :: object - r8 :: dict + r8 :: str r9 :: object - r10 :: short_int - r11 :: str - r12 :: object - r13, r14 :: str - r15 :: tuple - r16 :: native_int - r17, r18 :: object - r19 :: dict - r20 :: object + r10, r11 :: str + r12 :: tuple + r13, r14 :: object + r15 :: dict + r16 :: object L0: - r0 = 0 - r1 = unicode_4 :: static ('insert') - r2 = getattr xs, r1 - r3 = unicode_5 :: static ('x') - r4 = box(short_int, r0) - r5 = (r4) :: tuple - r6 = 1 - r7 = box(int, first) - r8 = CPyDict_Build(r6, r3, r7) - r9 = py_call_with_kwargs(r2, r5, r8) - r10 = 2 - r11 = unicode_4 :: static ('insert') - r12 = getattr xs, r11 - r13 = unicode_5 :: static ('x') - r14 = unicode_6 :: static ('i') - r15 = () :: tuple - r16 = 2 - r17 = box(int, second) - r18 = box(short_int, r10) - r19 = CPyDict_Build(r16, r13, r17, r14, r18) - r20 = py_call_with_kwargs(r12, r15, r19) + r0 = unicode_4 :: static ('insert') + r1 = getattr xs, r0 + r2 = unicode_5 :: static ('x') + r3 = box(short_int, 0) + r4 = (r3) :: tuple + r5 = box(int, first) + r6 = CPyDict_Build(1, r2, r5) + r7 = py_call_with_kwargs(r1, r4, r6) + r8 = unicode_4 :: static ('insert') + r9 = getattr xs, r8 + r10 = unicode_5 :: static ('x') + r11 = unicode_6 :: static ('i') + r12 = () :: tuple + r13 = box(int, second) + r14 = box(short_int, 2) + r15 = CPyDict_Build(2, r10, r13, r11, r14) + r16 = py_call_with_kwargs(r9, r12, r15) return xs [case testObjectAsBoolean] @@ -1321,51 +1203,39 @@ def lst(x: List[int]) -> int: def obj(x): x :: object r0 :: bool - r1, r2 :: short_int L0: r0 = bool x :: object if r0 goto L1 else goto L2 :: bool L1: - r1 = 2 - return r1 + return 2 L2: - r2 = 0 - return r2 + return 0 L3: unreachable def num(x): x :: int - r0 :: short_int - r1 :: bool - r2, r3 :: short_int + r0 :: bool L0: - r0 = 0 - r1 = CPyTagged_IsNe(x, r0) - if r1 goto L1 else goto L2 :: bool + r0 = CPyTagged_IsNe(x, 0) + if r0 goto L1 else goto L2 :: bool L1: - r2 = 2 - return r2 + return 2 L2: - r3 = 0 - return r3 + return 0 L3: unreachable def lst(x): x :: list - r0, r1 :: short_int - r2 :: bool - r3, r4 :: short_int + r0 :: short_int + r1 :: bool L0: r0 = len x :: list - r1 = 0 - r2 = r0 != r1 - if r2 goto L1 else goto L2 :: bool + r1 = r0 != 0 + if r1 goto L1 else goto L2 :: bool L1: - r3 = 2 - return r3 + return 2 L2: - r4 = 0 - return r4 + return 0 L3: unreachable @@ -1397,41 +1267,33 @@ def opt_int(x): r0 :: object r1 :: bool r2 :: int - r3 :: short_int - r4 :: bool - r5, r6 :: short_int + r3 :: bool L0: r0 = builtins.None :: object r1 = x is not r0 if r1 goto L1 else goto L3 :: bool L1: r2 = unbox(int, x) - r3 = 0 - r4 = CPyTagged_IsNe(r2, r3) - if r4 goto L2 else goto L3 :: bool + r3 = CPyTagged_IsNe(r2, 0) + if r3 goto L2 else goto L3 :: bool L2: - r5 = 2 - return r5 + return 2 L3: - r6 = 0 - return r6 + return 0 L4: unreachable def opt_a(x): x :: union[__main__.A, None] r0 :: object r1 :: bool - r2, r3 :: short_int L0: r0 = builtins.None :: object r1 = x is not r0 if r1 goto L1 else goto L2 :: bool L1: - r2 = 2 - return r2 + return 2 L2: - r3 = 0 - return r3 + return 0 L3: unreachable def opt_o(x): @@ -1440,7 +1302,6 @@ def opt_o(x): r1 :: bool r2 :: object r3 :: bool - r4, r5 :: short_int L0: r0 = builtins.None :: object r1 = x is not r0 @@ -1450,11 +1311,9 @@ L1: r3 = bool r2 :: object if r3 goto L2 else goto L3 :: bool L2: - r4 = 2 - return r4 + return 2 L3: - r5 = 0 - return r5 + return 0 L4: unreachable @@ -1520,19 +1379,18 @@ def __top_level__(): r2 :: bool r3 :: str r4 :: object - r5 :: short_int - r6 :: dict - r7 :: str - r8 :: object - r9 :: int32 - r10 :: dict - r11 :: str - r12 :: object - r13 :: int - r14 :: object - r15 :: str - r16, r17, r18 :: object - r19 :: None + r5 :: dict + r6 :: str + r7 :: object + r8 :: int32 + r9 :: dict + r10 :: str + r11 :: object + r12 :: int + r13 :: object + r14 :: str + r15, r16, r17 :: object + r18 :: None L0: r0 = builtins :: module r1 = builtins.None :: object @@ -1543,22 +1401,21 @@ L1: r4 = import r3 :: str builtins = r4 :: module L2: - r5 = 2 - r6 = __main__.globals :: static - r7 = unicode_1 :: static ('x') - r8 = box(short_int, r5) - r9 = CPyDict_SetItem(r6, r7, r8) - r10 = __main__.globals :: static - r11 = unicode_1 :: static ('x') - r12 = CPyDict_GetItem(r10, r11) - r13 = unbox(int, r12) - r14 = builtins :: module - r15 = unicode_2 :: static ('print') - r16 = getattr r14, r15 - r17 = box(int, r13) - r18 = py_call(r16, r17) - r19 = None - return r19 + r5 = __main__.globals :: static + r6 = unicode_1 :: static ('x') + r7 = box(short_int, 2) + r8 = CPyDict_SetItem(r5, r6, r7) + r9 = __main__.globals :: static + r10 = unicode_1 :: static ('x') + r11 = CPyDict_GetItem(r9, r10) + r12 = unbox(int, r11) + r13 = builtins :: module + r14 = unicode_2 :: static ('print') + r15 = getattr r13, r14 + r16 = box(int, r12) + r17 = py_call(r15, r16) + r18 = None + return r18 [case testCallOverloaded] import m @@ -1575,18 +1432,16 @@ def f(): r0 :: object r1 :: str r2 :: object - r3 :: short_int - r4, r5 :: object - r6 :: str + r3, r4 :: object + r5 :: str L0: r0 = m :: module r1 = unicode_2 :: static ('f') r2 = getattr r0, r1 - r3 = 2 - r4 = box(short_int, r3) - r5 = py_call(r2, r4) - r6 = cast(str, r5) - return r6 + r3 = box(short_int, 2) + r4 = py_call(r2, r3) + r5 = cast(str, r4) + return r5 [case testCallOverloadedNative] from typing import overload, Union @@ -1608,19 +1463,17 @@ def foo(x): L0: return x def main(): - r0 :: short_int - r1 :: object - r2 :: union[int, str] - r3, x :: int - r4 :: None + r0 :: object + r1 :: union[int, str] + r2, x :: int + r3 :: None L0: - r0 = 0 - r1 = box(short_int, r0) - r2 = foo(r1) - r3 = unbox(int, r2) - x = r3 - r4 = None - return r4 + r0 = box(short_int, 0) + r1 = foo(r0) + r2 = unbox(int, r1) + x = r2 + r3 = None + return r3 [case testCallOverloadedNativeSubclass] from typing import overload, Union @@ -1663,19 +1516,17 @@ L2: r4 = A() return r4 def main(): - r0 :: short_int - r1 :: object - r2 :: __main__.A - r3, x :: __main__.B - r4 :: None + r0 :: object + r1 :: __main__.A + r2, x :: __main__.B + r3 :: None L0: - r0 = 0 - r1 = box(short_int, r0) - r2 = foo(r1) - r3 = cast(__main__.B, r2) - x = r3 - r4 = None - return r4 + r0 = box(short_int, 0) + r1 = foo(r0) + r2 = cast(__main__.B, r1) + x = r2 + r3 = None + return r3 [case testFunctionCallWithKeywordArgs] def f(x: int, y: str) -> None: pass @@ -1693,20 +1544,16 @@ L0: return r0 def g(): r0 :: str - r1 :: short_int - r2 :: None - r3 :: short_int - r4 :: str - r5, r6 :: None + r1 :: None + r2 :: str + r3, r4 :: None L0: r0 = unicode_1 :: static ('a') - r1 = 0 - r2 = f(r1, r0) - r3 = 2 - r4 = unicode_2 :: static ('b') - r5 = f(r3, r4) - r6 = None - return r6 + r1 = f(0, r0) + r2 = unicode_2 :: static ('b') + r3 = f(2, r2) + r4 = None + return r4 [case testMethodCallWithKeywordArgs] class A: @@ -1727,20 +1574,16 @@ L0: def g(a): a :: __main__.A r0 :: str - r1 :: short_int - r2 :: None - r3 :: short_int - r4 :: str - r5, r6 :: None + r1 :: None + r2 :: str + r3, r4 :: None L0: r0 = unicode_4 :: static ('a') - r1 = 0 - r2 = a.f(r1, r0) - r3 = 2 - r4 = unicode_5 :: static ('b') - r5 = a.f(r3, r4) - r6 = None - return r6 + r1 = a.f(0, r0) + r2 = unicode_5 :: static ('b') + r3 = a.f(2, r2) + r4 = None + return r4 [case testStarArgs] from typing import Tuple @@ -1758,62 +1601,54 @@ L0: r0 = (a, b, c) return r0 def g(): - r0, r1, r2 :: short_int - r3 :: tuple[int, int, int] - r4 :: dict - r5 :: str - r6 :: object - r7 :: list - r8, r9 :: object - r10 :: tuple - r11 :: dict - r12 :: object - r13 :: tuple[int, int, int] -L0: - r0 = 2 - r1 = 4 - r2 = 6 - r3 = (r0, r1, r2) - r4 = __main__.globals :: static - r5 = unicode_3 :: static ('f') - r6 = CPyDict_GetItem(r4, r5) - r7 = [] - r8 = box(tuple[int, int, int], r3) - r9 = CPyList_Extend(r7, r8) - r10 = PyList_AsTuple(r7) - r11 = PyDict_New() - r12 = py_call_with_kwargs(r6, r10, r11) - r13 = unbox(tuple[int, int, int], r12) - return r13 + r0 :: tuple[int, int, int] + r1 :: dict + r2 :: str + r3 :: object + r4 :: list + r5, r6 :: object + r7 :: tuple + r8 :: dict + r9 :: object + r10 :: tuple[int, int, int] +L0: + r0 = (2, 4, 6) + r1 = __main__.globals :: static + r2 = unicode_3 :: static ('f') + r3 = CPyDict_GetItem(r1, r2) + r4 = [] + r5 = box(tuple[int, int, int], r0) + r6 = CPyList_Extend(r4, r5) + r7 = PyList_AsTuple(r4) + r8 = PyDict_New() + r9 = py_call_with_kwargs(r3, r7, r8) + r10 = unbox(tuple[int, int, int], r9) + return r10 def h(): - r0, r1, r2 :: short_int - r3 :: tuple[int, int] - r4 :: dict - r5 :: str + r0 :: tuple[int, int] + r1 :: dict + r2 :: str + r3, r4 :: object + r5 :: list r6, r7 :: object - r8 :: list - r9, r10 :: object - r11 :: tuple - r12 :: dict - r13 :: object - r14 :: tuple[int, int, int] -L0: - r0 = 2 - r1 = 4 - r2 = 6 - r3 = (r1, r2) - r4 = __main__.globals :: static - r5 = unicode_3 :: static ('f') - r6 = CPyDict_GetItem(r4, r5) - r7 = box(short_int, r0) - r8 = [r7] - r9 = box(tuple[int, int], r3) - r10 = CPyList_Extend(r8, r9) - r11 = PyList_AsTuple(r8) - r12 = PyDict_New() - r13 = py_call_with_kwargs(r6, r11, r12) - r14 = unbox(tuple[int, int, int], r13) - return r14 + r8 :: tuple + r9 :: dict + r10 :: object + r11 :: tuple[int, int, int] +L0: + r0 = (4, 6) + r1 = __main__.globals :: static + r2 = unicode_3 :: static ('f') + r3 = CPyDict_GetItem(r1, r2) + r4 = box(short_int, 2) + r5 = [r4] + r6 = box(tuple[int, int], r0) + r7 = CPyList_Extend(r5, r6) + r8 = PyList_AsTuple(r5) + r9 = PyDict_New() + r10 = py_call_with_kwargs(r3, r8, r9) + r11 = unbox(tuple[int, int, int], r10) + return r11 [case testStar2Args] from typing import Tuple @@ -1832,78 +1667,62 @@ L0: return r0 def g(): r0 :: str - r1 :: short_int + r1 :: str r2 :: str - r3 :: short_int - r4 :: str - r5 :: short_int - r6 :: native_int - r7, r8, r9 :: object - r10, r11 :: dict - r12 :: str + r3, r4, r5 :: object + r6, r7 :: dict + r8 :: str + r9 :: object + r10 :: tuple + r11 :: dict + r12 :: int32 r13 :: object - r14 :: tuple - r15 :: dict - r16 :: int32 - r17 :: object - r18 :: tuple[int, int, int] + r14 :: tuple[int, int, int] L0: r0 = unicode_3 :: static ('a') - r1 = 2 - r2 = unicode_4 :: static ('b') - r3 = 4 - r4 = unicode_5 :: static ('c') - r5 = 6 - r6 = 3 - r7 = box(short_int, r1) - r8 = box(short_int, r3) - r9 = box(short_int, r5) - r10 = CPyDict_Build(r6, r0, r7, r2, r8, r4, r9) - r11 = __main__.globals :: static - r12 = unicode_6 :: static ('f') - r13 = CPyDict_GetItem(r11, r12) - r14 = () :: tuple - r15 = PyDict_New() - r16 = CPyDict_UpdateInDisplay(r15, r10) - r17 = py_call_with_kwargs(r13, r14, r15) - r18 = unbox(tuple[int, int, int], r17) - return r18 + r1 = unicode_4 :: static ('b') + r2 = unicode_5 :: static ('c') + r3 = box(short_int, 2) + r4 = box(short_int, 4) + r5 = box(short_int, 6) + r6 = CPyDict_Build(3, r0, r3, r1, r4, r2, r5) + r7 = __main__.globals :: static + r8 = unicode_6 :: static ('f') + r9 = CPyDict_GetItem(r7, r8) + r10 = () :: tuple + r11 = PyDict_New() + r12 = CPyDict_UpdateInDisplay(r11, r6) + r13 = py_call_with_kwargs(r9, r10, r11) + r14 = unbox(tuple[int, int, int], r13) + return r14 def h(): - r0 :: short_int + r0 :: str r1 :: str - r2 :: short_int - r3 :: str - r4 :: short_int - r5 :: native_int - r6, r7 :: object - r8, r9 :: dict - r10 :: str - r11, r12 :: object - r13 :: tuple - r14 :: dict - r15 :: int32 - r16 :: object - r17 :: tuple[int, int, int] + r2, r3 :: object + r4, r5 :: dict + r6 :: str + r7, r8 :: object + r9 :: tuple + r10 :: dict + r11 :: int32 + r12 :: object + r13 :: tuple[int, int, int] L0: - r0 = 2 - r1 = unicode_4 :: static ('b') - r2 = 4 - r3 = unicode_5 :: static ('c') - r4 = 6 - r5 = 2 - r6 = box(short_int, r2) - r7 = box(short_int, r4) - r8 = CPyDict_Build(r5, r1, r6, r3, r7) - r9 = __main__.globals :: static - r10 = unicode_6 :: static ('f') - r11 = CPyDict_GetItem(r9, r10) - r12 = box(short_int, r0) - r13 = (r12) :: tuple - r14 = PyDict_New() - r15 = CPyDict_UpdateInDisplay(r14, r8) - r16 = py_call_with_kwargs(r11, r13, r14) - r17 = unbox(tuple[int, int, int], r16) - return r17 + r0 = unicode_4 :: static ('b') + r1 = unicode_5 :: static ('c') + r2 = box(short_int, 4) + r3 = box(short_int, 6) + r4 = CPyDict_Build(2, r0, r2, r1, r3) + r5 = __main__.globals :: static + r6 = unicode_6 :: static ('f') + r7 = CPyDict_GetItem(r5, r6) + r8 = box(short_int, 2) + r9 = (r8) :: tuple + r10 = PyDict_New() + r11 = CPyDict_UpdateInDisplay(r10, r4) + r12 = py_call_with_kwargs(r7, r9, r10) + r13 = unbox(tuple[int, int, int], r12) + return r13 [case testFunctionCallWithDefaultArgs] def f(x: int, y: int = 3, z: str = "test") -> None: @@ -1916,41 +1735,34 @@ def g() -> None: def f(x, y, z): x, y :: int z :: str - r0 :: short_int - r1 :: str - r2 :: None + r0 :: str + r1 :: None L0: if is_error(y) goto L1 else goto L2 L1: - r0 = 6 - y = r0 + y = 6 L2: if is_error(z) goto L3 else goto L4 L3: - r1 = unicode_1 :: static ('test') - z = r1 + r0 = unicode_1 :: static ('test') + z = r0 L4: - r2 = None - return r2 + r1 = None + return r1 def g(): - r0 :: short_int - r1 :: int - r2 :: str - r3 :: None - r4, r5 :: short_int - r6 :: str - r7, r8 :: None + r0 :: int + r1 :: str + r2 :: None + r3 :: str + r4, r5 :: None L0: - r0 = 4 - r1 = :: int - r2 = :: str - r3 = f(r0, r1, r2) - r4 = 6 - r5 = 12 - r6 = :: str - r7 = f(r5, r4, r6) - r8 = None - return r8 + r0 = :: int + r1 = :: str + r2 = f(4, r0, r1) + r3 = :: str + r4 = f(12, 6, r3) + r5 = None + return r5 [case testMethodCallWithDefaultArgs] class A: @@ -1966,44 +1778,37 @@ def A.f(self, x, y, z): self :: __main__.A x, y :: int z :: str - r0 :: short_int - r1 :: str - r2 :: None + r0 :: str + r1 :: None L0: if is_error(y) goto L1 else goto L2 L1: - r0 = 6 - y = r0 + y = 6 L2: if is_error(z) goto L3 else goto L4 L3: - r1 = unicode_4 :: static ('test') - z = r1 + r0 = unicode_4 :: static ('test') + z = r0 L4: - r2 = None - return r2 + r1 = None + return r1 def g(): r0, a :: __main__.A - r1 :: short_int - r2 :: int - r3 :: str - r4 :: None - r5, r6 :: short_int - r7 :: str - r8, r9 :: None + r1 :: int + r2 :: str + r3 :: None + r4 :: str + r5, r6 :: None L0: r0 = A() a = r0 - r1 = 4 - r2 = :: int - r3 = :: str - r4 = a.f(r1, r2, r3) - r5 = 6 - r6 = 12 - r7 = :: str - r8 = a.f(r6, r5, r7) - r9 = None - return r9 + r1 = :: int + r2 = :: str + r3 = a.f(4, r1, r2) + r4 = :: str + r5 = a.f(12, 6, r4) + r6 = None + return r6 [case testListComprehension] from typing import List @@ -2013,59 +1818,49 @@ def f() -> List[int]: [out] def f(): r0 :: list - r1, r2, r3 :: short_int - r4, r5, r6 :: object - r7 :: list - r8, r9, r10 :: short_int + r1, r2, r3 :: object + r4 :: list + r5, r6 :: short_int + r7 :: bool + r8 :: object + x, r9 :: int + r10 :: bool r11 :: bool - r12 :: object - x, r13 :: int - r14 :: short_int - r15 :: bool - r16 :: short_int - r17 :: bool - r18 :: int - r19 :: object - r20 :: int32 - r21, r22 :: short_int + r12 :: int + r13 :: object + r14 :: int32 + r15 :: short_int L0: r0 = [] - r1 = 2 - r2 = 4 - r3 = 6 - r4 = box(short_int, r1) - r5 = box(short_int, r2) - r6 = box(short_int, r3) - r7 = [r4, r5, r6] - r8 = 0 - r9 = r8 + r1 = box(short_int, 2) + r2 = box(short_int, 4) + r3 = box(short_int, 6) + r4 = [r1, r2, r3] + r5 = 0 L1: - r10 = len r7 :: list - r11 = r9 < r10 :: signed - if r11 goto L2 else goto L8 :: bool + r6 = len r4 :: list + r7 = r5 < r6 :: signed + if r7 goto L2 else goto L8 :: bool L2: - r12 = r7[r9] :: unsafe list - r13 = unbox(int, r12) - x = r13 - r14 = 4 - r15 = CPyTagged_IsNe(x, r14) - if r15 goto L4 else goto L3 :: bool + r8 = r4[r5] :: unsafe list + r9 = unbox(int, r8) + x = r9 + r10 = CPyTagged_IsNe(x, 4) + if r10 goto L4 else goto L3 :: bool L3: goto L7 L4: - r16 = 6 - r17 = CPyTagged_IsNe(x, r16) - if r17 goto L6 else goto L5 :: bool + r11 = CPyTagged_IsNe(x, 6) + if r11 goto L6 else goto L5 :: bool L5: goto L7 L6: - r18 = CPyTagged_Multiply(x, x) - r19 = box(int, r18) - r20 = PyList_Append(r0, r19) + r12 = CPyTagged_Multiply(x, x) + r13 = box(int, r12) + r14 = PyList_Append(r0, r13) L7: - r21 = 2 - r22 = r9 + r21 - r9 = r22 + r15 = r5 + 2 + r5 = r15 goto L1 L8: return r0 @@ -2077,60 +1872,50 @@ def f() -> Dict[int, int]: [out] def f(): r0 :: dict - r1, r2, r3 :: short_int - r4, r5, r6 :: object - r7 :: list - r8, r9, r10 :: short_int + r1, r2, r3 :: object + r4 :: list + r5, r6 :: short_int + r7 :: bool + r8 :: object + x, r9 :: int + r10 :: bool r11 :: bool - r12 :: object - x, r13 :: int - r14 :: short_int - r15 :: bool + r12 :: int + r13, r14 :: object + r15 :: int32 r16 :: short_int - r17 :: bool - r18 :: int - r19, r20 :: object - r21 :: int32 - r22, r23 :: short_int L0: r0 = PyDict_New() - r1 = 2 - r2 = 4 - r3 = 6 - r4 = box(short_int, r1) - r5 = box(short_int, r2) - r6 = box(short_int, r3) - r7 = [r4, r5, r6] - r8 = 0 - r9 = r8 + r1 = box(short_int, 2) + r2 = box(short_int, 4) + r3 = box(short_int, 6) + r4 = [r1, r2, r3] + r5 = 0 L1: - r10 = len r7 :: list - r11 = r9 < r10 :: signed - if r11 goto L2 else goto L8 :: bool + r6 = len r4 :: list + r7 = r5 < r6 :: signed + if r7 goto L2 else goto L8 :: bool L2: - r12 = r7[r9] :: unsafe list - r13 = unbox(int, r12) - x = r13 - r14 = 4 - r15 = CPyTagged_IsNe(x, r14) - if r15 goto L4 else goto L3 :: bool + r8 = r4[r5] :: unsafe list + r9 = unbox(int, r8) + x = r9 + r10 = CPyTagged_IsNe(x, 4) + if r10 goto L4 else goto L3 :: bool L3: goto L7 L4: - r16 = 6 - r17 = CPyTagged_IsNe(x, r16) - if r17 goto L6 else goto L5 :: bool + r11 = CPyTagged_IsNe(x, 6) + if r11 goto L6 else goto L5 :: bool L5: goto L7 L6: - r18 = CPyTagged_Multiply(x, x) - r19 = box(int, x) - r20 = box(int, r18) - r21 = CPyDict_SetItem(r0, r19, r20) + r12 = CPyTagged_Multiply(x, x) + r13 = box(int, x) + r14 = box(int, r12) + r15 = CPyDict_SetItem(r0, r13, r14) L7: - r22 = 2 - r23 = r9 + r22 - r9 = r23 + r16 = r5 + 2 + r5 = r16 goto L1 L8: return r0 @@ -2144,72 +1929,68 @@ def f(l: List[Tuple[int, int, int]]) -> List[int]: [out] def f(l): l :: list - r0, r1, r2 :: short_int - r3 :: bool - r4 :: object + r0, r1 :: short_int + r2 :: bool + r3 :: object x, y, z :: int - r5 :: tuple[int, int, int] - r6, r7, r8 :: int - r9, r10 :: short_int - r11 :: list - r12, r13, r14 :: short_int - r15 :: bool - r16 :: object + r4 :: tuple[int, int, int] + r5, r6, r7 :: int + r8 :: short_int + r9 :: list + r10, r11 :: short_int + r12 :: bool + r13 :: object x0, y0, z0 :: int - r17 :: tuple[int, int, int] - r18, r19, r20, r21, r22 :: int - r23 :: object - r24 :: int32 - r25, r26 :: short_int + r14 :: tuple[int, int, int] + r15, r16, r17, r18, r19 :: int + r20 :: object + r21 :: int32 + r22 :: short_int L0: r0 = 0 - r1 = r0 L1: - r2 = len l :: list - r3 = r1 < r2 :: signed - if r3 goto L2 else goto L4 :: bool + r1 = len l :: list + r2 = r0 < r1 :: signed + if r2 goto L2 else goto L4 :: bool L2: - r4 = l[r1] :: unsafe list - r5 = unbox(tuple[int, int, int], r4) - r6 = r5[0] - x = r6 - r7 = r5[1] - y = r7 - r8 = r5[2] - z = r8 + r3 = l[r0] :: unsafe list + r4 = unbox(tuple[int, int, int], r3) + r5 = r4[0] + x = r5 + r6 = r4[1] + y = r6 + r7 = r4[2] + z = r7 L3: - r9 = 2 - r10 = r1 + r9 - r1 = r10 + r8 = r0 + 2 + r0 = r8 goto L1 L4: - r11 = [] - r12 = 0 - r13 = r12 + r9 = [] + r10 = 0 L5: - r14 = len l :: list - r15 = r13 < r14 :: signed - if r15 goto L6 else goto L8 :: bool + r11 = len l :: list + r12 = r10 < r11 :: signed + if r12 goto L6 else goto L8 :: bool L6: - r16 = l[r13] :: unsafe list - r17 = unbox(tuple[int, int, int], r16) - r18 = r17[0] - x0 = r18 - r19 = r17[1] - y0 = r19 - r20 = r17[2] - z0 = r20 - r21 = CPyTagged_Add(x0, y0) - r22 = CPyTagged_Add(r21, z0) - r23 = box(int, r22) - r24 = PyList_Append(r11, r23) + r13 = l[r10] :: unsafe list + r14 = unbox(tuple[int, int, int], r13) + r15 = r14[0] + x0 = r15 + r16 = r14[1] + y0 = r16 + r17 = r14[2] + z0 = r17 + r18 = CPyTagged_Add(x0, y0) + r19 = CPyTagged_Add(r18, z0) + r20 = box(int, r19) + r21 = PyList_Append(r9, r20) L7: - r25 = 2 - r26 = r13 + r25 - r13 = r26 + r22 = r10 + 2 + r10 = r22 goto L5 L8: - return r11 + return r9 [case testProperty] class PropertyHolder: @@ -2256,13 +2037,11 @@ L0: return r3 def PropertyHolder.twice_value(self): self :: __main__.PropertyHolder - r0 :: short_int - r1, r2 :: int + r0, r1 :: int L0: - r0 = 4 - r1 = self.value - r2 = CPyTagged_Multiply(r0, r1) - return r2 + r0 = self.value + r1 = CPyTagged_Multiply(4, r0) + return r1 [case testPropertyDerivedGen] from typing import Callable @@ -2328,15 +2107,13 @@ L0: def BaseProperty.next(self): self :: __main__.BaseProperty r0 :: int - r1 :: short_int - r2 :: int - r3 :: __main__.BaseProperty + r1 :: int + r2 :: __main__.BaseProperty L0: r0 = self._incrementer - r1 = 2 - r2 = CPyTagged_Add(r0, r1) - r3 = BaseProperty(r2) - return r3 + r1 = CPyTagged_Add(r0, 2) + r2 = BaseProperty(r1) + return r2 def BaseProperty.__init__(self, value): self :: __main__.BaseProperty value :: int @@ -2483,12 +2260,10 @@ L0: return self def SubclassedTrait.boxed(self): self :: __main__.SubclassedTrait - r0 :: short_int - r1 :: object + r0 :: object L0: - r0 = 6 - r1 = box(short_int, r0) - return r1 + r0 = box(short_int, 6) + return r0 def DerivingObject.this(self): self :: __main__.DerivingObject L0: @@ -2500,10 +2275,8 @@ L0: return r0 def DerivingObject.boxed(self): self :: __main__.DerivingObject - r0 :: short_int L0: - r0 = 10 - return r0 + return 10 def DerivingObject.boxed__SubclassedTrait_glue(__mypyc_self__): __mypyc_self__ :: __main__.DerivingObject r0 :: int @@ -2584,41 +2357,39 @@ def __top_level__(): r39 :: dict r40 :: str r41 :: int32 - r42 :: short_int - r43 :: str - r44 :: dict - r45 :: str - r46, r47, r48 :: object - r49 :: tuple - r50 :: dict - r51 :: str - r52 :: int32 - r53 :: dict - r54 :: str - r55, r56, r57 :: object - r58 :: dict - r59 :: str - r60 :: int32 - r61 :: str - r62 :: dict - r63 :: str - r64 :: object - r65 :: dict - r66 :: str - r67, r68 :: object - r69 :: dict - r70 :: str - r71 :: int32 - r72, r73, r74 :: short_int - r75, r76, r77 :: object - r78 :: list + r42 :: str + r43 :: dict + r44 :: str + r45, r46, r47 :: object + r48 :: tuple + r49 :: dict + r50 :: str + r51 :: int32 + r52 :: dict + r53 :: str + r54, r55, r56 :: object + r57 :: dict + r58 :: str + r59 :: int32 + r60 :: str + r61 :: dict + r62 :: str + r63 :: object + r64 :: dict + r65 :: str + r66, r67 :: object + r68 :: dict + r69 :: str + r70 :: int32 + r71, r72, r73 :: object + r74 :: list + r75 :: dict + r76 :: str + r77, r78 :: object r79 :: dict r80 :: str - r81, r82 :: object - r83 :: dict - r84 :: str - r85 :: int32 - r86 :: None + r81 :: int32 + r82 :: None L0: r0 = builtins :: module r1 = builtins.None :: object @@ -2670,52 +2441,48 @@ L4: r39 = __main__.globals :: static r40 = unicode_5 :: static ('Lol') r41 = CPyDict_SetItem(r39, r40, r38) - r42 = 2 - r43 = unicode_8 :: static - r44 = __main__.globals :: static - r45 = unicode_5 :: static ('Lol') - r46 = CPyDict_GetItem(r44, r45) - r47 = box(short_int, r42) - r48 = py_call(r46, r47, r43) - r49 = cast(tuple, r48) - r50 = __main__.globals :: static - r51 = unicode_9 :: static ('x') - r52 = CPyDict_SetItem(r50, r51, r49) - r53 = __main__.globals :: static - r54 = unicode_2 :: static ('List') - r55 = CPyDict_GetItem(r53, r54) - r56 = int - r57 = r55[r56] :: object - r58 = __main__.globals :: static - r59 = unicode_10 :: static ('Foo') - r60 = CPyDict_SetItem(r58, r59, r57) - r61 = unicode_11 :: static ('Bar') - r62 = __main__.globals :: static - r63 = unicode_10 :: static ('Foo') - r64 = CPyDict_GetItem(r62, r63) - r65 = __main__.globals :: static - r66 = unicode_3 :: static ('NewType') - r67 = CPyDict_GetItem(r65, r66) - r68 = py_call(r67, r61, r64) - r69 = __main__.globals :: static - r70 = unicode_11 :: static ('Bar') - r71 = CPyDict_SetItem(r69, r70, r68) - r72 = 2 - r73 = 4 - r74 = 6 - r75 = box(short_int, r72) - r76 = box(short_int, r73) - r77 = box(short_int, r74) - r78 = [r75, r76, r77] + r42 = unicode_8 :: static + r43 = __main__.globals :: static + r44 = unicode_5 :: static ('Lol') + r45 = CPyDict_GetItem(r43, r44) + r46 = box(short_int, 2) + r47 = py_call(r45, r46, r42) + r48 = cast(tuple, r47) + r49 = __main__.globals :: static + r50 = unicode_9 :: static ('x') + r51 = CPyDict_SetItem(r49, r50, r48) + r52 = __main__.globals :: static + r53 = unicode_2 :: static ('List') + r54 = CPyDict_GetItem(r52, r53) + r55 = int + r56 = r54[r55] :: object + r57 = __main__.globals :: static + r58 = unicode_10 :: static ('Foo') + r59 = CPyDict_SetItem(r57, r58, r56) + r60 = unicode_11 :: static ('Bar') + r61 = __main__.globals :: static + r62 = unicode_10 :: static ('Foo') + r63 = CPyDict_GetItem(r61, r62) + r64 = __main__.globals :: static + r65 = unicode_3 :: static ('NewType') + r66 = CPyDict_GetItem(r64, r65) + r67 = py_call(r66, r60, r63) + r68 = __main__.globals :: static + r69 = unicode_11 :: static ('Bar') + r70 = CPyDict_SetItem(r68, r69, r67) + r71 = box(short_int, 2) + r72 = box(short_int, 4) + r73 = box(short_int, 6) + r74 = [r71, r72, r73] + r75 = __main__.globals :: static + r76 = unicode_11 :: static ('Bar') + r77 = CPyDict_GetItem(r75, r76) + r78 = py_call(r77, r74) r79 = __main__.globals :: static - r80 = unicode_11 :: static ('Bar') - r81 = CPyDict_GetItem(r79, r80) - r82 = py_call(r81, r78) - r83 = __main__.globals :: static - r84 = unicode_12 :: static ('y') - r85 = CPyDict_SetItem(r83, r84, r82) - r86 = None - return r86 + r80 = unicode_12 :: static ('y') + r81 = CPyDict_SetItem(r79, r80, r78) + r82 = None + return r82 [case testChainedConditional] def g(x: int) -> int: @@ -2730,41 +2497,37 @@ L0: def f(x, y, z): x, y, z, r0, r1 :: int r2, r3 :: bool - r4, r5, r6 :: native_int - r7 :: bool - r8, r9, r10 :: native_int - r11, r12, r13, r14 :: bool - r15 :: int - r16 :: bool + r4 :: native_int + r5 :: bool + r6 :: native_int + r7, r8, r9, r10 :: bool + r11 :: int + r12 :: bool L0: r0 = g(x) r1 = g(y) - r4 = 1 - r5 = r0 & r4 - r6 = 0 - r7 = r5 == r6 - r8 = 1 - r9 = r1 & r8 - r10 = 0 - r11 = r9 == r10 - r12 = r7 & r11 - if r12 goto L1 else goto L2 :: bool + r4 = r0 & 1 + r5 = r4 == 0 + r6 = r1 & 1 + r7 = r6 == 0 + r8 = r5 & r7 + if r8 goto L1 else goto L2 :: bool L1: - r13 = r0 < r1 :: signed - r3 = r13 + r9 = r0 < r1 :: signed + r3 = r9 goto L3 L2: - r14 = CPyTagged_IsLt_(r0, r1) - r3 = r14 + r10 = CPyTagged_IsLt_(r0, r1) + r3 = r10 L3: if r3 goto L5 else goto L4 :: bool L4: r2 = r3 goto L6 L5: - r15 = g(z) - r16 = CPyTagged_IsGt(r1, r15) - r2 = r16 + r11 = g(z) + r12 = CPyTagged_IsGt(r1, r11) + r2 = r12 L6: return r2 @@ -3208,8 +2971,7 @@ def call_any(l): r0, r1 :: bool r2, r3 :: object r4, i :: int - r5 :: short_int - r6, r7, r8 :: bool + r5, r6, r7 :: bool L0: r1 = False r0 = r1 @@ -3220,18 +2982,17 @@ L1: L2: r4 = unbox(int, r3) i = r4 - r5 = 0 - r6 = CPyTagged_IsEq(i, r5) - if r6 goto L3 else goto L4 :: bool + r5 = CPyTagged_IsEq(i, 0) + if r5 goto L3 else goto L4 :: bool L3: - r7 = True - r0 = r7 + r6 = True + r0 = r6 goto L8 L4: L5: goto L1 L6: - r8 = CPy_NoErrOccured() + r7 = CPy_NoErrOccured() L7: L8: return r0 @@ -3240,8 +3001,7 @@ def call_all(l): r0, r1 :: bool r2, r3 :: object r4, i :: int - r5 :: short_int - r6, r7, r8, r9 :: bool + r5, r6, r7, r8 :: bool L0: r1 = True r0 = r1 @@ -3252,19 +3012,18 @@ L1: L2: r4 = unbox(int, r3) i = r4 - r5 = 0 - r6 = CPyTagged_IsEq(i, r5) - r7 = !r6 - if r7 goto L3 else goto L4 :: bool + r5 = CPyTagged_IsEq(i, 0) + r6 = !r5 + if r6 goto L3 else goto L4 :: bool L3: - r8 = False - r0 = r8 + r7 = False + r0 = r7 goto L8 L4: L5: goto L1 L6: - r9 = CPy_NoErrOccured() + r8 = CPy_NoErrOccured() L7: L8: return r0 @@ -3304,15 +3063,12 @@ def f(a: bool) -> int: [out] def f(a): a :: bool - r0, r1 :: short_int L0: if a goto L1 else goto L2 :: bool L1: - r0 = 2 - return r0 + return 2 L2: - r1 = 4 - return r1 + return 4 L3: unreachable @@ -3382,28 +3138,21 @@ def f(a: bool) -> int: [out] def C.__mypyc_defaults_setup(__mypyc_self__): __mypyc_self__ :: __main__.C - r0 :: short_int - r1 :: bool - r2 :: short_int - r3, r4 :: bool + r0 :: bool + r1, r2 :: bool L0: - r0 = 2 - __mypyc_self__.x = r0; r1 = is_error - r2 = 4 - __mypyc_self__.y = r2; r3 = is_error - r4 = True - return r4 + __mypyc_self__.x = 2; r0 = is_error + __mypyc_self__.y = 4; r1 = is_error + r2 = True + return r2 def f(a): a :: bool - r0, r1 :: short_int L0: if a goto L1 else goto L2 :: bool L1: - r0 = 2 - return r0 + return 2 L2: - r1 = 4 - return r1 + return 4 L3: unreachable @@ -3418,9 +3167,8 @@ def f() -> int: def f(): r0 :: list r1 :: bool - r2 :: short_int - r3 :: object - r4 :: int + r2 :: object + r3 :: int L0: r0 = __main__.x :: static if is_error(r0) goto L1 else goto L2 @@ -3428,10 +3176,9 @@ L1: raise NameError('value for final name "x" was not set') unreachable L2: - r2 = 0 - r3 = CPyList_GetItemShort(r0, r2) - r4 = unbox(int, r3) - return r4 + r2 = CPyList_GetItemShort(r0, 0) + r3 = unbox(int, r2) + return r3 [case testFinalStaticTuple] from typing import Final @@ -3466,8 +3213,7 @@ def f() -> int: def f(): r0 :: int r1 :: bool - r2 :: short_int - r3 :: int + r2 :: int L0: r0 = __main__.x :: static if is_error(r0) goto L1 else goto L2 @@ -3475,9 +3221,8 @@ L1: raise NameError('value for final name "x" was not set') unreachable L2: - r2 = 2 - r3 = CPyTagged_Subtract(r0, r2) - return r3 + r2 = CPyTagged_Subtract(r0, 2) + return r2 [case testFinalRestrictedTypeVar] from typing import TypeVar @@ -3492,12 +3237,10 @@ def foo(z: Targ) -> None: [out] def foo(z): z :: object - r0 :: short_int - r1 :: None + r0 :: None L0: - r0 = 20 - r1 = None - return r1 + r0 = None + return r0 [case testDirectlyCall__bool__] class A: @@ -3529,16 +3272,13 @@ L0: def lol(x): x :: __main__.A r0 :: bool - r1, r2 :: short_int L0: r0 = x.__bool__() if r0 goto L1 else goto L2 :: bool L1: - r1 = 2 - return r1 + return 2 L2: - r2 = 0 - return r2 + return 0 L3: unreachable diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index 42d0e361fb9c..50a079955c7a 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -21,14 +21,12 @@ def f(a: A) -> None: [out] def f(a): a :: __main__.A - r0 :: short_int - r1 :: bool - r2 :: None + r0 :: bool + r1 :: None L0: - r0 = 2 - a.x = r0; r1 = is_error - r2 = None - return r2 + a.x = 2; r0 = is_error + r1 = None + return r1 [case testUserClassInList] class C: @@ -43,30 +41,24 @@ def f() -> int: [out] def f(): r0, c :: __main__.C - r1 :: short_int - r2 :: bool - r3, a :: list - r4 :: short_int - r5 :: object - r6, d :: __main__.C - r7 :: int - r8 :: short_int - r9 :: int + r1 :: bool + r2, a :: list + r3 :: object + r4, d :: __main__.C + r5 :: int + r6 :: int L0: r0 = C() c = r0 - r1 = 10 - c.x = r1; r2 = is_error - r3 = [c] - a = r3 - r4 = 0 - r5 = CPyList_GetItemShort(a, r4) - r6 = cast(__main__.C, r5) - d = r6 - r7 = d.x - r8 = 2 - r9 = CPyTagged_Add(r7, r8) - return r9 + c.x = 10; r1 = is_error + r2 = [c] + a = r2 + r3 = CPyList_GetItemShort(a, 0) + r4 = cast(__main__.C, r3) + d = r4 + r5 = d.x + r6 = CPyTagged_Add(r5, 2) + return r6 [case testMethodCall] class A: @@ -80,24 +72,20 @@ def A.f(self, x, y): self :: __main__.A x :: int y :: str - r0 :: short_int - r1 :: int + r0 :: int L0: - r0 = 20 - r1 = CPyTagged_Add(x, r0) - return r1 + r0 = CPyTagged_Add(x, 20) + return r0 def g(a): a :: __main__.A - r0 :: short_int - r1 :: str - r2 :: int - r3 :: None + r0 :: str + r1 :: int + r2 :: None L0: - r0 = 2 - r1 = unicode_4 :: static ('hi') - r2 = a.f(r0, r1) - r3 = None - return r3 + r0 = unicode_4 :: static ('hi') + r1 = a.f(2, r0) + r2 = None + return r2 [case testForwardUse] def g(a: A) -> int: @@ -129,11 +117,9 @@ def Node.length(self): r1 :: None r2 :: object r3, r4 :: bool - r5 :: short_int - r6 :: union[__main__.Node, None] - r7 :: __main__.Node - r8, r9 :: int - r10 :: short_int + r5 :: union[__main__.Node, None] + r6 :: __main__.Node + r7, r8 :: int L0: r0 = self.next r1 = None @@ -142,15 +128,13 @@ L0: r4 = !r3 if r4 goto L1 else goto L2 :: bool L1: - r5 = 2 - r6 = self.next - r7 = cast(__main__.Node, r6) - r8 = r7.length() - r9 = CPyTagged_Add(r5, r8) - return r9 + r5 = self.next + r6 = cast(__main__.Node, r5) + r7 = r6.length() + r8 = CPyTagged_Add(2, r7) + return r8 L2: - r10 = 2 - return r10 + return 2 [case testSubclass] class A: @@ -163,28 +147,22 @@ class B(A): [out] def A.__init__(self): self :: __main__.A - r0 :: short_int - r1 :: bool - r2 :: None + r0 :: bool + r1 :: None L0: - r0 = 20 - self.x = r0; r1 = is_error - r2 = None - return r2 + self.x = 20; r0 = is_error + r1 = None + return r1 def B.__init__(self): self :: __main__.B - r0 :: short_int + r0 :: bool r1 :: bool - r2 :: short_int - r3 :: bool - r4 :: None + r2 :: None L0: - r0 = 40 - self.x = r0; r1 = is_error - r2 = 60 - self.y = r2; r3 = is_error - r4 = None - return r4 + self.x = 40; r0 = is_error + self.y = 60; r1 = is_error + r2 = None + return r2 [case testAttrLvalue] class O(object): @@ -197,25 +175,21 @@ def increment(o: O) -> O: [out] def O.__init__(self): self :: __main__.O - r0 :: short_int - r1 :: bool - r2 :: None + r0 :: bool + r1 :: None L0: - r0 = 2 - self.x = r0; r1 = is_error - r2 = None - return r2 + self.x = 2; r0 = is_error + r1 = None + return r1 def increment(o): o :: __main__.O r0 :: int - r1 :: short_int - r2 :: int - r3 :: bool + r1 :: int + r2 :: bool L0: r0 = o.x - r1 = 2 - r2 = CPyTagged_Add(r0, r1) - o.x = r2; r3 = is_error + r1 = CPyTagged_Add(r0, 2) + o.x = r1; r2 = is_error return o [case testSubclassSpecialize2] @@ -701,35 +675,27 @@ def lol() -> int: [out] def C.foo(x): x :: int - r0 :: short_int - r1 :: int + r0 :: int L0: - r0 = 20 - r1 = CPyTagged_Add(r0, x) - return r1 + r0 = CPyTagged_Add(20, x) + return r0 def C.bar(cls, x): cls :: object x :: int - r0 :: short_int - r1 :: int + r0 :: int L0: - r0 = 20 - r1 = CPyTagged_Add(r0, x) - return r1 + r0 = CPyTagged_Add(20, x) + return r0 def lol(): - r0 :: short_int - r1 :: int - r2 :: object - r3 :: short_int - r4, r5 :: int + r0 :: int + r1 :: object + r2, r3 :: int L0: - r0 = 2 - r1 = C.foo(r0) - r2 = __main__.C :: type - r3 = 4 - r4 = C.bar(r2, r3) - r5 = CPyTagged_Add(r1, r4) - return r5 + r0 = C.foo(2) + r1 = __main__.C :: type + r2 = C.bar(r1, 4) + r3 = CPyTagged_Add(r0, r2) + return r3 [case testSuper1] class A: @@ -1013,50 +979,44 @@ class B(A): [out] def A.lol(self): self :: __main__.A - r0 :: short_int - r1 :: bool - r2 :: None + r0 :: bool + r1 :: None L0: - r0 = 200 - self.x = r0; r1 = is_error - r2 = None - return r2 + self.x = 200; r0 = is_error + r1 = None + return r1 def A.__mypyc_defaults_setup(__mypyc_self__): __mypyc_self__ :: __main__.A - r0 :: short_int - r1, r2 :: bool + r0, r1 :: bool L0: - r0 = 20 - __mypyc_self__.x = r0; r1 = is_error - r2 = True - return r2 + __mypyc_self__.x = 20; r0 = is_error + r1 = True + return r1 def B.__mypyc_defaults_setup(__mypyc_self__): __mypyc_self__ :: __main__.B - r0 :: short_int - r1 :: bool - r2 :: dict - r3 :: str - r4 :: object - r5 :: str - r6 :: bool - r7 :: None - r8 :: object - r9, r10, r11, r12 :: bool + r0 :: bool + r1 :: dict + r2 :: str + r3 :: object + r4 :: str + r5 :: bool + r6 :: None + r7 :: object + r8, r9, r10, r11 :: bool L0: - r0 = 20 - __mypyc_self__.x = r0; r1 = is_error - r2 = __main__.globals :: static - r3 = unicode_9 :: static ('LOL') - r4 = CPyDict_GetItem(r2, r3) - r5 = cast(str, r4) - __mypyc_self__.y = r5; r6 = is_error - r7 = None - r8 = box(None, r7) - __mypyc_self__.z = r8; r9 = is_error - r10 = True - __mypyc_self__.b = r10; r11 = is_error - r12 = True - return r12 + __mypyc_self__.x = 20; r0 = is_error + r1 = __main__.globals :: static + r2 = unicode_9 :: static ('LOL') + r3 = CPyDict_GetItem(r1, r2) + r4 = cast(str, r3) + __mypyc_self__.y = r4; r5 = is_error + r6 = None + r7 = box(None, r6) + __mypyc_self__.z = r7; r8 = is_error + r9 = True + __mypyc_self__.b = r9; r10 = is_error + r11 = True + return r11 [case testSubclassDictSpecalized] from typing import Dict diff --git a/mypyc/test-data/irbuild-dict.test b/mypyc/test-data/irbuild-dict.test index 1b62a8188a03..85f23058126d 100644 --- a/mypyc/test-data/irbuild-dict.test +++ b/mypyc/test-data/irbuild-dict.test @@ -5,15 +5,13 @@ def f(d: Dict[int, bool]) -> bool: [out] def f(d): d :: dict - r0 :: short_int - r1, r2 :: object - r3 :: bool + r0, r1 :: object + r2 :: bool L0: - r0 = 0 - r1 = box(short_int, r0) - r2 = CPyDict_GetItem(d, r1) - r3 = unbox(bool, r2) - return r3 + r0 = box(short_int, 0) + r1 = CPyDict_GetItem(d, r0) + r2 = unbox(bool, r1) + return r2 [case testDictSet] from typing import Dict @@ -23,18 +21,16 @@ def f(d: Dict[int, bool]) -> None: def f(d): d :: dict r0 :: bool - r1 :: short_int - r2, r3 :: object - r4 :: int32 - r5 :: None + r1, r2 :: object + r3 :: int32 + r4 :: None L0: r0 = False - r1 = 0 - r2 = box(short_int, r1) - r3 = box(bool, r0) - r4 = CPyDict_SetItem(d, r2, r3) - r5 = None - return r5 + r1 = box(short_int, 0) + r2 = box(bool, r0) + r3 = CPyDict_SetItem(d, r1, r2) + r4 = None + return r4 [case testNewEmptyDict] from typing import Dict @@ -56,23 +52,18 @@ def f(x: object) -> None: [out] def f(x): x :: object - r0, r1 :: short_int - r2 :: str - r3 :: native_int - r4, r5 :: object - r6, d :: dict - r7 :: None + r0 :: str + r1, r2 :: object + r3, d :: dict + r4 :: None L0: - r0 = 2 - r1 = 4 - r2 = unicode_1 :: static - r3 = 2 - r4 = box(short_int, r0) - r5 = box(short_int, r1) - r6 = CPyDict_Build(r3, r4, r5, r2, x) - d = r6 - r7 = None - return r7 + r0 = unicode_1 :: static + r1 = box(short_int, 2) + r2 = box(short_int, 4) + r3 = CPyDict_Build(2, r1, r2, r0, x) + d = r3 + r4 = None + return r4 [case testInDict] from typing import Dict @@ -84,22 +75,20 @@ def f(d: Dict[int, int]) -> bool: [out] def f(d): d :: dict - r0 :: short_int - r1 :: object - r2 :: int32 - r3, r4, r5 :: bool + r0 :: object + r1 :: int32 + r2, r3, r4 :: bool L0: - r0 = 8 - r1 = box(short_int, r0) - r2 = PyDict_Contains(d, r1) - r3 = truncate r2: int32 to builtins.bool - if r3 goto L1 else goto L2 :: bool + r0 = box(short_int, 8) + r1 = PyDict_Contains(d, r0) + r2 = truncate r1: int32 to builtins.bool + if r2 goto L1 else goto L2 :: bool L1: - r4 = True - return r4 + r3 = True + return r3 L2: - r5 = False - return r5 + r4 = False + return r4 L3: unreachable @@ -113,23 +102,21 @@ def f(d: Dict[int, int]) -> bool: [out] def f(d): d :: dict - r0 :: short_int - r1 :: object - r2 :: int32 - r3, r4, r5, r6 :: bool + r0 :: object + r1 :: int32 + r2, r3, r4, r5 :: bool L0: - r0 = 8 - r1 = box(short_int, r0) - r2 = PyDict_Contains(d, r1) - r3 = truncate r2: int32 to builtins.bool - r4 = !r3 - if r4 goto L1 else goto L2 :: bool + r0 = box(short_int, 8) + r1 = PyDict_Contains(d, r0) + r2 = truncate r1: int32 to builtins.bool + r3 = !r2 + if r3 goto L1 else goto L2 :: bool L1: - r5 = True - return r5 + r4 = True + return r4 L2: - r6 = False - return r6 + r5 = False + return r5 L3: unreachable @@ -157,44 +144,41 @@ def increment(d: Dict[str, int]) -> Dict[str, int]: [out] def increment(d): d :: dict - r0, r1 :: short_int - r2 :: int - r3 :: object - r4 :: tuple[bool, int, object] - r5 :: int - r6 :: bool - r7 :: object - k, r8 :: str - r9 :: object - r10 :: short_int - r11, r12 :: object - r13 :: int32 - r14, r15 :: bool + r0 :: short_int + r1 :: int + r2 :: object + r3 :: tuple[bool, int, object] + r4 :: int + r5 :: bool + r6 :: object + k, r7 :: str + r8 :: object + r9, r10 :: object + r11 :: int32 + r12, r13 :: bool L0: r0 = 0 - r1 = r0 - r2 = len d :: dict - r3 = CPyDict_GetKeysIter(d) + r1 = len d :: dict + r2 = CPyDict_GetKeysIter(d) L1: - r4 = CPyDict_NextKey(r3, r1) - r5 = r4[1] - r1 = r5 - r6 = r4[0] - if r6 goto L2 else goto L4 :: bool + r3 = CPyDict_NextKey(r2, r0) + r4 = r3[1] + r0 = r4 + r5 = r3[0] + if r5 goto L2 else goto L4 :: bool L2: - r7 = r4[2] - r8 = cast(str, r7) - k = r8 - r9 = CPyDict_GetItem(d, k) - r10 = 2 - r11 = box(short_int, r10) - r12 = r9 += r11 - r13 = CPyDict_SetItem(d, k, r12) + r6 = r3[2] + r7 = cast(str, r6) + k = r7 + r8 = CPyDict_GetItem(d, k) + r9 = box(short_int, 2) + r10 = r8 += r9 + r11 = CPyDict_SetItem(d, k, r10) L3: - r14 = CPyDict_CheckSize(d, r2) + r12 = CPyDict_CheckSize(d, r1) goto L1 L4: - r15 = CPy_NoErrOccured() + r13 = CPy_NoErrOccured() L5: return d @@ -206,26 +190,20 @@ def f(x: str, y: Dict[str, int]) -> Dict[str, int]: def f(x, y): x :: str y :: dict - r0 :: short_int - r1 :: str - r2 :: short_int - r3 :: native_int + r0 :: str + r1 :: object + r2 :: dict + r3 :: int32 r4 :: object - r5 :: dict - r6 :: int32 - r7 :: object - r8 :: int32 + r5 :: int32 L0: - r0 = 4 - r1 = unicode_3 :: static ('z') - r2 = 6 - r3 = 1 - r4 = box(short_int, r0) - r5 = CPyDict_Build(r3, x, r4) - r6 = CPyDict_UpdateInDisplay(r5, y) - r7 = box(short_int, r2) - r8 = CPyDict_SetItem(r5, r1, r7) - return r5 + r0 = unicode_3 :: static ('z') + r1 = box(short_int, 4) + r2 = CPyDict_Build(1, x, r1) + r3 = CPyDict_UpdateInDisplay(r2, y) + r4 = box(short_int, 6) + r5 = CPyDict_SetItem(r2, r0, r4) + return r2 [case testDictIterationMethods] from typing import Dict @@ -238,89 +216,87 @@ def print_dict_methods(d1: Dict[int, int], d2: Dict[int, int]) -> None: [out] def print_dict_methods(d1, d2): d1, d2 :: dict - r0, r1 :: short_int - r2 :: int - r3 :: object - r4 :: tuple[bool, int, object] - r5 :: int - r6 :: bool - r7 :: object - v, r8 :: int - r9 :: object - r10 :: int32 - r11 :: bool - r12 :: None - r13, r14 :: bool - r15, r16 :: short_int - r17 :: int - r18 :: object - r19 :: tuple[bool, int, object, object] - r20 :: int - r21 :: bool - r22, r23 :: object - r24, r25, k :: int - r26, r27, r28, r29, r30 :: object - r31 :: int32 - r32, r33 :: bool - r34 :: None + r0 :: short_int + r1 :: int + r2 :: object + r3 :: tuple[bool, int, object] + r4 :: int + r5 :: bool + r6 :: object + v, r7 :: int + r8 :: object + r9 :: int32 + r10 :: bool + r11 :: None + r12, r13 :: bool + r14 :: short_int + r15 :: int + r16 :: object + r17 :: tuple[bool, int, object, object] + r18 :: int + r19 :: bool + r20, r21 :: object + r22, r23, k :: int + r24, r25, r26, r27, r28 :: object + r29 :: int32 + r30, r31 :: bool + r32 :: None L0: r0 = 0 - r1 = r0 - r2 = len d1 :: dict - r3 = CPyDict_GetValuesIter(d1) + r1 = len d1 :: dict + r2 = CPyDict_GetValuesIter(d1) L1: - r4 = CPyDict_NextValue(r3, r1) - r5 = r4[1] - r1 = r5 - r6 = r4[0] - if r6 goto L2 else goto L6 :: bool + r3 = CPyDict_NextValue(r2, r0) + r4 = r3[1] + r0 = r4 + r5 = r3[0] + if r5 goto L2 else goto L6 :: bool L2: - r7 = r4[2] - r8 = unbox(int, r7) - v = r8 - r9 = box(int, v) - r10 = PyDict_Contains(d2, r9) - r11 = truncate r10: int32 to builtins.bool - if r11 goto L3 else goto L4 :: bool + r6 = r3[2] + r7 = unbox(int, r6) + v = r7 + r8 = box(int, v) + r9 = PyDict_Contains(d2, r8) + r10 = truncate r9: int32 to builtins.bool + if r10 goto L3 else goto L4 :: bool L3: - r12 = None - return r12 + r11 = None + return r11 L4: L5: - r13 = CPyDict_CheckSize(d1, r2) + r12 = CPyDict_CheckSize(d1, r1) goto L1 L6: - r14 = CPy_NoErrOccured() + r13 = CPy_NoErrOccured() L7: - r15 = 0 - r16 = r15 - r17 = len d2 :: dict - r18 = CPyDict_GetItemsIter(d2) + r14 = 0 + r15 = len d2 :: dict + r16 = CPyDict_GetItemsIter(d2) L8: - r19 = CPyDict_NextItem(r18, r16) - r20 = r19[1] - r16 = r20 - r21 = r19[0] - if r21 goto L9 else goto L11 :: bool + r17 = CPyDict_NextItem(r16, r14) + r18 = r17[1] + r14 = r18 + r19 = r17[0] + if r19 goto L9 else goto L11 :: bool L9: - r22 = r19[2] - r23 = r19[3] - r24 = unbox(int, r22) - r25 = unbox(int, r23) - k = r24 - v = r25 - r26 = box(int, k) - r27 = CPyDict_GetItem(d2, r26) - r28 = box(int, v) - r29 = r27 += r28 - r30 = box(int, k) - r31 = CPyDict_SetItem(d2, r30, r29) + r20 = r17[2] + r21 = r17[3] + r22 = unbox(int, r20) + r23 = unbox(int, r21) + k = r22 + v = r23 + r24 = box(int, k) + r25 = CPyDict_GetItem(d2, r24) + r26 = box(int, v) + r27 = r25 += r26 + r28 = box(int, k) + r29 = CPyDict_SetItem(d2, r28, r27) L10: - r32 = CPyDict_CheckSize(d2, r17) + r30 = CPyDict_CheckSize(d2, r15) goto L8 L11: - r33 = CPy_NoErrOccured() + r31 = CPy_NoErrOccured() L12: - r34 = None - return r34 + r32 = None + return r32 diff --git a/mypyc/test-data/irbuild-generics.test b/mypyc/test-data/irbuild-generics.test index eae3b058b708..069c33f303e9 100644 --- a/mypyc/test-data/irbuild-generics.test +++ b/mypyc/test-data/irbuild-generics.test @@ -15,14 +15,12 @@ L0: return x def g(x): x :: list - r0 :: short_int - r1 :: object - r2 :: list + r0 :: object + r1 :: list L0: - r0 = 0 - r1 = CPyList_GetItemShort(x, r0) - r2 = [r1] - return r2 + r0 = CPyList_GetItemShort(x, 0) + r1 = [r0] + return r1 def h(x, y): x :: int y :: list @@ -52,25 +50,21 @@ def f() -> None: [out] def f(): r0, c :: __main__.C - r1 :: short_int - r2 :: object - r3 :: bool - r4 :: short_int - r5 :: object - r6, r7 :: int - r8 :: None + r1 :: object + r2 :: bool + r3 :: object + r4, r5 :: int + r6 :: None L0: r0 = C() c = r0 - r1 = 2 - r2 = box(short_int, r1) - c.x = r2; r3 = is_error - r4 = 4 - r5 = c.x - r6 = unbox(int, r5) - r7 = CPyTagged_Add(r4, r6) - r8 = None - return r8 + r1 = box(short_int, 2) + c.x = r1; r2 = is_error + r3 = c.x + r4 = unbox(int, r3) + r5 = CPyTagged_Add(4, r4) + r6 = None + return r6 [case testGenericMethod] from typing import TypeVar, Generic @@ -116,26 +110,22 @@ def f(x): x :: __main__.C r0 :: object r1, y :: int - r2 :: short_int - r3 :: int - r4 :: object - r5 :: None - r6 :: short_int - r7 :: object - r8 :: __main__.C - r9 :: None + r2 :: int + r3 :: object + r4 :: None + r5 :: object + r6 :: __main__.C + r7 :: None L0: r0 = x.get() r1 = unbox(int, r0) y = r1 - r2 = 2 - r3 = CPyTagged_Add(y, r2) - r4 = box(int, r3) - r5 = x.set(r4) - r6 = 4 - r7 = box(short_int, r6) - r8 = C(r7) - x = r8 - r9 = None - return r9 + r2 = CPyTagged_Add(y, 2) + r3 = box(int, r2) + r4 = x.set(r3) + r5 = box(short_int, 4) + r6 = C(r5) + x = r6 + r7 = None + return r7 diff --git a/mypyc/test-data/irbuild-int.test b/mypyc/test-data/irbuild-int.test index 74d5861713f4..9c4ca8e17a0e 100644 --- a/mypyc/test-data/irbuild-int.test +++ b/mypyc/test-data/irbuild-int.test @@ -5,21 +5,20 @@ def f(x: int, y: int) -> bool: def f(x, y): x, y :: int r0 :: bool - r1, r2, r3 :: native_int - r4, r5, r6, r7 :: bool + r1 :: native_int + r2, r3, r4, r5 :: bool L0: - r1 = 1 - r2 = x & r1 - r3 = 0 - r4 = r2 == r3 - if r4 goto L1 else goto L2 :: bool + r1 = x & 1 + r2 = r1 == 0 + if r2 goto L1 else goto L2 :: bool L1: - r5 = x != y - r0 = r5 + r3 = x != y + r0 = r3 goto L3 L2: - r6 = CPyTagged_IsEq_(x, y) - r7 = !r6 - r0 = r7 + r4 = CPyTagged_IsEq_(x, y) + r5 = !r4 + r0 = r5 L3: return r0 + diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test index 454bea233fc7..b07e5c40b118 100644 --- a/mypyc/test-data/irbuild-lists.test +++ b/mypyc/test-data/irbuild-lists.test @@ -5,14 +5,12 @@ def f(x: List[int]) -> int: [out] def f(x): x :: list - r0 :: short_int - r1 :: object - r2 :: int + r0 :: object + r1 :: int L0: - r0 = 0 - r1 = CPyList_GetItemShort(x, r0) - r2 = unbox(int, r1) - return r2 + r0 = CPyList_GetItemShort(x, 0) + r1 = unbox(int, r0) + return r1 [case testListOfListGet] from typing import List @@ -21,14 +19,12 @@ def f(x: List[List[int]]) -> List[int]: [out] def f(x): x :: list - r0 :: short_int - r1 :: object - r2 :: list + r0 :: object + r1 :: list L0: - r0 = 0 - r1 = CPyList_GetItemShort(x, r0) - r2 = cast(list, r1) - return r2 + r0 = CPyList_GetItemShort(x, 0) + r1 = cast(list, r0) + return r1 [case testListOfListGet2] from typing import List @@ -37,20 +33,16 @@ def f(x: List[List[int]]) -> int: [out] def f(x): x :: list - r0 :: short_int - r1 :: object - r2 :: list - r3 :: short_int - r4 :: object - r5 :: int + r0 :: object + r1 :: list + r2 :: object + r3 :: int L0: - r0 = 0 - r1 = CPyList_GetItemShort(x, r0) - r2 = cast(list, r1) - r3 = 2 - r4 = CPyList_GetItemShort(r2, r3) - r5 = unbox(int, r4) - return r5 + r0 = CPyList_GetItemShort(x, 0) + r1 = cast(list, r0) + r2 = CPyList_GetItemShort(r1, 2) + r3 = unbox(int, r2) + return r3 [case testListSet] from typing import List @@ -59,17 +51,14 @@ def f(x: List[int]) -> None: [out] def f(x): x :: list - r0, r1 :: short_int - r2 :: object - r3 :: bool - r4 :: None + r0 :: object + r1 :: bool + r2 :: None L0: - r0 = 2 - r1 = 0 - r2 = box(short_int, r0) - r3 = CPyList_SetItem(x, r1, r2) - r4 = None - return r4 + r0 = box(short_int, 2) + r1 = CPyList_SetItem(x, 0, r0) + r2 = None + return r2 [case testNewListEmpty] from typing import List @@ -91,19 +80,16 @@ def f() -> None: x: List[int] = [1, 2] [out] def f(): - r0, r1 :: short_int - r2, r3 :: object - r4, x :: list - r5 :: None + r0, r1 :: object + r2, x :: list + r3 :: None L0: - r0 = 2 - r1 = 4 - r2 = box(short_int, r0) - r3 = box(short_int, r1) - r4 = [r2, r3] - x = r4 - r5 = None - return r5 + r0 = box(short_int, 2) + r1 = box(short_int, 4) + r2 = [r0, r1] + x = r2 + r3 = None + return r3 [case testListMultiply] from typing import List @@ -113,24 +99,19 @@ def f(a: List[int]) -> None: [out] def f(a): a :: list - r0 :: short_int - r1, b :: list - r2, r3 :: short_int - r4 :: object - r5, r6 :: list - r7 :: None + r0, b :: list + r1 :: object + r2, r3 :: list + r4 :: None L0: - r0 = 4 - r1 = CPySequence_Multiply(a, r0) - b = r1 - r2 = 6 - r3 = 8 - r4 = box(short_int, r3) - r5 = [r4] - r6 = CPySequence_RMultiply(r2, r5) - b = r6 - r7 = None - return r7 + r0 = CPySequence_Multiply(a, 4) + b = r0 + r1 = box(short_int, 8) + r2 = [r1] + r3 = CPySequence_RMultiply(6, r2) + b = r3 + r4 = None + return r4 [case testListLen] from typing import List @@ -171,33 +152,29 @@ def increment(l: List[int]) -> List[int]: [out] def increment(l): l :: list - r0, r1, r2 :: short_int + r0, r1 :: short_int i :: int - r3 :: bool - r4 :: object - r5 :: short_int - r6, r7 :: object - r8 :: bool - r9, r10 :: short_int + r2 :: bool + r3 :: object + r4, r5 :: object + r6 :: bool + r7 :: short_int L0: - r0 = 0 - r1 = len l :: list - r2 = r0 - i = r2 + r0 = len l :: list + r1 = 0 + i = r1 L1: - r3 = r2 < r1 :: signed - if r3 goto L2 else goto L4 :: bool + r2 = r1 < r0 :: signed + if r2 goto L2 else goto L4 :: bool L2: - r4 = CPyList_GetItem(l, i) - r5 = 2 - r6 = box(short_int, r5) - r7 = r4 += r6 - r8 = CPyList_SetItem(l, i, r7) + r3 = CPyList_GetItem(l, i) + r4 = box(short_int, 2) + r5 = r3 += r4 + r6 = CPyList_SetItem(l, i, r5) L3: - r9 = 2 - r10 = r2 + r9 - r2 = r10 - i = r10 + r7 = r1 + 2 + r1 = r7 + i = r7 goto L1 L4: return l @@ -209,21 +186,17 @@ def f(x: List[int], y: List[int]) -> List[int]: [out] def f(x, y): x, y :: list - r0, r1, r2 :: short_int - r3, r4 :: object - r5 :: list - r6, r7, r8 :: object - r9 :: int32 + r0, r1 :: object + r2 :: list + r3, r4, r5 :: object + r6 :: int32 L0: - r0 = 2 - r1 = 4 - r2 = 6 - r3 = box(short_int, r0) - r4 = box(short_int, r1) - r5 = [r3, r4] - r6 = CPyList_Extend(r5, x) - r7 = CPyList_Extend(r5, y) - r8 = box(short_int, r2) - r9 = PyList_Append(r5, r8) - return r5 + r0 = box(short_int, 2) + r1 = box(short_int, 4) + r2 = [r0, r1] + r3 = CPyList_Extend(r2, x) + r4 = CPyList_Extend(r2, y) + r5 = box(short_int, 6) + r6 = PyList_Append(r2, r5) + return r2 diff --git a/mypyc/test-data/irbuild-nested.test b/mypyc/test-data/irbuild-nested.test index ae3ebe9bcf42..6419dc325da7 100644 --- a/mypyc/test-data/irbuild-nested.test +++ b/mypyc/test-data/irbuild-nested.test @@ -336,41 +336,35 @@ def inner_b_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.inner_b_obj r0 :: __main__.b_env r1, inner :: object - r2 :: short_int - r3 :: bool - r4 :: short_int - foo, r5 :: int + r2 :: bool + foo, r3 :: int L0: r0 = __mypyc_self__.__mypyc_env__ r1 = r0.inner inner = r1 - r2 = 8 - r0.num = r2; r3 = is_error - r4 = 12 - foo = r4 - r5 = r0.num - return r5 + r0.num = 8; r2 = is_error + foo = 12 + r3 = r0.num + return r3 def b(): r0 :: __main__.b_env - r1 :: short_int - r2 :: bool - r3 :: __main__.inner_b_obj - r4, r5 :: bool - r6, r7 :: object - r8, r9, r10 :: int + r1 :: bool + r2 :: __main__.inner_b_obj + r3, r4 :: bool + r5, r6 :: object + r7, r8, r9 :: int L0: r0 = b_env() - r1 = 6 - r0.num = r1; r2 = is_error - r3 = inner_b_obj() - r3.__mypyc_env__ = r0; r4 = is_error - r0.inner = r3; r5 = is_error - r6 = r0.inner - r7 = py_call(r6) - r8 = unbox(int, r7) - r9 = r0.num - r10 = CPyTagged_Add(r8, r9) - return r10 + r0.num = 6; r1 = is_error + r2 = inner_b_obj() + r2.__mypyc_env__ = r0; r3 = is_error + r0.inner = r2; r4 = is_error + r5 = r0.inner + r6 = py_call(r5) + r7 = unbox(int, r6) + r8 = r0.num + r9 = CPyTagged_Add(r7, r8) + return r9 def inner_c_obj.__get__(__mypyc_self__, instance, owner): __mypyc_self__, instance, owner, r0 :: object r1 :: bool @@ -503,13 +497,12 @@ def b_a_obj.__call__(__mypyc_self__): r2 :: __main__.b_a_env r3 :: bool r4 :: int - r5 :: short_int - r6 :: int - r7 :: bool - r8 :: __main__.c_a_b_obj - r9, r10 :: bool - r11, r12 :: object - r13 :: int + r5 :: int + r6 :: bool + r7 :: __main__.c_a_b_obj + r8, r9 :: bool + r10, r11 :: object + r12 :: int L0: r0 = __mypyc_self__.__mypyc_env__ r1 = r0.b @@ -517,35 +510,32 @@ L0: r2 = b_a_env() r2.__mypyc_env__ = r0; r3 = is_error r4 = r0.x - r5 = 2 - r6 = CPyTagged_Add(r4, r5) - r0.x = r6; r7 = is_error - r8 = c_a_b_obj() - r8.__mypyc_env__ = r2; r9 = is_error - r2.c = r8; r10 = is_error - r11 = r2.c - r12 = py_call(r11) - r13 = unbox(int, r12) - return r13 + r5 = CPyTagged_Add(r4, 2) + r0.x = r5; r6 = is_error + r7 = c_a_b_obj() + r7.__mypyc_env__ = r2; r8 = is_error + r2.c = r7; r9 = is_error + r10 = r2.c + r11 = py_call(r10) + r12 = unbox(int, r11) + return r12 def a(): r0 :: __main__.a_env - r1 :: short_int - r2 :: bool - r3 :: __main__.b_a_obj - r4, r5 :: bool - r6, r7 :: object - r8 :: int + r1 :: bool + r2 :: __main__.b_a_obj + r3, r4 :: bool + r5, r6 :: object + r7 :: int L0: r0 = a_env() - r1 = 2 - r0.x = r1; r2 = is_error - r3 = b_a_obj() - r3.__mypyc_env__ = r0; r4 = is_error - r0.b = r3; r5 = is_error - r6 = r0.b - r7 = py_call(r6) - r8 = unbox(int, r7) - return r8 + r0.x = 2; r1 = is_error + r2 = b_a_obj() + r2.__mypyc_env__ = r0; r3 = is_error + r0.b = r2; r4 = is_error + r5 = r0.b + r6 = py_call(r5) + r7 = unbox(int, r6) + return r7 [case testNestedFunctionInsideStatements] def f(flag: bool) -> str: @@ -668,16 +658,14 @@ def foo_f_obj.__call__(__mypyc_self__): r0 :: __main__.f_env r1, foo :: object r2 :: int - r3 :: short_int - r4 :: int + r3 :: int L0: r0 = __mypyc_self__.__mypyc_env__ r1 = r0.foo foo = r1 r2 = r0.a - r3 = 2 - r4 = CPyTagged_Add(r2, r3) - return r4 + r3 = CPyTagged_Add(r2, 2) + return r3 def bar_f_obj.__get__(__mypyc_self__, instance, owner): __mypyc_self__, instance, owner, r0 :: object r1 :: bool @@ -722,30 +710,25 @@ def baz_f_obj.__call__(__mypyc_self__, n): n :: int r0 :: __main__.f_env r1, baz :: object - r2 :: short_int - r3 :: bool - r4, r5 :: short_int - r6 :: int - r7, r8 :: object - r9, r10 :: int + r2 :: bool + r3 :: int + r4, r5 :: object + r6, r7 :: int L0: r0 = __mypyc_self__.__mypyc_env__ r1 = r0.baz baz = r1 - r2 = 0 - r3 = CPyTagged_IsEq(n, r2) - if r3 goto L1 else goto L2 :: bool + r2 = CPyTagged_IsEq(n, 0) + if r2 goto L1 else goto L2 :: bool L1: - r4 = 0 - return r4 + return 0 L2: - r5 = 2 - r6 = CPyTagged_Subtract(n, r5) - r7 = box(int, r6) - r8 = py_call(baz, r7) - r9 = unbox(int, r8) - r10 = CPyTagged_Add(n, r9) - return r10 + r3 = CPyTagged_Subtract(n, 2) + r4 = box(int, r3) + r5 = py_call(baz, r4) + r6 = unbox(int, r5) + r7 = CPyTagged_Add(n, r6) + return r7 def f(a): a :: int r0 :: __main__.f_env @@ -869,20 +852,16 @@ def baz(n: int) -> int: [out] def baz(n): n :: int - r0 :: short_int - r1 :: bool - r2, r3 :: short_int - r4, r5, r6 :: int + r0 :: bool + r1, r2, r3 :: int L0: - r0 = 0 - r1 = CPyTagged_IsEq(n, r0) - if r1 goto L1 else goto L2 :: bool + r0 = CPyTagged_IsEq(n, 0) + if r0 goto L1 else goto L2 :: bool L1: - r2 = 0 - return r2 + return 0 L2: - r3 = 2 - r4 = CPyTagged_Subtract(n, r3) - r5 = baz(r4) - r6 = CPyTagged_Add(n, r5) - return r6 + r1 = CPyTagged_Subtract(n, 2) + r2 = baz(r1) + r3 = CPyTagged_Add(n, r2) + return r3 + diff --git a/mypyc/test-data/irbuild-optional.test b/mypyc/test-data/irbuild-optional.test index 7099fafc5698..de61c60cf1d1 100644 --- a/mypyc/test-data/irbuild-optional.test +++ b/mypyc/test-data/irbuild-optional.test @@ -13,18 +13,15 @@ def f(x): r0 :: None r1 :: object r2 :: bool - r3, r4 :: short_int L0: r0 = None r1 = box(None, r0) r2 = x is r1 if r2 goto L1 else goto L2 :: bool L1: - r3 = 2 - return r3 + return 2 L2: - r4 = 4 - return r4 + return 4 [case testIsNotNone] from typing import Optional @@ -41,7 +38,6 @@ def f(x): r0 :: None r1 :: object r2, r3 :: bool - r4, r5 :: short_int L0: r0 = None r1 = box(None, r0) @@ -49,11 +45,9 @@ L0: r3 = !r2 if r3 goto L1 else goto L2 :: bool L1: - r4 = 2 - return r4 + return 2 L2: - r5 = 4 - return r5 + return 4 [case testIsTruthy] from typing import Optional @@ -69,17 +63,14 @@ def f(x): x :: union[__main__.A, None] r0 :: object r1 :: bool - r2, r3 :: short_int L0: r0 = builtins.None :: object r1 = x is not r0 if r1 goto L1 else goto L2 :: bool L1: - r2 = 2 - return r2 + return 2 L2: - r3 = 4 - return r3 + return 4 [case testIsTruthyOverride] from typing import Optional @@ -108,7 +99,6 @@ def f(x): r1 :: bool r2 :: __main__.A r3 :: bool - r4, r5 :: short_int L0: r0 = builtins.None :: object r1 = x is not r0 @@ -118,11 +108,9 @@ L1: r3 = bool r2 :: object if r3 goto L2 else goto L3 :: bool L2: - r4 = 2 - return r4 + return 2 L3: - r5 = 4 - return r5 + return 4 [case testAssignToOptional] from typing import Optional @@ -145,16 +133,14 @@ def f(x, y, z): r0 :: None r1 :: object r2 :: __main__.A - r3 :: short_int - r4 :: object - r5, a :: __main__.A - r6 :: short_int - r7 :: object - r8 :: bool - r9 :: None - r10 :: object - r11 :: bool - r12 :: None + r3 :: object + r4, a :: __main__.A + r5 :: object + r6 :: bool + r7 :: None + r8 :: object + r9 :: bool + r10 :: None L0: r0 = None r1 = box(None, r0) @@ -162,19 +148,17 @@ L0: r2 = A() x = r2 x = y - r3 = 2 - r4 = box(short_int, r3) - z = r4 - r5 = A() - a = r5 - r6 = 2 - r7 = box(short_int, r6) - a.a = r7; r8 = is_error - r9 = None - r10 = box(None, r9) - a.a = r10; r11 = is_error - r12 = None - return r12 + r3 = box(short_int, 2) + z = r3 + r4 = A() + a = r4 + r5 = box(short_int, 2) + a.a = r5; r6 = is_error + r7 = None + r8 = box(None, r7) + a.a = r8; r9 = is_error + r10 = None + return r10 [case testBoxOptionalListItem] from typing import List, Optional @@ -185,25 +169,20 @@ def f(x: List[Optional[int]]) -> None: [out] def f(x): x :: list - r0, r1 :: short_int - r2 :: object - r3 :: bool - r4 :: None - r5 :: short_int - r6 :: object - r7 :: bool - r8 :: None + r0 :: object + r1 :: bool + r2 :: None + r3 :: object + r4 :: bool + r5 :: None L0: - r0 = 0 - r1 = 0 - r2 = box(short_int, r0) - r3 = CPyList_SetItem(x, r1, r2) - r4 = None - r5 = 2 - r6 = box(None, r4) - r7 = CPyList_SetItem(x, r5, r6) - r8 = None - return r8 + r0 = box(short_int, 0) + r1 = CPyList_SetItem(x, 0, r0) + r2 = None + r3 = box(None, r2) + r4 = CPyList_SetItem(x, 2, r3) + r5 = None + return r5 [case testNarrowDownFromOptional] from typing import Optional @@ -253,36 +232,34 @@ def f(y): r0 :: None x :: union[int, None] r1 :: object - r2 :: short_int - r3 :: bool - r4 :: object - r5 :: None - r6 :: object - r7, r8 :: bool - r9 :: int - r10 :: None + r2 :: bool + r3 :: object + r4 :: None + r5 :: object + r6, r7 :: bool + r8 :: int + r9 :: None L0: r0 = None r1 = box(None, r0) x = r1 - r2 = 2 - r3 = CPyTagged_IsEq(y, r2) - if r3 goto L1 else goto L2 :: bool + r2 = CPyTagged_IsEq(y, 2) + if r2 goto L1 else goto L2 :: bool L1: - r4 = box(int, y) - x = r4 + r3 = box(int, y) + x = r3 L2: - r5 = None - r6 = box(None, r5) - r7 = x is r6 - r8 = !r7 - if r8 goto L3 else goto L4 :: bool + r4 = None + r5 = box(None, r4) + r6 = x is r5 + r7 = !r6 + if r7 goto L3 else goto L4 :: bool L3: - r9 = unbox(int, x) - y = r9 + r8 = unbox(int, x) + y = r8 L4: - r10 = None - return r10 + r9 = None + return r9 [case testUnionType] from typing import Union @@ -302,10 +279,9 @@ def f(x): r1 :: int32 r2 :: bool r3 :: int - r4 :: short_int - r5 :: int - r6 :: __main__.A - r7 :: int + r4 :: int + r5 :: __main__.A + r6 :: int L0: r0 = int r1 = PyObject_IsInstance(x, r0) @@ -313,13 +289,12 @@ L0: if r2 goto L1 else goto L2 :: bool L1: r3 = unbox(int, x) - r4 = 2 - r5 = CPyTagged_Add(r3, r4) - return r5 + r4 = CPyTagged_Add(r3, 2) + return r4 L2: - r6 = cast(__main__.A, x) - r7 = r6.a - return r7 + r5 = cast(__main__.A, x) + r6 = r5.a + return r6 L3: unreachable @@ -331,14 +306,12 @@ def f(x: List[Union[int, str]]) -> object: [out] def f(x): x :: list - r0 :: short_int - r1 :: object - r2 :: union[int, str] + r0 :: object + r1 :: union[int, str] L0: - r0 = 0 - r1 = CPyList_GetItemShort(x, r0) - r2 = cast(union[int, str], r1) - return r2 + r0 = CPyList_GetItemShort(x, 0) + r1 = cast(union[int, str], r0) + return r1 [case testUnionAttributeAccess] from typing import Union @@ -419,57 +392,53 @@ L0: def C.f(self, x): self :: __main__.C x :: object - r0 :: short_int L0: - r0 = 0 - return r0 + return 0 def g(o): o :: union[__main__.A, __main__.B, __main__.C] - r0 :: short_int - r1, r2 :: object - r3 :: bool - r4 :: __main__.A - r5 :: int - r6, r7 :: object - r8 :: bool - r9 :: __main__.B - r10, r11 :: object - r12 :: __main__.C - r13 :: object - r14 :: int - r15, z :: object - r16 :: None + r0, r1 :: object + r2 :: bool + r3 :: __main__.A + r4 :: int + r5, r6 :: object + r7 :: bool + r8 :: __main__.B + r9, r10 :: object + r11 :: __main__.C + r12 :: object + r13 :: int + r14, z :: object + r15 :: None L0: - r0 = 2 - r2 = __main__.A :: type - r3 = type_is o, r2 - if r3 goto L1 else goto L2 :: bool + r1 = __main__.A :: type + r2 = type_is o, r1 + if r2 goto L1 else goto L2 :: bool L1: - r4 = cast(__main__.A, o) - r5 = r4.f(r0) - r6 = box(int, r5) - r1 = r6 + r3 = cast(__main__.A, o) + r4 = r3.f(2) + r5 = box(int, r4) + r0 = r5 goto L5 L2: - r7 = __main__.B :: type - r8 = type_is o, r7 - if r8 goto L3 else goto L4 :: bool + r6 = __main__.B :: type + r7 = type_is o, r6 + if r7 goto L3 else goto L4 :: bool L3: - r9 = cast(__main__.B, o) - r10 = box(short_int, r0) - r11 = r9.f(r10) - r1 = r11 + r8 = cast(__main__.B, o) + r9 = box(short_int, 2) + r10 = r8.f(r9) + r0 = r10 goto L5 L4: - r12 = cast(__main__.C, o) - r13 = box(short_int, r0) - r14 = r12.f(r13) - r15 = box(int, r14) - r1 = r15 + r11 = cast(__main__.C, o) + r12 = box(short_int, 2) + r13 = r11.f(r12) + r14 = box(int, r13) + r0 = r14 L5: - z = r1 - r16 = None - return r16 + z = r0 + r15 = None + return r15 [case testUnionWithNonNativeItem] from typing import Union diff --git a/mypyc/test-data/irbuild-set.test b/mypyc/test-data/irbuild-set.test index ccffc1af5351..02ec9f3b9a33 100644 --- a/mypyc/test-data/irbuild-set.test +++ b/mypyc/test-data/irbuild-set.test @@ -4,26 +4,22 @@ def f() -> Set[int]: return {1, 2, 3} [out] def f(): - r0, r1, r2 :: short_int - r3 :: set - r4 :: object - r5 :: int32 - r6 :: object - r7 :: int32 - r8 :: object - r9 :: int32 + r0 :: set + r1 :: object + r2 :: int32 + r3 :: object + r4 :: int32 + r5 :: object + r6 :: int32 L0: - r0 = 2 - r1 = 4 - r2 = 6 - r3 = set - r4 = box(short_int, r0) - r5 = PySet_Add(r3, r4) - r6 = box(short_int, r1) - r7 = PySet_Add(r3, r6) - r8 = box(short_int, r2) - r9 = PySet_Add(r3, r8) - return r3 + r0 = set + r1 = box(short_int, 2) + r2 = PySet_Add(r0, r1) + r3 = box(short_int, 4) + r4 = PySet_Add(r0, r3) + r5 = box(short_int, 6) + r6 = PySet_Add(r0, r5) + return r0 [case testNewEmptySet] from typing import Set @@ -54,28 +50,24 @@ def f() -> int: return len({1, 2, 3}) [out] def f(): - r0, r1, r2 :: short_int - r3 :: set - r4 :: object - r5 :: int32 - r6 :: object - r7 :: int32 - r8 :: object - r9 :: int32 - r10 :: int + r0 :: set + r1 :: object + r2 :: int32 + r3 :: object + r4 :: int32 + r5 :: object + r6 :: int32 + r7 :: int L0: - r0 = 2 - r1 = 4 - r2 = 6 - r3 = set - r4 = box(short_int, r0) - r5 = PySet_Add(r3, r4) - r6 = box(short_int, r1) - r7 = PySet_Add(r3, r6) - r8 = box(short_int, r2) - r9 = PySet_Add(r3, r8) - r10 = len r3 :: set - return r10 + r0 = set + r1 = box(short_int, 2) + r2 = PySet_Add(r0, r1) + r3 = box(short_int, 4) + r4 = PySet_Add(r0, r3) + r5 = box(short_int, 6) + r6 = PySet_Add(r0, r5) + r7 = len r0 :: set + return r7 [case testSetContains] from typing import Set @@ -84,31 +76,26 @@ def f() -> bool: return (5 in x) [out] def f(): - r0, r1 :: short_int - r2 :: set + r0 :: set + r1 :: object + r2 :: int32 r3 :: object r4 :: int32 + x :: set r5 :: object r6 :: int32 - x :: set - r7 :: short_int - r8 :: object - r9 :: int32 - r10 :: bool + r7 :: bool L0: - r0 = 6 - r1 = 8 - r2 = set - r3 = box(short_int, r0) - r4 = PySet_Add(r2, r3) - r5 = box(short_int, r1) - r6 = PySet_Add(r2, r5) - x = r2 - r7 = 10 - r8 = box(short_int, r7) - r9 = PySet_Contains(x, r8) - r10 = truncate r9: int32 to builtins.bool - return r10 + r0 = set + r1 = box(short_int, 6) + r2 = PySet_Add(r0, r1) + r3 = box(short_int, 8) + r4 = PySet_Add(r0, r3) + x = r0 + r5 = box(short_int, 10) + r6 = PySet_Contains(x, r5) + r7 = truncate r6: int32 to builtins.bool + return r7 [case testSetRemove] from typing import Set @@ -119,17 +106,15 @@ def f() -> Set[int]: [out] def f(): r0, x :: set - r1 :: short_int - r2 :: object - r3 :: bool - r4 :: None + r1 :: object + r2 :: bool + r3 :: None L0: r0 = set x = r0 - r1 = 2 - r2 = box(short_int, r1) - r3 = CPySet_Remove(x, r2) - r4 = None + r1 = box(short_int, 2) + r2 = CPySet_Remove(x, r1) + r3 = None return x [case testSetDiscard] @@ -141,17 +126,15 @@ def f() -> Set[int]: [out] def f(): r0, x :: set - r1 :: short_int - r2 :: object - r3 :: int32 - r4 :: None + r1 :: object + r2 :: int32 + r3 :: None L0: r0 = set x = r0 - r1 = 2 - r2 = box(short_int, r1) - r3 = PySet_Discard(x, r2) - r4 = None + r1 = box(short_int, 2) + r2 = PySet_Discard(x, r1) + r3 = None return x [case testSetAdd] @@ -163,17 +146,15 @@ def f() -> Set[int]: [out] def f(): r0, x :: set - r1 :: short_int - r2 :: object - r3 :: int32 - r4 :: None + r1 :: object + r2 :: int32 + r3 :: None L0: r0 = set x = r0 - r1 = 2 - r2 = box(short_int, r1) - r3 = PySet_Add(x, r2) - r4 = None + r1 = box(short_int, 2) + r2 = PySet_Add(x, r1) + r3 = None return x [case testSetClear] @@ -231,26 +212,22 @@ def f(x: Set[int], y: Set[int]) -> Set[int]: [out] def f(x, y): x, y :: set - r0, r1, r2 :: short_int - r3 :: set - r4 :: object - r5 :: int32 - r6 :: object - r7, r8, r9 :: int32 - r10 :: object - r11 :: int32 + r0 :: set + r1 :: object + r2 :: int32 + r3 :: object + r4, r5, r6 :: int32 + r7 :: object + r8 :: int32 L0: - r0 = 2 - r1 = 4 - r2 = 6 - r3 = set - r4 = box(short_int, r0) - r5 = PySet_Add(r3, r4) - r6 = box(short_int, r1) - r7 = PySet_Add(r3, r6) - r8 = _PySet_Update(r3, x) - r9 = _PySet_Update(r3, y) - r10 = box(short_int, r2) - r11 = PySet_Add(r3, r10) - return r3 + r0 = set + r1 = box(short_int, 2) + r2 = PySet_Add(r0, r1) + r3 = box(short_int, 4) + r4 = PySet_Add(r0, r3) + r5 = _PySet_Update(r0, x) + r6 = _PySet_Update(r0, y) + r7 = box(short_int, 6) + r8 = PySet_Add(r0, r7) + return r0 diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index 988751e9cdb9..ec681d0f1376 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -5,36 +5,31 @@ def f() -> None: x = x + i [out] def f(): - r0 :: short_int x :: int - r1, r2, r3 :: short_int + r0 :: short_int i :: int - r4 :: bool - r5 :: int - r6, r7 :: short_int - r8 :: None + r1 :: bool + r2 :: int + r3 :: short_int + r4 :: None L0: + x = 0 r0 = 0 - x = r0 - r1 = 0 - r2 = 10 - r3 = r1 - i = r3 + i = r0 L1: - r4 = r3 < r2 :: signed - if r4 goto L2 else goto L4 :: bool + r1 = r0 < 10 :: signed + if r1 goto L2 else goto L4 :: bool L2: - r5 = CPyTagged_Add(x, i) - x = r5 + r2 = CPyTagged_Add(x, i) + x = r2 L3: - r6 = 2 - r7 = r3 + r6 - r3 = r7 - i = r7 + r3 = r0 + 2 + r0 = r3 + i = r3 goto L1 L4: - r8 = None - return r8 + r4 = None + return r4 [case testForInNegativeRange] def f() -> None: @@ -42,29 +37,26 @@ def f() -> None: pass [out] def f(): - r0, r1, r2 :: short_int + r0 :: short_int i :: int - r3 :: bool - r4, r5 :: short_int - r6 :: None + r1 :: bool + r2 :: short_int + r3 :: None L0: r0 = 20 - r1 = 0 - r2 = r0 - i = r2 + i = r0 L1: - r3 = CPyTagged_IsGt(r2, r1) - if r3 goto L2 else goto L4 :: bool + r1 = CPyTagged_IsGt(r0, 0) + if r1 goto L2 else goto L4 :: bool L2: L3: - r4 = -2 - r5 = r2 + r4 - r2 = r5 - i = r5 + r2 = r0 + -2 + r0 = r2 + i = r2 goto L1 L4: - r6 = None - return r6 + r3 = None + return r3 [case testBreak] def f() -> None: @@ -73,22 +65,18 @@ def f() -> None: break [out] def f(): - r0 :: short_int n :: int - r1 :: short_int - r2 :: bool - r3 :: None + r0 :: bool + r1 :: None L0: - r0 = 0 - n = r0 + n = 0 L1: - r1 = 10 - r2 = CPyTagged_IsLt(n, r1) - if r2 goto L2 else goto L3 :: bool + r0 = CPyTagged_IsLt(n, 10) + if r0 goto L2 else goto L3 :: bool L2: L3: - r3 = None - return r3 + r1 = None + return r1 [case testBreakFor] def f() -> None: @@ -96,30 +84,27 @@ def f() -> None: break [out] def f(): - r0, r1, r2 :: short_int + r0 :: short_int n :: int - r3 :: bool - r4, r5 :: short_int - r6 :: None + r1 :: bool + r2 :: short_int + r3 :: None L0: r0 = 0 - r1 = 10 - r2 = r0 - n = r2 + n = r0 L1: - r3 = r2 < r1 :: signed - if r3 goto L2 else goto L4 :: bool + r1 = r0 < 10 :: signed + if r1 goto L2 else goto L4 :: bool L2: goto L4 L3: - r4 = 2 - r5 = r2 + r4 - r2 = r5 - n = r5 + r2 = r0 + 2 + r0 = r2 + n = r2 goto L1 L4: - r6 = None - return r6 + r3 = None + return r3 [case testBreakNested] def f() -> None: @@ -130,30 +115,24 @@ def f() -> None: break [out] def f(): - r0 :: short_int n :: int - r1 :: short_int - r2 :: bool - r3 :: short_int - r4 :: bool - r5 :: None + r0 :: bool + r1 :: bool + r2 :: None L0: - r0 = 0 - n = r0 + n = 0 L1: - r1 = 10 - r2 = CPyTagged_IsLt(n, r1) - if r2 goto L2 else goto L6 :: bool + r0 = CPyTagged_IsLt(n, 10) + if r0 goto L2 else goto L6 :: bool L2: L3: - r3 = 8 - r4 = CPyTagged_IsLt(n, r3) - if r4 goto L4 else goto L5 :: bool + r1 = CPyTagged_IsLt(n, 8) + if r1 goto L4 else goto L5 :: bool L4: L5: L6: - r5 = None - return r5 + r2 = None + return r2 [case testContinue] def f() -> None: @@ -162,23 +141,19 @@ def f() -> None: continue [out] def f(): - r0 :: short_int n :: int - r1 :: short_int - r2 :: bool - r3 :: None + r0 :: bool + r1 :: None L0: - r0 = 0 - n = r0 + n = 0 L1: - r1 = 10 - r2 = CPyTagged_IsLt(n, r1) - if r2 goto L2 else goto L3 :: bool + r0 = CPyTagged_IsLt(n, 10) + if r0 goto L2 else goto L3 :: bool L2: goto L1 L3: - r3 = None - return r3 + r1 = None + return r1 [case testContinueFor] def f() -> None: @@ -186,29 +161,26 @@ def f() -> None: continue [out] def f(): - r0, r1, r2 :: short_int + r0 :: short_int n :: int - r3 :: bool - r4, r5 :: short_int - r6 :: None + r1 :: bool + r2 :: short_int + r3 :: None L0: r0 = 0 - r1 = 10 - r2 = r0 - n = r2 + n = r0 L1: - r3 = r2 < r1 :: signed - if r3 goto L2 else goto L4 :: bool + r1 = r0 < 10 :: signed + if r1 goto L2 else goto L4 :: bool L2: L3: - r4 = 2 - r5 = r2 + r4 - r2 = r5 - n = r5 + r2 = r0 + 2 + r0 = r2 + n = r2 goto L1 L4: - r6 = None - return r6 + r3 = None + return r3 [case testContinueNested] def f() -> None: @@ -219,32 +191,26 @@ def f() -> None: continue [out] def f(): - r0 :: short_int n :: int - r1 :: short_int - r2 :: bool - r3 :: short_int - r4 :: bool - r5 :: None + r0 :: bool + r1 :: bool + r2 :: None L0: - r0 = 0 - n = r0 + n = 0 L1: - r1 = 10 - r2 = CPyTagged_IsLt(n, r1) - if r2 goto L2 else goto L6 :: bool + r0 = CPyTagged_IsLt(n, 10) + if r0 goto L2 else goto L6 :: bool L2: L3: - r3 = 8 - r4 = CPyTagged_IsLt(n, r3) - if r4 goto L4 else goto L5 :: bool + r1 = CPyTagged_IsLt(n, 8) + if r1 goto L4 else goto L5 :: bool L4: goto L3 L5: goto L1 L6: - r5 = None - return r5 + r2 = None + return r2 [case testForList] from typing import List @@ -257,32 +223,28 @@ def f(ls: List[int]) -> int: [out] def f(ls): ls :: list - r0 :: short_int y :: int - r1, r2, r3 :: short_int - r4 :: bool - r5 :: object - x, r6, r7 :: int - r8, r9 :: short_int + r0, r1 :: short_int + r2 :: bool + r3 :: object + x, r4, r5 :: int + r6 :: short_int L0: + y = 0 r0 = 0 - y = r0 - r1 = 0 - r2 = r1 L1: - r3 = len ls :: list - r4 = r2 < r3 :: signed - if r4 goto L2 else goto L4 :: bool + r1 = len ls :: list + r2 = r0 < r1 :: signed + if r2 goto L2 else goto L4 :: bool L2: - r5 = ls[r2] :: unsafe list - r6 = unbox(int, r5) - x = r6 - r7 = CPyTagged_Add(y, x) - y = r7 + r3 = ls[r0] :: unsafe list + r4 = unbox(int, r3) + x = r4 + r5 = CPyTagged_Add(y, x) + y = r5 L3: - r8 = 2 - r9 = r2 + r8 - r2 = r9 + r6 = r0 + 2 + r0 = r6 goto L1 L4: return y @@ -296,44 +258,43 @@ def f(d: Dict[int, int]) -> None: [out] def f(d): d :: dict - r0, r1 :: short_int - r2 :: int - r3 :: object - r4 :: tuple[bool, int, object] - r5 :: int - r6 :: bool - r7 :: object - key, r8 :: int - r9, r10 :: object - r11 :: int - r12, r13 :: bool - r14 :: None + r0 :: short_int + r1 :: int + r2 :: object + r3 :: tuple[bool, int, object] + r4 :: int + r5 :: bool + r6 :: object + key, r7 :: int + r8, r9 :: object + r10 :: int + r11, r12 :: bool + r13 :: None L0: r0 = 0 - r1 = r0 - r2 = len d :: dict - r3 = CPyDict_GetKeysIter(d) + r1 = len d :: dict + r2 = CPyDict_GetKeysIter(d) L1: - r4 = CPyDict_NextKey(r3, r1) - r5 = r4[1] - r1 = r5 - r6 = r4[0] - if r6 goto L2 else goto L4 :: bool + r3 = CPyDict_NextKey(r2, r0) + r4 = r3[1] + r0 = r4 + r5 = r3[0] + if r5 goto L2 else goto L4 :: bool L2: - r7 = r4[2] - r8 = unbox(int, r7) - key = r8 - r9 = box(int, key) - r10 = CPyDict_GetItem(d, r9) - r11 = unbox(int, r10) + r6 = r3[2] + r7 = unbox(int, r6) + key = r7 + r8 = box(int, key) + r9 = CPyDict_GetItem(d, r8) + r10 = unbox(int, r9) L3: - r12 = CPyDict_CheckSize(d, r2) + r11 = CPyDict_CheckSize(d, r1) goto L1 L4: - r13 = CPy_NoErrOccured() + r12 = CPy_NoErrOccured() L5: - r14 = None - return r14 + r13 = None + return r13 [case testForDictContinue] from typing import Dict @@ -348,63 +309,56 @@ def sum_over_even_values(d: Dict[int, int]) -> int: [out] def sum_over_even_values(d): d :: dict - r0 :: short_int s :: int - r1, r2 :: short_int - r3 :: int - r4 :: object - r5 :: tuple[bool, int, object] - r6 :: int - r7 :: bool - r8 :: object - key, r9 :: int - r10, r11 :: object - r12 :: int - r13 :: short_int - r14 :: int - r15 :: short_int - r16 :: bool - r17, r18 :: object - r19, r20 :: int - r21, r22 :: bool + r0 :: short_int + r1 :: int + r2 :: object + r3 :: tuple[bool, int, object] + r4 :: int + r5 :: bool + r6 :: object + key, r7 :: int + r8, r9 :: object + r10 :: int + r11 :: int + r12 :: bool + r13, r14 :: object + r15, r16 :: int + r17, r18 :: bool L0: + s = 0 r0 = 0 - s = r0 - r1 = 0 - r2 = r1 - r3 = len d :: dict - r4 = CPyDict_GetKeysIter(d) + r1 = len d :: dict + r2 = CPyDict_GetKeysIter(d) L1: - r5 = CPyDict_NextKey(r4, r2) - r6 = r5[1] - r2 = r6 - r7 = r5[0] - if r7 goto L2 else goto L6 :: bool + r3 = CPyDict_NextKey(r2, r0) + r4 = r3[1] + r0 = r4 + r5 = r3[0] + if r5 goto L2 else goto L6 :: bool L2: - r8 = r5[2] - r9 = unbox(int, r8) - key = r9 - r10 = box(int, key) - r11 = CPyDict_GetItem(d, r10) - r12 = unbox(int, r11) - r13 = 4 - r14 = CPyTagged_Remainder(r12, r13) - r15 = 0 - r16 = CPyTagged_IsNe(r14, r15) - if r16 goto L3 else goto L4 :: bool + r6 = r3[2] + r7 = unbox(int, r6) + key = r7 + r8 = box(int, key) + r9 = CPyDict_GetItem(d, r8) + r10 = unbox(int, r9) + r11 = CPyTagged_Remainder(r10, 4) + r12 = CPyTagged_IsNe(r11, 0) + if r12 goto L3 else goto L4 :: bool L3: goto L5 L4: - r17 = box(int, key) - r18 = CPyDict_GetItem(d, r17) - r19 = unbox(int, r18) - r20 = CPyTagged_Add(s, r19) - s = r20 + r13 = box(int, key) + r14 = CPyDict_GetItem(d, r13) + r15 = unbox(int, r14) + r16 = CPyTagged_Add(s, r15) + s = r16 L5: - r21 = CPyDict_CheckSize(d, r3) + r17 = CPyDict_CheckSize(d, r1) goto L1 L6: - r22 = CPy_NoErrOccured() + r18 = CPy_NoErrOccured() L7: return s @@ -544,27 +498,25 @@ def multi_assign(t, a, l): a :: __main__.A l :: list z :: int - r0 :: short_int - r1 :: int - r2 :: bool - r3 :: tuple[str, object] - r4 :: str - r5 :: bool - r6 :: object - r7 :: int - r8 :: None + r0 :: int + r1 :: bool + r2 :: tuple[str, object] + r3 :: str + r4 :: bool + r5 :: object + r6 :: int + r7 :: None L0: - r0 = 0 - r1 = t[0] - a.x = r1; r2 = is_error - r3 = t[1] - r4 = r3[0] - r5 = CPyList_SetItem(l, r0, r4) - r6 = r3[1] - r7 = unbox(int, r6) - z = r7 - r8 = None - return r8 + r0 = t[0] + a.x = r0; r1 = is_error + r2 = t[1] + r3 = r2[0] + r4 = CPyList_SetItem(l, 0, r3) + r5 = r2[1] + r6 = unbox(int, r5) + z = r6 + r7 = None + return r7 [case testAssert] from typing import Optional @@ -582,19 +534,16 @@ def complex_msg(x: Optional[str], s: str) -> None: [out] def no_msg(x): x, r0 :: bool - r1 :: short_int L0: if x goto L2 else goto L1 :: bool L1: raise AssertionError unreachable L2: - r1 = 2 - return r1 + return 2 def literal_msg(x): x :: object r0, r1 :: bool - r2 :: short_int L0: r0 = bool x :: object if r0 goto L2 else goto L1 :: bool @@ -602,8 +551,7 @@ L1: raise AssertionError('message') unreachable L2: - r2 = 4 - return r2 + return 4 def complex_msg(x, s): x :: union[str, None] s :: str @@ -643,65 +591,48 @@ def delListMultiple() -> None: del l[1], l[2], l[3] [out] def delList(): - r0, r1 :: short_int - r2, r3 :: object - r4, l :: list - r5 :: short_int - r6 :: object - r7 :: bool - r8 :: None + r0, r1 :: object + r2, l :: list + r3 :: object + r4 :: bool + r5 :: None L0: - r0 = 2 - r1 = 4 - r2 = box(short_int, r0) - r3 = box(short_int, r1) - r4 = [r2, r3] - l = r4 - r5 = 2 - r6 = box(short_int, r5) - r7 = l.__delitem__(r6) :: object - r8 = None - return r8 + r0 = box(short_int, 2) + r1 = box(short_int, 4) + r2 = [r0, r1] + l = r2 + r3 = box(short_int, 2) + r4 = l.__delitem__(r3) :: object + r5 = None + return r5 def delListMultiple(): - r0, r1, r2, r3, r4, r5, r6 :: short_int - r7, r8, r9, r10, r11, r12, r13 :: object - r14, l :: list - r15, r16, r17 :: short_int - r18 :: object - r19 :: bool - r20 :: object - r21 :: bool - r22 :: object - r23 :: bool - r24 :: None + r0, r1, r2, r3, r4, r5, r6 :: object + r7, l :: list + r8 :: object + r9 :: bool + r10 :: object + r11 :: bool + r12 :: object + r13 :: bool + r14 :: None L0: - r0 = 2 - r1 = 4 - r2 = 6 - r3 = 8 - r4 = 10 - r5 = 12 - r6 = 14 - r7 = box(short_int, r0) - r8 = box(short_int, r1) - r9 = box(short_int, r2) - r10 = box(short_int, r3) - r11 = box(short_int, r4) - r12 = box(short_int, r5) - r13 = box(short_int, r6) - r14 = [r7, r8, r9, r10, r11, r12, r13] - l = r14 - r15 = 2 - r16 = 4 - r17 = 6 - r18 = box(short_int, r15) - r19 = l.__delitem__(r18) :: object - r20 = box(short_int, r16) - r21 = l.__delitem__(r20) :: object - r22 = box(short_int, r17) - r23 = l.__delitem__(r22) :: object - r24 = None - return r24 + r0 = box(short_int, 2) + r1 = box(short_int, 4) + r2 = box(short_int, 6) + r3 = box(short_int, 8) + r4 = box(short_int, 10) + r5 = box(short_int, 12) + r6 = box(short_int, 14) + r7 = [r0, r1, r2, r3, r4, r5, r6] + l = r7 + r8 = box(short_int, 2) + r9 = l.__delitem__(r8) :: object + r10 = box(short_int, 4) + r11 = l.__delitem__(r10) :: object + r12 = box(short_int, 6) + r13 = l.__delitem__(r12) :: object + r14 = None + return r14 [case testDelDict] def delDict() -> None: @@ -713,66 +644,50 @@ def delDictMultiple() -> None: [out] def delDict(): r0 :: str - r1 :: short_int - r2 :: str - r3 :: short_int - r4 :: native_int - r5, r6 :: object - r7, d :: dict - r8 :: str - r9 :: bool - r10 :: None + r1 :: str + r2, r3 :: object + r4, d :: dict + r5 :: str + r6 :: bool + r7 :: None L0: r0 = unicode_1 :: static ('one') - r1 = 2 - r2 = unicode_2 :: static ('two') - r3 = 4 - r4 = 2 - r5 = box(short_int, r1) - r6 = box(short_int, r3) - r7 = CPyDict_Build(r4, r0, r5, r2, r6) - d = r7 - r8 = unicode_1 :: static ('one') - r9 = d.__delitem__(r8) :: object - r10 = None - return r10 + r1 = unicode_2 :: static ('two') + r2 = box(short_int, 2) + r3 = box(short_int, 4) + r4 = CPyDict_Build(2, r0, r2, r1, r3) + d = r4 + r5 = unicode_1 :: static ('one') + r6 = d.__delitem__(r5) :: object + r7 = None + return r7 def delDictMultiple(): r0 :: str - r1 :: short_int + r1 :: str r2 :: str - r3 :: short_int - r4 :: str - r5 :: short_int - r6 :: str - r7 :: short_int - r8 :: native_int - r9, r10, r11, r12 :: object - r13, d :: dict - r14, r15 :: str - r16, r17 :: bool - r18 :: None + r3 :: str + r4, r5, r6, r7 :: object + r8, d :: dict + r9, r10 :: str + r11, r12 :: bool + r13 :: None L0: r0 = unicode_1 :: static ('one') - r1 = 2 - r2 = unicode_2 :: static ('two') - r3 = 4 - r4 = unicode_3 :: static ('three') - r5 = 6 - r6 = unicode_4 :: static ('four') - r7 = 8 - r8 = 4 - r9 = box(short_int, r1) - r10 = box(short_int, r3) - r11 = box(short_int, r5) - r12 = box(short_int, r7) - r13 = CPyDict_Build(r8, r0, r9, r2, r10, r4, r11, r6, r12) - d = r13 - r14 = unicode_1 :: static ('one') - r15 = unicode_4 :: static ('four') - r16 = d.__delitem__(r14) :: object - r17 = d.__delitem__(r15) :: object - r18 = None - return r18 + r1 = unicode_2 :: static ('two') + r2 = unicode_3 :: static ('three') + r3 = unicode_4 :: static ('four') + r4 = box(short_int, 2) + r5 = box(short_int, 4) + r6 = box(short_int, 6) + r7 = box(short_int, 8) + r8 = CPyDict_Build(4, r0, r4, r1, r5, r2, r6, r3, r7) + d = r8 + r9 = unicode_1 :: static ('one') + r10 = unicode_4 :: static ('four') + r11 = d.__delitem__(r9) :: object + r12 = d.__delitem__(r10) :: object + r13 = None + return r13 [case testDelAttribute] class Dummy(): @@ -797,39 +712,33 @@ L0: r2 = None return r2 def delAttribute(): - r0, r1 :: short_int - r2, dummy :: __main__.Dummy + r0, dummy :: __main__.Dummy + r1 :: str + r2 :: bool + r3 :: None +L0: + r0 = Dummy(2, 4) + dummy = r0 + r1 = unicode_3 :: static ('x') + r2 = delattr dummy, r1 + r3 = None + return r3 +def delAttributeMultiple(): + r0, dummy :: __main__.Dummy + r1 :: str + r2 :: bool r3 :: str r4 :: bool r5 :: None L0: - r0 = 2 - r1 = 4 - r2 = Dummy(r0, r1) - dummy = r2 - r3 = unicode_3 :: static ('x') + r0 = Dummy(2, 4) + dummy = r0 + r1 = unicode_3 :: static ('x') + r2 = delattr dummy, r1 + r3 = unicode_4 :: static ('y') r4 = delattr dummy, r3 r5 = None return r5 -def delAttributeMultiple(): - r0, r1 :: short_int - r2, dummy :: __main__.Dummy - r3 :: str - r4 :: bool - r5 :: str - r6 :: bool - r7 :: None -L0: - r0 = 2 - r1 = 4 - r2 = Dummy(r0, r1) - dummy = r2 - r3 = unicode_3 :: static ('x') - r4 = delattr dummy, r3 - r5 = unicode_4 :: static ('y') - r6 = delattr dummy, r5 - r7 = None - return r7 [case testForEnumerate] from typing import List, Iterable @@ -843,73 +752,67 @@ def g(x: Iterable[int]) -> None: [out] def f(a): a :: list - r0, r1 :: short_int + r0 :: short_int i :: int - r2, r3, r4 :: short_int - r5 :: bool - r6 :: object - x, r7, r8 :: int - r9, r10, r11, r12 :: short_int - r13 :: None + r1, r2 :: short_int + r3 :: bool + r4 :: object + x, r5, r6 :: int + r7, r8 :: short_int + r9 :: None L0: r0 = 0 - r1 = r0 - i = r0 - r2 = 0 - r3 = r2 + i = 0 + r1 = 0 L1: - r4 = len a :: list - r5 = r3 < r4 :: signed - if r5 goto L2 else goto L4 :: bool + r2 = len a :: list + r3 = r1 < r2 :: signed + if r3 goto L2 else goto L4 :: bool L2: - r6 = a[r3] :: unsafe list - r7 = unbox(int, r6) - x = r7 - r8 = CPyTagged_Add(i, x) + r4 = a[r1] :: unsafe list + r5 = unbox(int, r4) + x = r5 + r6 = CPyTagged_Add(i, x) L3: - r9 = 2 - r10 = r1 + r9 - r1 = r10 - i = r10 - r11 = 2 - r12 = r3 + r11 - r3 = r12 + r7 = r0 + 2 + r0 = r7 + i = r7 + r8 = r1 + 2 + r1 = r8 goto L1 L4: L5: - r13 = None - return r13 + r9 = None + return r9 def g(x): x :: object - r0, r1 :: short_int + r0 :: short_int i :: int - r2, r3 :: object - r4, n :: int - r5, r6 :: short_int - r7 :: bool - r8 :: None + r1, r2 :: object + r3, n :: int + r4 :: short_int + r5 :: bool + r6 :: None L0: r0 = 0 - r1 = r0 - i = r0 - r2 = iter x :: object + i = 0 + r1 = iter x :: object L1: - r3 = next r2 :: object - if is_error(r3) goto L4 else goto L2 + r2 = next r1 :: object + if is_error(r2) goto L4 else goto L2 L2: - r4 = unbox(int, r3) - n = r4 + r3 = unbox(int, r2) + n = r3 L3: - r5 = 2 - r6 = r1 + r5 - r1 = r6 - i = r6 + r4 = r0 + 2 + r0 = r4 + i = r4 goto L1 L4: - r7 = CPy_NoErrOccured() + r5 = CPy_NoErrOccured() L5: - r8 = None - return r8 + r6 = None + return r6 [case testForZip] from typing import List, Iterable @@ -926,102 +829,94 @@ def g(a: Iterable[bool], b: List[int]) -> None: def f(a, b): a :: list b :: object - r0, r1 :: short_int - r2 :: object - r3 :: short_int - r4 :: bool - r5, r6 :: object - x, r7 :: int - r8, y, r9 :: bool - r10, r11, r12 :: short_int - r13 :: bool - r14 :: None + r0 :: short_int + r1 :: object + r2 :: short_int + r3 :: bool + r4, r5 :: object + x, r6 :: int + r7, y, r8 :: bool + r9 :: short_int + r10 :: bool + r11 :: None L0: r0 = 0 - r1 = r0 - r2 = iter b :: object + r1 = iter b :: object L1: - r3 = len a :: list - r4 = r1 < r3 :: signed - if r4 goto L2 else goto L7 :: bool + r2 = len a :: list + r3 = r0 < r2 :: signed + if r3 goto L2 else goto L7 :: bool L2: - r5 = next r2 :: object - if is_error(r5) goto L7 else goto L3 + r4 = next r1 :: object + if is_error(r4) goto L7 else goto L3 L3: - r6 = a[r1] :: unsafe list - r7 = unbox(int, r6) - x = r7 - r8 = unbox(bool, r5) - y = r8 - r9 = bool b :: object - if r9 goto L4 else goto L5 :: bool + r5 = a[r0] :: unsafe list + r6 = unbox(int, r5) + x = r6 + r7 = unbox(bool, r4) + y = r7 + r8 = bool b :: object + if r8 goto L4 else goto L5 :: bool L4: - r10 = 2 - x = r10 + x = 2 L5: L6: - r11 = 2 - r12 = r1 + r11 - r1 = r12 + r9 = r0 + 2 + r0 = r9 goto L1 L7: - r13 = CPy_NoErrOccured() + r10 = CPy_NoErrOccured() L8: - r14 = None - return r14 + r11 = None + return r11 def g(a, b): a :: object b :: list r0 :: object - r1, r2, r3, r4, r5 :: short_int + r1, r2 :: short_int z :: int - r6 :: object - r7 :: short_int - r8, r9, r10, x :: bool - r11 :: object - y, r12 :: int + r3 :: object + r4 :: short_int + r5, r6, r7, x :: bool + r8 :: object + y, r9 :: int + r10 :: bool + r11, r12 :: short_int r13 :: bool - r14, r15, r16, r17 :: short_int - r18 :: bool - r19 :: None + r14 :: None L0: r0 = iter a :: object r1 = 0 - r2 = r1 - r3 = 0 - r4 = 10 - r5 = r3 - z = r5 + r2 = 0 + z = r2 L1: - r6 = next r0 :: object - if is_error(r6) goto L6 else goto L2 + r3 = next r0 :: object + if is_error(r3) goto L6 else goto L2 L2: - r7 = len b :: list - r8 = r2 < r7 :: signed - if r8 goto L3 else goto L6 :: bool + r4 = len b :: list + r5 = r1 < r4 :: signed + if r5 goto L3 else goto L6 :: bool L3: - r9 = r5 < r4 :: signed - if r9 goto L4 else goto L6 :: bool + r6 = r2 < 10 :: signed + if r6 goto L4 else goto L6 :: bool L4: - r10 = unbox(bool, r6) + r7 = unbox(bool, r3) + x = r7 + r8 = b[r1] :: unsafe list + r9 = unbox(int, r8) + y = r9 + r10 = False x = r10 - r11 = b[r2] :: unsafe list - r12 = unbox(int, r11) - y = r12 - r13 = False - x = r13 L5: - r14 = 2 - r15 = r2 + r14 - r2 = r15 - r16 = 2 - r17 = r5 + r16 - r5 = r17 - z = r17 + r11 = r1 + 2 + r1 = r11 + r12 = r2 + 2 + r2 = r12 + z = r12 goto L1 L6: - r18 = CPy_NoErrOccured() + r13 = CPy_NoErrOccured() L7: - r19 = None - return r19 + r14 = None + return r14 diff --git a/mypyc/test-data/irbuild-strip-asserts.test b/mypyc/test-data/irbuild-strip-asserts.test index 8ee062a8c123..1ab6b4107b4d 100644 --- a/mypyc/test-data/irbuild-strip-asserts.test +++ b/mypyc/test-data/irbuild-strip-asserts.test @@ -5,14 +5,11 @@ def g(): return x [out] def g(): - r0, r1 :: short_int - r2 :: int - r3, x :: object + r0 :: int + r1, x :: object L0: - r0 = 2 - r1 = 4 - r2 = CPyTagged_Add(r0, r1) - r3 = box(int, r2) - x = r3 + r0 = CPyTagged_Add(2, 4) + r1 = box(int, r0) + x = r1 return x diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index f4d2b4d81ff0..d641827f37de 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -22,16 +22,14 @@ def f() -> int: [out] def f(): r0 :: bool - r1 :: short_int - r2, t :: tuple[bool, int] - r3 :: int + r1, t :: tuple[bool, int] + r2 :: int L0: r0 = True - r1 = 2 - r2 = (r0, r1) - t = r2 - r3 = t[1] - return r3 + r1 = (r0, 2) + t = r1 + r2 = t[1] + return r2 [case testTupleLen] from typing import Tuple @@ -40,10 +38,8 @@ def f(x: Tuple[bool, bool, int]) -> int: [out] def f(x): x :: tuple[bool, bool, int] - r0 :: short_int L0: - r0 = 6 - return r0 + return 6 [case testSequenceTuple] from typing import List @@ -53,15 +49,13 @@ def f(x: List[bool]) -> bool: def f(x): x :: list r0 :: tuple - r1 :: short_int - r2 :: object - r3 :: bool + r1 :: object + r2 :: bool L0: r0 = PyList_AsTuple(x) - r1 = 2 - r2 = CPySequenceTuple_GetItem(r0, r1) - r3 = unbox(bool, r2) - return r3 + r1 = CPySequenceTuple_GetItem(r0, 2) + r2 = unbox(bool, r1) + return r2 [case testSequenceTupleLen] from typing import Tuple @@ -82,23 +76,18 @@ def f() -> int: return t[1] [out] def f(): - r0, r1 :: short_int - r2 :: tuple[int, int] + r0 :: tuple[int, int] t :: tuple - r3 :: object - r4 :: short_int - r5 :: object - r6 :: int + r1 :: object + r2 :: object + r3 :: int L0: - r0 = 2 - r1 = 4 - r2 = (r0, r1) - r3 = box(tuple[int, int], r2) - t = r3 - r4 = 2 - r5 = CPySequenceTuple_GetItem(t, r4) - r6 = unbox(int, r5) - return r6 + r0 = (2, 4) + r1 = box(tuple[int, int], r0) + t = r1 + r2 = CPySequenceTuple_GetItem(t, 2) + r3 = unbox(int, r2) + return r3 [case testTupleDisplay] from typing import Sequence, Tuple @@ -107,25 +96,21 @@ def f(x: Sequence[int], y: Sequence[int]) -> Tuple[int, ...]: [out] def f(x, y): x, y :: object - r0, r1, r2 :: short_int - r3, r4 :: object - r5 :: list - r6, r7, r8 :: object - r9 :: int32 - r10 :: tuple + r0, r1 :: object + r2 :: list + r3, r4, r5 :: object + r6 :: int32 + r7 :: tuple L0: - r0 = 2 - r1 = 4 - r2 = 6 - r3 = box(short_int, r0) - r4 = box(short_int, r1) - r5 = [r3, r4] - r6 = CPyList_Extend(r5, x) - r7 = CPyList_Extend(r5, y) - r8 = box(short_int, r2) - r9 = PyList_Append(r5, r8) - r10 = PyList_AsTuple(r5) - return r10 + r0 = box(short_int, 2) + r1 = box(short_int, 4) + r2 = [r0, r1] + r3 = CPyList_Extend(r2, x) + r4 = CPyList_Extend(r2, y) + r5 = box(short_int, 6) + r6 = PyList_Append(r2, r5) + r7 = PyList_AsTuple(r2) + return r7 [case testTupleFor] from typing import Tuple, List @@ -135,32 +120,30 @@ def f(xs: Tuple[str, ...]) -> None: [out] def f(xs): xs :: tuple - r0, r1 :: short_int - r2 :: int - r3 :: bool - r4 :: object - x, r5 :: str - r6, r7 :: short_int - r8 :: None + r0 :: short_int + r1 :: int + r2 :: bool + r3 :: object + x, r4 :: str + r5 :: short_int + r6 :: None L0: r0 = 0 - r1 = r0 L1: - r2 = len xs :: tuple - r3 = CPyTagged_IsLt(r1, r2) - if r3 goto L2 else goto L4 :: bool + r1 = len xs :: tuple + r2 = CPyTagged_IsLt(r0, r1) + if r2 goto L2 else goto L4 :: bool L2: - r4 = CPySequenceTuple_GetItem(xs, r1) - r5 = cast(str, r4) - x = r5 + r3 = CPySequenceTuple_GetItem(xs, r0) + r4 = cast(str, r3) + x = r4 L3: - r6 = 2 - r7 = r1 + r6 - r1 = r7 + r5 = r0 + 2 + r0 = r5 goto L1 L4: - r8 = None - return r8 + r6 = None + return r6 [case testNamedTupleAttribute] from typing import NamedTuple @@ -175,21 +158,18 @@ def f(nt: NT, b: bool) -> int: def f(nt, b): nt :: tuple b :: bool - r0 :: short_int - r1 :: object - r2 :: int - r3 :: short_int - r4 :: object - r5 :: int + r0 :: object + r1 :: int + r2 :: object + r3 :: int L0: if b goto L1 else goto L2 :: bool L1: - r0 = 0 - r1 = CPySequenceTuple_GetItem(nt, r0) - r2 = unbox(int, r1) - return r2 + r0 = CPySequenceTuple_GetItem(nt, 0) + r1 = unbox(int, r0) + return r1 L2: - r3 = 2 - r4 = CPySequenceTuple_GetItem(nt, r3) - r5 = unbox(int, r4) - return r5 + r2 = CPySequenceTuple_GetItem(nt, 2) + r3 = unbox(int, r2) + return r3 + diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index fa957b876932..0e634145ae41 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -5,10 +5,8 @@ def f() -> int: return 1 [out] def f(): - r0 :: short_int L0: - r0 = 2 - return r0 + return 2 [case testReturnLocal] def f() -> int: @@ -16,11 +14,9 @@ def f() -> int: return x [out] def f(): - r0 :: short_int x :: int L0: - r0 = 2 - x = r0 + x = 2 return x [case testLocalVars] @@ -31,11 +27,9 @@ def f() -> int: return x [out] def f(): - r0 :: short_int x, y :: int L0: - r0 = 2 - x = r0 + x = 2 y = x x = y return x @@ -48,18 +42,16 @@ def f() -> int: return y + z [out] def f(): - r0 :: short_int - x, y, z, r1 :: int + x, y, z, r0 :: int L0: - r0 = 2 - x = r0 + x = 2 inc_ref x :: int y = x z = x - r1 = CPyTagged_Add(y, z) + r0 = CPyTagged_Add(y, z) dec_ref y :: int dec_ref z :: int - return r1 + return r0 [case testFreeAtReturn] def f() -> int: @@ -70,20 +62,14 @@ def f() -> int: return y [out] def f(): - r0 :: short_int x :: int - r1 :: short_int y :: int - r2 :: short_int - r3 :: bool + r0 :: bool L0: - r0 = 2 - x = r0 - r1 = 4 - y = r1 - r2 = 2 - r3 = CPyTagged_IsEq(x, r2) - if r3 goto L3 else goto L4 :: bool + x = 2 + y = 4 + r0 = CPyTagged_IsEq(x, 2) + if r0 goto L3 else goto L4 :: bool L1: return x L2: @@ -103,15 +89,13 @@ def f(a: int, b: int) -> int: [out] def f(a, b): a, b :: int - r0 :: short_int - r1, x, r2, y :: int + r0, x, r1, y :: int L0: - r0 = 2 - r1 = CPyTagged_Add(a, r0) - x = r1 - r2 = CPyTagged_Add(x, a) + r0 = CPyTagged_Add(a, 2) + x = r0 + r1 = CPyTagged_Add(x, a) dec_ref x :: int - y = r2 + y = r1 return y [case testArgumentsInAssign] @@ -123,20 +107,18 @@ def f(a: int) -> int: [out] def f(a): a, x, y :: int - r0 :: short_int - r1 :: int + r0 :: int L0: inc_ref a :: int x = a dec_ref x :: int inc_ref a :: int y = a - r0 = 2 - x = r0 - r1 = CPyTagged_Add(x, y) + x = 2 + r0 = CPyTagged_Add(x, y) dec_ref x :: int dec_ref y :: int - return r1 + return r0 [case testAssignToArgument1] def f(a: int) -> int: @@ -146,11 +128,9 @@ def f(a: int) -> int: [out] def f(a): a :: int - r0 :: short_int y :: int L0: - r0 = 2 - a = r0 + a = 2 y = a return y @@ -163,16 +143,12 @@ def f(a: int) -> int: [out] def f(a): a :: int - r0, r1, r2 :: short_int L0: - r0 = 2 - a = r0 + a = 2 dec_ref a :: int - r1 = 4 - a = r1 + a = 4 dec_ref a :: int - r2 = 6 - a = r2 + a = 6 return a [case testAssignToArgument3] @@ -184,11 +160,9 @@ def f(a: int) -> int: [out] def f(a): a :: int - r0 :: short_int x, y :: int L0: - r0 = 2 - x = r0 + x = 2 inc_ref x :: int a = x y = x @@ -217,41 +191,34 @@ def f(a: int) -> int: def f(a): a :: int r0 :: bool - r1, r2, r3 :: native_int - r4, r5, r6 :: bool - r7, r8 :: short_int + r1 :: native_int + r2, r3, r4 :: bool x :: int - r9 :: short_int - r10, y :: int -L0: - r1 = 1 - r2 = a & r1 - r3 = 0 - r4 = r2 == r3 - if r4 goto L1 else goto L2 :: bool + r5, y :: int +L0: + r1 = a & 1 + r2 = r1 == 0 + if r2 goto L1 else goto L2 :: bool L1: - r5 = a == a - r0 = r5 + r3 = a == a + r0 = r3 goto L3 L2: - r6 = CPyTagged_IsEq_(a, a) - r0 = r6 + r4 = CPyTagged_IsEq_(a, a) + r0 = r4 L3: if r0 goto L4 else goto L5 :: bool L4: - r7 = 2 - a = r7 + a = 2 goto L6 L5: - r8 = 4 - x = r8 + x = 4 dec_ref x :: int goto L7 L6: - r9 = 2 - r10 = CPyTagged_Add(a, r9) + r5 = CPyTagged_Add(a, 2) dec_ref a :: int - y = r10 + y = r5 return y L7: inc_ref a :: int @@ -269,40 +236,33 @@ def f(a: int) -> int: def f(a): a :: int r0 :: bool - r1, r2, r3 :: native_int - r4, r5, r6 :: bool - r7 :: short_int + r1 :: native_int + r2, r3, r4 :: bool x :: int - r8, r9 :: short_int - r10, y :: int -L0: - r1 = 1 - r2 = a & r1 - r3 = 0 - r4 = r2 == r3 - if r4 goto L1 else goto L2 :: bool + r5, y :: int +L0: + r1 = a & 1 + r2 = r1 == 0 + if r2 goto L1 else goto L2 :: bool L1: - r5 = a == a - r0 = r5 + r3 = a == a + r0 = r3 goto L3 L2: - r6 = CPyTagged_IsEq_(a, a) - r0 = r6 + r4 = CPyTagged_IsEq_(a, a) + r0 = r4 L3: if r0 goto L4 else goto L5 :: bool L4: - r7 = 4 - x = r7 + x = 4 dec_ref x :: int goto L7 L5: - r8 = 2 - a = r8 + a = 2 L6: - r9 = 2 - r10 = CPyTagged_Add(a, r9) + r5 = CPyTagged_Add(a, 2) dec_ref a :: int - y = r10 + y = r5 return y L7: inc_ref a :: int @@ -317,27 +277,23 @@ def f(a: int) -> int: def f(a): a :: int r0 :: bool - r1, r2, r3 :: native_int - r4, r5, r6 :: bool - r7 :: short_int -L0: - r1 = 1 - r2 = a & r1 - r3 = 0 - r4 = r2 == r3 - if r4 goto L1 else goto L2 :: bool + r1 :: native_int + r2, r3, r4 :: bool +L0: + r1 = a & 1 + r2 = r1 == 0 + if r2 goto L1 else goto L2 :: bool L1: - r5 = a == a - r0 = r5 + r3 = a == a + r0 = r3 goto L3 L2: - r6 = CPyTagged_IsEq_(a, a) - r0 = r6 + r4 = CPyTagged_IsEq_(a, a) + r0 = r4 L3: if r0 goto L4 else goto L6 :: bool L4: - r7 = 2 - a = r7 + a = 2 L5: return a L6: @@ -354,20 +310,18 @@ def f(a: int) -> int: [out] def f(a): a :: int - r0 :: short_int - x, r1 :: int + x, r0 :: int L0: inc_ref a :: int a = a - r0 = 2 - x = r0 + x = 2 inc_ref x :: int dec_ref x :: int x = x - r1 = CPyTagged_Add(x, a) + r0 = CPyTagged_Add(x, a) dec_ref x :: int dec_ref a :: int - return r1 + return r0 [case testIncrement1] def f(a: int) -> int: @@ -378,26 +332,20 @@ def f(a: int) -> int: [out] def f(a): a :: int - r0 :: short_int - r1 :: int - r2 :: short_int + r0 :: int x :: int - r3 :: short_int - r4, r5 :: int + r1, r2 :: int L0: - r0 = 2 - r1 = CPyTagged_Add(a, r0) - a = r1 - r2 = 2 - x = r2 - r3 = 2 - r4 = CPyTagged_Add(x, r3) + r0 = CPyTagged_Add(a, 2) + a = r0 + x = 2 + r1 = CPyTagged_Add(x, 2) dec_ref x :: int - x = r4 - r5 = CPyTagged_Add(a, x) + x = r1 + r2 = CPyTagged_Add(a, x) dec_ref a :: int dec_ref x :: int - return r5 + return r2 [case testIncrement2] def f() -> None: @@ -405,21 +353,17 @@ def f() -> None: x = x + 1 [out] def f(): - r0 :: short_int x :: int - r1 :: short_int - r2 :: int - r3 :: None + r0 :: int + r1 :: None L0: - r0 = 2 - x = r0 - r1 = 2 - r2 = CPyTagged_Add(x, r1) + x = 2 + r0 = CPyTagged_Add(x, 2) dec_ref x :: int - x = r2 + x = r0 dec_ref x :: int - r3 = None - return r3 + r1 = None + return r1 [case testAdd1] def f() -> None: @@ -427,21 +371,17 @@ def f() -> None: x = y + 1 [out] def f(): - r0 :: short_int y :: int - r1 :: short_int - r2, x :: int - r3 :: None + r0, x :: int + r1 :: None L0: - r0 = 2 - y = r0 - r1 = 2 - r2 = CPyTagged_Add(y, r1) + y = 2 + r0 = CPyTagged_Add(y, 2) dec_ref y :: int - x = r2 + x = r0 dec_ref x :: int - r3 = None - return r3 + r1 = None + return r1 [case testAdd2] def f(a: int) -> int: @@ -485,21 +425,19 @@ def f(a: int) -> None: [out] def f(a): a, r0, x :: int - r1 :: short_int - y, r2, z :: int - r3 :: None + y, r1, z :: int + r2 :: None L0: r0 = CPyTagged_Add(a, a) x = r0 dec_ref x :: int - r1 = 2 - y = r1 - r2 = CPyTagged_Add(y, y) + y = 2 + r1 = CPyTagged_Add(y, y) dec_ref y :: int - z = r2 + z = r1 dec_ref z :: int - r3 = None - return r3 + r2 = None + return r2 [case testAdd5] def f(a: int) -> None: @@ -509,21 +447,19 @@ def f(a: int) -> None: [out] def f(a): a, r0 :: int - r1 :: short_int - x, r2 :: int - r3 :: None + x, r1 :: int + r2 :: None L0: r0 = CPyTagged_Add(a, a) a = r0 dec_ref a :: int - r1 = 2 - x = r1 - r2 = CPyTagged_Add(x, x) + x = 2 + r1 = CPyTagged_Add(x, x) dec_ref x :: int - x = r2 + x = r1 dec_ref x :: int - r3 = None - return r3 + r2 = None + return r2 [case testReturnInMiddleOfFunction] def f() -> int: @@ -536,50 +472,40 @@ def f() -> int: return x + y - a [out] def f(): - r0 :: short_int x :: int - r1 :: short_int y :: int - r2 :: short_int z :: int - r3 :: bool - r4, r5, r6 :: native_int - r7, r8, r9 :: bool - r10 :: short_int - a, r11, r12 :: int -L0: - r0 = 2 - x = r0 - r1 = 4 - y = r1 - r2 = 6 - z = r2 - r4 = 1 - r5 = z & r4 - r6 = 0 - r7 = r5 == r6 - if r7 goto L1 else goto L2 :: bool + r0 :: bool + r1 :: native_int + r2, r3, r4 :: bool + a, r5, r6 :: int +L0: + x = 2 + y = 4 + z = 6 + r1 = z & 1 + r2 = r1 == 0 + if r2 goto L1 else goto L2 :: bool L1: - r8 = z == z - r3 = r8 + r3 = z == z + r0 = r3 goto L3 L2: - r9 = CPyTagged_IsEq_(z, z) - r3 = r9 + r4 = CPyTagged_IsEq_(z, z) + r0 = r4 L3: - if r3 goto L6 else goto L7 :: bool + if r0 goto L6 else goto L7 :: bool L4: return z L5: - r10 = 2 - a = r10 - r11 = CPyTagged_Add(x, y) + a = 2 + r5 = CPyTagged_Add(x, y) dec_ref x :: int dec_ref y :: int - r12 = CPyTagged_Subtract(r11, a) - dec_ref r11 :: int + r6 = CPyTagged_Subtract(r5, a) + dec_ref r5 :: int dec_ref a :: int - return r12 + return r6 L6: dec_ref x :: int dec_ref y :: int @@ -599,30 +525,24 @@ def f(a: int) -> int: [out] def f(a): a :: int - r0 :: short_int sum :: int - r1 :: short_int i :: int - r2 :: bool - r3 :: int - r4 :: short_int - r5 :: int + r0 :: bool + r1 :: int + r2 :: int L0: - r0 = 0 - sum = r0 - r1 = 0 - i = r1 + sum = 0 + i = 0 L1: - r2 = CPyTagged_IsLe(i, a) - if r2 goto L2 else goto L4 :: bool + r0 = CPyTagged_IsLe(i, a) + if r0 goto L2 else goto L4 :: bool L2: - r3 = CPyTagged_Add(sum, i) + r1 = CPyTagged_Add(sum, i) dec_ref sum :: int - sum = r3 - r4 = 2 - r5 = CPyTagged_Add(i, r4) + sum = r1 + r2 = CPyTagged_Add(i, 2) dec_ref i :: int - i = r5 + i = r2 goto L1 L3: return sum @@ -636,14 +556,12 @@ def f(a: int) -> int: [out] def f(a): a :: int - r0 :: short_int - r1, r2 :: int + r0, r1 :: int L0: - r0 = 2 - r1 = CPyTagged_Add(a, r0) - r2 = f(r1) - dec_ref r1 :: int - return r2 + r0 = CPyTagged_Add(a, 2) + r1 = f(r0) + dec_ref r0 :: int + return r1 [case testError] def f(x: List[int]) -> None: pass # E: Name 'List' is not defined \ @@ -655,20 +573,15 @@ def f() -> int: return 0 [out] def f(): - r0, r1 :: short_int - r2, r3 :: object - r4, a :: list - r5 :: short_int + r0, r1 :: object + r2, a :: list L0: - r0 = 0 - r1 = 2 - r2 = box(short_int, r0) - r3 = box(short_int, r1) - r4 = [r2, r3] - a = r4 + r0 = box(short_int, 0) + r1 = box(short_int, 2) + r2 = [r0, r1] + a = r2 dec_ref a - r5 = 0 - return r5 + return 0 [case testListSet] from typing import List @@ -677,23 +590,19 @@ def f(a: List[int], b: List[int]) -> None: [out] def f(a, b): a, b :: list - r0 :: short_int - r1 :: object - r2 :: int - r3 :: short_int - r4 :: object - r5 :: bool - r6 :: None + r0 :: object + r1 :: int + r2 :: object + r3 :: bool + r4 :: None L0: - r0 = 0 - r1 = CPyList_GetItemShort(b, r0) - r2 = unbox(int, r1) - dec_ref r1 - r3 = 0 - r4 = box(int, r2) - r5 = CPyList_SetItem(a, r3, r4) - r6 = None - return r6 + r0 = CPyList_GetItemShort(b, 0) + r1 = unbox(int, r0) + dec_ref r0 + r2 = box(int, r1) + r3 = CPyList_SetItem(a, 0, r2) + r4 = None + return r4 [case testTupleRefcount] from typing import Tuple @@ -740,22 +649,20 @@ def f() -> None: def f(): r0 :: __main__.C r1, a :: list - r2 :: short_int - r3 :: object - r4, d :: __main__.C - r5 :: None + r2 :: object + r3, d :: __main__.C + r4 :: None L0: r0 = C() r1 = [r0] a = r1 - r2 = 0 - r3 = CPyList_GetItemShort(a, r2) + r2 = CPyList_GetItemShort(a, 0) dec_ref a - r4 = cast(__main__.C, r3) - d = r4 + r3 = cast(__main__.C, r2) + d = r3 dec_ref d - r5 = None - return r5 + r4 = None + return r4 [case testUnaryBranchSpecialCase] def f(x: bool) -> int: @@ -765,15 +672,12 @@ def f(x: bool) -> int: [out] def f(x): x :: bool - r0, r1 :: short_int L0: if x goto L1 else goto L2 :: bool L1: - r0 = 2 - return r0 + return 2 L2: - r1 = 4 - return r1 + return 4 [case testUnicodeLiteral] def f() -> str: @@ -792,30 +696,26 @@ def g(x: str) -> int: [out] def g(x): x :: str - r0 :: short_int - r1 :: object - r2 :: str - r3 :: tuple - r4 :: native_int + r0 :: object + r1 :: str + r2 :: tuple + r3 :: object + r4 :: dict r5 :: object - r6 :: dict - r7 :: object - r8 :: int -L0: - r0 = 4 - r1 = int - r2 = unicode_1 :: static ('base') - r3 = (x) :: tuple - r4 = 1 - r5 = box(short_int, r0) - r6 = CPyDict_Build(r4, r2, r5) - dec_ref r5 - r7 = py_call_with_kwargs(r1, r3, r6) + r6 :: int +L0: + r0 = int + r1 = unicode_1 :: static ('base') + r2 = (x) :: tuple + r3 = box(short_int, 4) + r4 = CPyDict_Build(1, r1, r3) dec_ref r3 - dec_ref r6 - r8 = unbox(int, r7) - dec_ref r7 - return r8 + r5 = py_call_with_kwargs(r0, r2, r4) + dec_ref r2 + dec_ref r4 + r6 = unbox(int, r5) + dec_ref r5 + return r6 [case testListAppend] from typing import List @@ -846,53 +746,52 @@ def f(d: Dict[int, int]) -> None: [out] def f(d): d :: dict - r0, r1 :: short_int - r2 :: int - r3 :: object - r4 :: tuple[bool, int, object] - r5 :: int - r6 :: bool - r7 :: object - key, r8 :: int - r9, r10 :: object - r11 :: int - r12, r13 :: bool - r14 :: None + r0 :: short_int + r1 :: int + r2 :: object + r3 :: tuple[bool, int, object] + r4 :: int + r5 :: bool + r6 :: object + key, r7 :: int + r8, r9 :: object + r10 :: int + r11, r12 :: bool + r13 :: None L0: r0 = 0 - r1 = r0 - r2 = len d :: dict - r3 = CPyDict_GetKeysIter(d) + r1 = len d :: dict + r2 = CPyDict_GetKeysIter(d) L1: - r4 = CPyDict_NextKey(r3, r1) - r5 = r4[1] - r1 = r5 - r6 = r4[0] - if r6 goto L2 else goto L6 :: bool + r3 = CPyDict_NextKey(r2, r0) + r4 = r3[1] + r0 = r4 + r5 = r3[0] + if r5 goto L2 else goto L6 :: bool L2: - r7 = r4[2] - dec_ref r4 - r8 = unbox(int, r7) - dec_ref r7 - key = r8 - r9 = box(int, key) - r10 = CPyDict_GetItem(d, r9) + r6 = r3[2] + dec_ref r3 + r7 = unbox(int, r6) + dec_ref r6 + key = r7 + r8 = box(int, key) + r9 = CPyDict_GetItem(d, r8) + dec_ref r8 + r10 = unbox(int, r9) dec_ref r9 - r11 = unbox(int, r10) - dec_ref r10 - dec_ref r11 :: int + dec_ref r10 :: int L3: - r12 = CPyDict_CheckSize(d, r2) + r11 = CPyDict_CheckSize(d, r1) goto L1 L4: - r13 = CPy_NoErrOccured() + r12 = CPy_NoErrOccured() L5: - r14 = None - return r14 + r13 = None + return r13 L6: - dec_ref r2 :: int + dec_ref r1 :: int + dec_ref r2 dec_ref r3 - dec_ref r4 goto L4 [case testBorrowRefs] diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index ab8bd816b923..9d4702146113 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -81,9 +81,9 @@ def test_return(self) -> None: def test_load_int(self) -> None: self.assert_emit(LoadInt(5), - "cpy_r_r0 = 10;") + "cpy_r_i0 = 10;") self.assert_emit(LoadInt(5, -1, c_int_rprimitive), - "cpy_r_r00 = 5;") + "cpy_r_i1 = 5;") def test_tuple_get(self) -> None: self.assert_emit(TupleGet(self.t, 1, 0), 'cpy_r_r0 = cpy_r_t.f1;') @@ -354,9 +354,9 @@ def test_register(self) -> None: assert_string_arrays_equal( [ 'PyObject *CPyDef_myfunc(CPyTagged cpy_r_arg) {\n', - ' CPyTagged cpy_r_r0;\n', + ' CPyTagged cpy_r_i0;\n', 'CPyL0: ;\n', - ' cpy_r_r0 = 10;\n', + ' cpy_r_i0 = 10;\n', '}\n', ], result, msg='Generated code invalid') From 32a63776ad76e468c97c592dfa7191eb6508d2a1 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Wed, 22 Jul 2020 19:29:10 +0800 Subject: [PATCH 075/138] [mypyc] Expand int ops when operation contains at least one tagged int (#9187) Follow-up of #9108 and #9127, generates the new inlined style ops when there is at least one tagged integer present (the previous two PRs actually specialized two ints and two short_ints cases). After this and the remaining merge, we will get rid of `CPyTagged_IsEq`, `CPyTagged_IsNe`, `CPyTagged_IsLt`, `CPyTagged_IsLe`, `CPyTagged_IsGt` and `CPyTagged_IsGe`. --- mypyc/irbuild/ll_builder.py | 13 +- mypyc/primitives/int_ops.py | 3 - mypyc/test-data/analysis.test | 270 ++++++++++++++++----- mypyc/test-data/irbuild-basic.test | 305 +++++++++++++++++------- mypyc/test-data/irbuild-nested.test | 64 +++-- mypyc/test-data/irbuild-optional.test | 50 ++-- mypyc/test-data/irbuild-statements.test | 201 ++++++++++++---- mypyc/test-data/irbuild-tuple.test | 45 ++-- mypyc/test-data/refcount.test | 26 +- 9 files changed, 719 insertions(+), 258 deletions(-) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 99b1b6795e27..38b18d80f7e9 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -26,7 +26,7 @@ from mypyc.ir.rtypes import ( RType, RUnion, RInstance, optional_value_type, int_rprimitive, float_rprimitive, bool_rprimitive, list_rprimitive, str_rprimitive, is_none_rprimitive, object_rprimitive, - c_pyssize_t_rprimitive, is_short_int_rprimitive + c_pyssize_t_rprimitive, is_short_int_rprimitive, is_tagged ) from mypyc.ir.func_ir import FuncDecl, FuncSignature from mypyc.ir.class_ir import ClassIR, all_concrete_classes @@ -547,11 +547,8 @@ def binary_op(self, if value is not None: return value - # generate fast binary logic ops on short ints - if (is_short_int_rprimitive(lreg.type) and is_short_int_rprimitive(rreg.type) - and expr_op in int_logical_op_mapping.keys()): - return self.binary_int_op(bool_rprimitive, lreg, rreg, - int_logical_op_mapping[expr_op][0], line) + if is_tagged(lreg.type) and is_tagged(rreg.type) and expr_op in int_logical_op_mapping: + return self.compare_tagged(lreg, rreg, expr_op, line) call_c_ops_candidates = c_binary_ops.get(expr_op, []) target = self.matching_call_c(call_c_ops_candidates, [lreg, rreg], line) @@ -573,6 +570,10 @@ def check_tagged_short_int(self, val: Value, line: int) -> Value: def compare_tagged(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: """Compare two tagged integers using given op""" + # generate fast binary logic ops on short ints + if is_short_int_rprimitive(lhs.type) and is_short_int_rprimitive(rhs.type): + return self.binary_int_op(bool_rprimitive, lhs, rhs, + int_logical_op_mapping[op][0], line) op_type, c_func_desc, negate_result, swap_op = int_logical_op_mapping[op] result = self.alloc_temp(bool_rprimitive) short_int_block, int_block, out = BasicBlock(), BasicBlock(), BasicBlock() diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index a10dd507fdfc..11b0a28c7bc4 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -114,9 +114,6 @@ def int_compare_op(name: str, c_function_name: str) -> None: int_binary_op('//=', 'CPyTagged_FloorDivide', error_kind=ERR_MAGIC) int_binary_op('%=', 'CPyTagged_Remainder', error_kind=ERR_MAGIC) -int_compare_op('==', 'CPyTagged_IsEq') -int_compare_op('!=', 'CPyTagged_IsNe') -int_compare_op('<', 'CPyTagged_IsLt') int_compare_op('<=', 'CPyTagged_IsLe') int_compare_op('>', 'CPyTagged_IsGt') int_compare_op('>=', 'CPyTagged_IsGe') diff --git a/mypyc/test-data/analysis.test b/mypyc/test-data/analysis.test index 53dadee6dfd4..c8fece89b0ba 100644 --- a/mypyc/test-data/analysis.test +++ b/mypyc/test-data/analysis.test @@ -74,24 +74,46 @@ def f(a): a :: int x :: int r0 :: bool + r1 :: native_int + r2, r3, r4 :: bool L0: x = 2 - r0 = CPyTagged_IsEq(x, 2) - if r0 goto L1 else goto L2 :: bool + r1 = x & 1 + r2 = r1 == 0 + if r2 goto L1 else goto L2 :: bool L1: - return a + r3 = x == 2 + r0 = r3 + goto L3 L2: - return x + r4 = CPyTagged_IsEq_(x, 2) + r0 = r4 L3: + if r0 goto L4 else goto L5 :: bool +L4: + return a +L5: + return x +L6: unreachable (0, 0) {a} {a, i0} (0, 1) {a, i0} {a, x} (0, 2) {a, x} {a, i1, x} -(0, 3) {a, i1, x} {a, r0, x} -(0, 4) {a, r0, x} {a, x} -(1, 0) {a} {} -(2, 0) {x} {} -(3, 0) {} {} +(0, 3) {a, i1, x} {a, i1, i2, x} +(0, 4) {a, i1, i2, x} {a, i1, r1, x} +(0, 5) {a, i1, r1, x} {a, i1, i3, r1, x} +(0, 6) {a, i1, i3, r1, x} {a, i1, r2, x} +(0, 7) {a, i1, r2, x} {a, i1, x} +(1, 0) {a, i1, x} {a, r3, x} +(1, 1) {a, r3, x} {a, r0, x} +(1, 2) {a, r0, x} {a, r0, x} +(2, 0) {a, i1, x} {a, r4, x} +(2, 1) {a, r4, x} {a, r0, x} +(2, 2) {a, r0, x} {a, r0, x} +(3, 0) {a, r0, x} {a, x} +(4, 0) {a} {} +(5, 0) {x} {} +(6, 0) {} {} [case testSpecial_Liveness] def f() -> int: @@ -149,34 +171,56 @@ def f(a: int) -> None: def f(a): a :: int r0 :: bool + r1 :: native_int + r2, r3, r4 :: bool y :: int x :: int - r1 :: None + r5 :: None L0: - r0 = CPyTagged_IsEq(a, 2) - if r0 goto L1 else goto L2 :: bool + r1 = a & 1 + r2 = r1 == 0 + if r2 goto L1 else goto L2 :: bool L1: - y = 2 - x = 4 + r3 = a == 2 + r0 = r3 goto L3 L2: - x = 4 + r4 = CPyTagged_IsEq_(a, 2) + r0 = r4 L3: - r1 = None - return r1 + if r0 goto L4 else goto L5 :: bool +L4: + y = 2 + x = 4 + goto L6 +L5: + x = 4 +L6: + r5 = None + return r5 (0, 0) {a} {a} (0, 1) {a} {a} (0, 2) {a} {a} +(0, 3) {a} {a} +(0, 4) {a} {a} +(0, 5) {a} {a} (1, 0) {a} {a} -(1, 1) {a} {a, y} -(1, 2) {a, y} {a, y} -(1, 3) {a, y} {a, x, y} -(1, 4) {a, x, y} {a, x, y} +(1, 1) {a} {a, r0} +(1, 2) {a, r0} {a, r0} (2, 0) {a} {a} -(2, 1) {a} {a, x} -(2, 2) {a, x} {a, x} -(3, 0) {a, x} {a, x} -(3, 1) {a, x} {a, x} +(2, 1) {a} {a, r0} +(2, 2) {a, r0} {a, r0} +(3, 0) {a, r0} {a, r0} +(4, 0) {a, r0} {a, r0} +(4, 1) {a, r0} {a, r0, y} +(4, 2) {a, r0, y} {a, r0, y} +(4, 3) {a, r0, y} {a, r0, x, y} +(4, 4) {a, r0, x, y} {a, r0, x, y} +(5, 0) {a, r0} {a, r0} +(5, 1) {a, r0} {a, r0, x} +(5, 2) {a, r0, x} {a, r0, x} +(6, 0) {a, r0, x} {a, r0, x} +(6, 1) {a, r0, x} {a, r0, x} [case testTwoArgs_MustDefined] def f(x: int, y: int) -> int: @@ -197,31 +241,63 @@ def f(n: int) -> None: def f(n): n :: int r0 :: bool - r1, m :: int - r2 :: None + r1 :: native_int + r2 :: bool + r3 :: native_int + r4, r5, r6, r7 :: bool + r8, m :: int + r9 :: None L0: L1: - r0 = CPyTagged_IsLt(n, 10) - if r0 goto L2 else goto L3 :: bool + r1 = n & 1 + r2 = r1 == 0 + r3 = 10 & 1 + r4 = r3 == 0 + r5 = r2 & r4 + if r5 goto L2 else goto L3 :: bool L2: - r1 = CPyTagged_Add(n, 2) - n = r1 + r6 = n < 10 :: signed + r0 = r6 + goto L4 +L3: + r7 = CPyTagged_IsLt_(n, 10) + r0 = r7 +L4: + if r0 goto L5 else goto L6 :: bool +L5: + r8 = CPyTagged_Add(n, 2) + n = r8 m = n goto L1 -L3: - r2 = None - return r2 +L6: + r9 = None + return r9 (0, 0) {n} {n} (1, 0) {n} {n} (1, 1) {n} {n} (1, 2) {n} {n} +(1, 3) {n} {n} +(1, 4) {n} {n} +(1, 5) {n} {n} +(1, 6) {n} {n} +(1, 7) {n} {n} +(1, 8) {n} {n} +(1, 9) {n} {n} +(1, 10) {n} {n} (2, 0) {n} {n} -(2, 1) {n} {n} -(2, 2) {n} {n} -(2, 3) {n} {m, n} -(2, 4) {m, n} {m, n} +(2, 1) {n} {n, r0} +(2, 2) {n, r0} {n, r0} (3, 0) {n} {n} -(3, 1) {n} {n} +(3, 1) {n} {n, r0} +(3, 2) {n, r0} {n, r0} +(4, 0) {n, r0} {n, r0} +(5, 0) {n, r0} {n, r0} +(5, 1) {n, r0} {n, r0} +(5, 2) {n, r0} {n, r0} +(5, 3) {n, r0} {m, n, r0} +(5, 4) {m, n, r0} {m, n, r0} +(6, 0) {n, r0} {n, r0} +(6, 1) {n, r0} {n, r0} [case testMultiPass_Liveness] def f(n: int) -> None: @@ -238,48 +314,112 @@ def f(n): x :: int y :: int r0 :: bool - r1 :: bool - r2 :: None + r1 :: native_int + r2 :: bool + r3 :: native_int + r4, r5, r6, r7 :: bool + r8 :: bool + r9 :: native_int + r10 :: bool + r11 :: native_int + r12, r13, r14, r15 :: bool + r16 :: None L0: x = 2 y = 2 L1: - r0 = CPyTagged_IsLt(n, 2) - if r0 goto L2 else goto L6 :: bool + r1 = n & 1 + r2 = r1 == 0 + r3 = 2 & 1 + r4 = r3 == 0 + r5 = r2 & r4 + if r5 goto L2 else goto L3 :: bool L2: - n = y + r6 = n < 2 :: signed + r0 = r6 + goto L4 L3: - r1 = CPyTagged_IsLt(n, 4) - if r1 goto L4 else goto L5 :: bool + r7 = CPyTagged_IsLt_(n, 2) + r0 = r7 L4: + if r0 goto L5 else goto L12 :: bool +L5: + n = y +L6: + r9 = n & 1 + r10 = r9 == 0 + r11 = 4 & 1 + r12 = r11 == 0 + r13 = r10 & r12 + if r13 goto L7 else goto L8 :: bool +L7: + r14 = n < 4 :: signed + r8 = r14 + goto L9 +L8: + r15 = CPyTagged_IsLt_(n, 4) + r8 = r15 +L9: + if r8 goto L10 else goto L11 :: bool +L10: n = 2 n = x - goto L3 -L5: + goto L6 +L11: goto L1 -L6: - r2 = None - return r2 +L12: + r16 = None + return r16 (0, 0) {n} {i0, n} (0, 1) {i0, n} {n, x} (0, 2) {n, x} {i1, n, x} (0, 3) {i1, n, x} {n, x, y} (0, 4) {n, x, y} {n, x, y} (1, 0) {n, x, y} {i2, n, x, y} -(1, 1) {i2, n, x, y} {r0, x, y} -(1, 2) {r0, x, y} {x, y} -(2, 0) {x, y} {n, x, y} -(2, 1) {n, x, y} {n, x, y} -(3, 0) {n, x, y} {i3, n, x, y} -(3, 1) {i3, n, x, y} {n, r1, x, y} -(3, 2) {n, r1, x, y} {n, x, y} -(4, 0) {x, y} {i4, x, y} -(4, 1) {i4, x, y} {x, y} -(4, 2) {x, y} {n, x, y} -(4, 3) {n, x, y} {n, x, y} -(5, 0) {n, x, y} {n, x, y} -(6, 0) {} {r2} -(6, 1) {r2} {} +(1, 1) {i2, n, x, y} {i2, i3, n, x, y} +(1, 2) {i2, i3, n, x, y} {i2, n, r1, x, y} +(1, 3) {i2, n, r1, x, y} {i2, i4, n, r1, x, y} +(1, 4) {i2, i4, n, r1, x, y} {i2, n, r2, x, y} +(1, 5) {i2, n, r2, x, y} {i2, i5, n, r2, x, y} +(1, 6) {i2, i5, n, r2, x, y} {i2, n, r2, r3, x, y} +(1, 7) {i2, n, r2, r3, x, y} {i2, i6, n, r2, r3, x, y} +(1, 8) {i2, i6, n, r2, r3, x, y} {i2, n, r2, r4, x, y} +(1, 9) {i2, n, r2, r4, x, y} {i2, n, r5, x, y} +(1, 10) {i2, n, r5, x, y} {i2, n, x, y} +(2, 0) {i2, n, x, y} {r6, x, y} +(2, 1) {r6, x, y} {r0, x, y} +(2, 2) {r0, x, y} {r0, x, y} +(3, 0) {i2, n, x, y} {r7, x, y} +(3, 1) {r7, x, y} {r0, x, y} +(3, 2) {r0, x, y} {r0, x, y} +(4, 0) {r0, x, y} {x, y} +(5, 0) {x, y} {n, x, y} +(5, 1) {n, x, y} {n, x, y} +(6, 0) {n, x, y} {i7, n, x, y} +(6, 1) {i7, n, x, y} {i7, i8, n, x, y} +(6, 2) {i7, i8, n, x, y} {i7, n, r9, x, y} +(6, 3) {i7, n, r9, x, y} {i7, i9, n, r9, x, y} +(6, 4) {i7, i9, n, r9, x, y} {i7, n, r10, x, y} +(6, 5) {i7, n, r10, x, y} {i10, i7, n, r10, x, y} +(6, 6) {i10, i7, n, r10, x, y} {i7, n, r10, r11, x, y} +(6, 7) {i7, n, r10, r11, x, y} {i11, i7, n, r10, r11, x, y} +(6, 8) {i11, i7, n, r10, r11, x, y} {i7, n, r10, r12, x, y} +(6, 9) {i7, n, r10, r12, x, y} {i7, n, r13, x, y} +(6, 10) {i7, n, r13, x, y} {i7, n, x, y} +(7, 0) {i7, n, x, y} {n, r14, x, y} +(7, 1) {n, r14, x, y} {n, r8, x, y} +(7, 2) {n, r8, x, y} {n, r8, x, y} +(8, 0) {i7, n, x, y} {n, r15, x, y} +(8, 1) {n, r15, x, y} {n, r8, x, y} +(8, 2) {n, r8, x, y} {n, r8, x, y} +(9, 0) {n, r8, x, y} {n, x, y} +(10, 0) {x, y} {i12, x, y} +(10, 1) {i12, x, y} {x, y} +(10, 2) {x, y} {n, x, y} +(10, 3) {n, x, y} {n, x, y} +(11, 0) {n, x, y} {n, x, y} +(12, 0) {} {r16} +(12, 1) {r16} {} [case testCall_Liveness] def f(x: int) -> int: diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 65a4b66dcabb..df072332afc4 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -506,24 +506,53 @@ def f(n: int) -> int: def f(n): n :: int r0 :: bool + r1 :: native_int + r2 :: bool + r3 :: native_int + r4, r5, r6, r7 :: bool x :: int - r1 :: bool + r8 :: bool + r9 :: native_int + r10, r11, r12 :: bool L0: - r0 = CPyTagged_IsLt(n, 0) - if r0 goto L1 else goto L2 :: bool + r1 = n & 1 + r2 = r1 == 0 + r3 = 0 & 1 + r4 = r3 == 0 + r5 = r2 & r4 + if r5 goto L1 else goto L2 :: bool L1: - x = 2 - goto L6 + r6 = n < 0 :: signed + r0 = r6 + goto L3 L2: - r1 = CPyTagged_IsEq(n, 0) - if r1 goto L3 else goto L4 :: bool + r7 = CPyTagged_IsLt_(n, 0) + r0 = r7 L3: - x = 2 - goto L5 + if r0 goto L4 else goto L5 :: bool L4: - x = 4 + x = 2 + goto L12 L5: + r9 = n & 1 + r10 = r9 == 0 + if r10 goto L6 else goto L7 :: bool L6: + r11 = n == 0 + r8 = r11 + goto L8 +L7: + r12 = CPyTagged_IsEq_(n, 0) + r8 = r12 +L8: + if r8 goto L9 else goto L10 :: bool +L9: + x = 2 + goto L11 +L10: + x = 4 +L11: +L12: return x [case testUnaryMinus] @@ -544,17 +573,29 @@ def f(n: int) -> int: def f(n): n :: int r0 :: bool - r1 :: int + r1 :: native_int + r2, r3, r4 :: bool + r5 :: int L0: - r0 = CPyTagged_IsEq(n, 0) - if r0 goto L1 else goto L2 :: bool + r1 = n & 1 + r2 = r1 == 0 + if r2 goto L1 else goto L2 :: bool L1: - r1 = 0 + r3 = n == 0 + r0 = r3 goto L3 L2: - r1 = 2 + r4 = CPyTagged_IsEq_(n, 0) + r0 = r4 L3: - return r1 + if r0 goto L4 else goto L5 :: bool +L4: + r5 = 0 + goto L6 +L5: + r5 = 2 +L6: + return r5 [case testOperatorAssignment] def f() -> int: @@ -1215,14 +1256,27 @@ L3: def num(x): x :: int r0 :: bool + r1 :: native_int + r2, r3, r4, r5 :: bool L0: - r0 = CPyTagged_IsNe(x, 0) - if r0 goto L1 else goto L2 :: bool + r1 = x & 1 + r2 = r1 == 0 + if r2 goto L1 else goto L2 :: bool L1: - return 2 + r3 = x != 0 + r0 = r3 + goto L3 L2: - return 0 + r4 = CPyTagged_IsEq_(x, 0) + r5 = !r4 + r0 = r5 L3: + if r0 goto L4 else goto L5 :: bool +L4: + return 2 +L5: + return 0 +L6: unreachable def lst(x): x :: list @@ -1268,19 +1322,32 @@ def opt_int(x): r1 :: bool r2 :: int r3 :: bool + r4 :: native_int + r5, r6, r7, r8 :: bool L0: r0 = builtins.None :: object r1 = x is not r0 - if r1 goto L1 else goto L3 :: bool + if r1 goto L1 else goto L6 :: bool L1: r2 = unbox(int, x) - r3 = CPyTagged_IsNe(r2, 0) - if r3 goto L2 else goto L3 :: bool + r4 = r2 & 1 + r5 = r4 == 0 + if r5 goto L2 else goto L3 :: bool L2: - return 2 + r6 = r2 != 0 + r3 = r6 + goto L4 L3: - return 0 + r7 = CPyTagged_IsEq_(r2, 0) + r8 = !r7 + r3 = r8 L4: + if r3 goto L5 else goto L6 :: bool +L5: + return 2 +L6: + return 0 +L7: unreachable def opt_a(x): x :: union[__main__.A, None] @@ -1825,11 +1892,15 @@ def f(): r8 :: object x, r9 :: int r10 :: bool - r11 :: bool - r12 :: int - r13 :: object - r14 :: int32 - r15 :: short_int + r11 :: native_int + r12, r13, r14, r15 :: bool + r16 :: bool + r17 :: native_int + r18, r19, r20, r21 :: bool + r22 :: int + r23 :: object + r24 :: int32 + r25 :: short_int L0: r0 = [] r1 = box(short_int, 2) @@ -1840,29 +1911,51 @@ L0: L1: r6 = len r4 :: list r7 = r5 < r6 :: signed - if r7 goto L2 else goto L8 :: bool + if r7 goto L2 else goto L14 :: bool L2: r8 = r4[r5] :: unsafe list r9 = unbox(int, r8) x = r9 - r10 = CPyTagged_IsNe(x, 4) - if r10 goto L4 else goto L3 :: bool + r11 = x & 1 + r12 = r11 == 0 + if r12 goto L3 else goto L4 :: bool L3: - goto L7 + r13 = x != 4 + r10 = r13 + goto L5 L4: - r11 = CPyTagged_IsNe(x, 6) - if r11 goto L6 else goto L5 :: bool + r14 = CPyTagged_IsEq_(x, 4) + r15 = !r14 + r10 = r15 L5: - goto L7 + if r10 goto L7 else goto L6 :: bool L6: - r12 = CPyTagged_Multiply(x, x) - r13 = box(int, r12) - r14 = PyList_Append(r0, r13) + goto L13 L7: - r15 = r5 + 2 - r5 = r15 - goto L1 + r17 = x & 1 + r18 = r17 == 0 + if r18 goto L8 else goto L9 :: bool L8: + r19 = x != 6 + r16 = r19 + goto L10 +L9: + r20 = CPyTagged_IsEq_(x, 6) + r21 = !r20 + r16 = r21 +L10: + if r16 goto L12 else goto L11 :: bool +L11: + goto L13 +L12: + r22 = CPyTagged_Multiply(x, x) + r23 = box(int, r22) + r24 = PyList_Append(r0, r23) +L13: + r25 = r5 + 2 + r5 = r25 + goto L1 +L14: return r0 [case testDictComprehension] @@ -1879,11 +1972,15 @@ def f(): r8 :: object x, r9 :: int r10 :: bool - r11 :: bool - r12 :: int - r13, r14 :: object - r15 :: int32 - r16 :: short_int + r11 :: native_int + r12, r13, r14, r15 :: bool + r16 :: bool + r17 :: native_int + r18, r19, r20, r21 :: bool + r22 :: int + r23, r24 :: object + r25 :: int32 + r26 :: short_int L0: r0 = PyDict_New() r1 = box(short_int, 2) @@ -1894,30 +1991,52 @@ L0: L1: r6 = len r4 :: list r7 = r5 < r6 :: signed - if r7 goto L2 else goto L8 :: bool + if r7 goto L2 else goto L14 :: bool L2: r8 = r4[r5] :: unsafe list r9 = unbox(int, r8) x = r9 - r10 = CPyTagged_IsNe(x, 4) - if r10 goto L4 else goto L3 :: bool + r11 = x & 1 + r12 = r11 == 0 + if r12 goto L3 else goto L4 :: bool L3: - goto L7 + r13 = x != 4 + r10 = r13 + goto L5 L4: - r11 = CPyTagged_IsNe(x, 6) - if r11 goto L6 else goto L5 :: bool + r14 = CPyTagged_IsEq_(x, 4) + r15 = !r14 + r10 = r15 L5: - goto L7 + if r10 goto L7 else goto L6 :: bool L6: - r12 = CPyTagged_Multiply(x, x) - r13 = box(int, x) - r14 = box(int, r12) - r15 = CPyDict_SetItem(r0, r13, r14) + goto L13 L7: - r16 = r5 + 2 - r5 = r16 - goto L1 + r17 = x & 1 + r18 = r17 == 0 + if r18 goto L8 else goto L9 :: bool L8: + r19 = x != 6 + r16 = r19 + goto L10 +L9: + r20 = CPyTagged_IsEq_(x, 6) + r21 = !r20 + r16 = r21 +L10: + if r16 goto L12 else goto L11 :: bool +L11: + goto L13 +L12: + r22 = CPyTagged_Multiply(x, x) + r23 = box(int, x) + r24 = box(int, r22) + r25 = CPyDict_SetItem(r0, r23, r24) +L13: + r26 = r5 + 2 + r5 = r26 + goto L1 +L14: return r0 [case testLoopsMultipleAssign] @@ -2971,61 +3090,85 @@ def call_any(l): r0, r1 :: bool r2, r3 :: object r4, i :: int - r5, r6, r7 :: bool + r5 :: bool + r6 :: native_int + r7, r8, r9, r10, r11 :: bool L0: r1 = False r0 = r1 r2 = iter l :: object L1: r3 = next r2 :: object - if is_error(r3) goto L6 else goto L2 + if is_error(r3) goto L9 else goto L2 L2: r4 = unbox(int, r3) i = r4 - r5 = CPyTagged_IsEq(i, 0) - if r5 goto L3 else goto L4 :: bool + r6 = i & 1 + r7 = r6 == 0 + if r7 goto L3 else goto L4 :: bool L3: - r6 = True - r0 = r6 - goto L8 + r8 = i == 0 + r5 = r8 + goto L5 L4: + r9 = CPyTagged_IsEq_(i, 0) + r5 = r9 L5: - goto L1 + if r5 goto L6 else goto L7 :: bool L6: - r7 = CPy_NoErrOccured() + r10 = True + r0 = r10 + goto L11 L7: L8: + goto L1 +L9: + r11 = CPy_NoErrOccured() +L10: +L11: return r0 def call_all(l): l :: object r0, r1 :: bool r2, r3 :: object r4, i :: int - r5, r6, r7, r8 :: bool + r5 :: bool + r6 :: native_int + r7, r8, r9, r10, r11, r12 :: bool L0: r1 = True r0 = r1 r2 = iter l :: object L1: r3 = next r2 :: object - if is_error(r3) goto L6 else goto L2 + if is_error(r3) goto L9 else goto L2 L2: r4 = unbox(int, r3) i = r4 - r5 = CPyTagged_IsEq(i, 0) - r6 = !r5 - if r6 goto L3 else goto L4 :: bool + r6 = i & 1 + r7 = r6 == 0 + if r7 goto L3 else goto L4 :: bool L3: - r7 = False - r0 = r7 - goto L8 + r8 = i == 0 + r5 = r8 + goto L5 L4: + r9 = CPyTagged_IsEq_(i, 0) + r5 = r9 L5: - goto L1 + r10 = !r5 + if r10 goto L6 else goto L7 :: bool L6: - r8 = CPy_NoErrOccured() + r11 = False + r0 = r11 + goto L11 L7: L8: + goto L1 +L9: + r12 = CPy_NoErrOccured() +L10: +L11: return r0 [case testSetAttr1] diff --git a/mypyc/test-data/irbuild-nested.test b/mypyc/test-data/irbuild-nested.test index 6419dc325da7..8eade905606d 100644 --- a/mypyc/test-data/irbuild-nested.test +++ b/mypyc/test-data/irbuild-nested.test @@ -711,24 +711,36 @@ def baz_f_obj.__call__(__mypyc_self__, n): r0 :: __main__.f_env r1, baz :: object r2 :: bool - r3 :: int - r4, r5 :: object - r6, r7 :: int + r3 :: native_int + r4, r5, r6 :: bool + r7 :: int + r8, r9 :: object + r10, r11 :: int L0: r0 = __mypyc_self__.__mypyc_env__ r1 = r0.baz baz = r1 - r2 = CPyTagged_IsEq(n, 0) - if r2 goto L1 else goto L2 :: bool + r3 = n & 1 + r4 = r3 == 0 + if r4 goto L1 else goto L2 :: bool L1: - return 0 + r5 = n == 0 + r2 = r5 + goto L3 L2: - r3 = CPyTagged_Subtract(n, 2) - r4 = box(int, r3) - r5 = py_call(baz, r4) - r6 = unbox(int, r5) - r7 = CPyTagged_Add(n, r6) - return r7 + r6 = CPyTagged_IsEq_(n, 0) + r2 = r6 +L3: + if r2 goto L4 else goto L5 :: bool +L4: + return 0 +L5: + r7 = CPyTagged_Subtract(n, 2) + r8 = box(int, r7) + r9 = py_call(baz, r8) + r10 = unbox(int, r9) + r11 = CPyTagged_Add(n, r10) + return r11 def f(a): a :: int r0 :: __main__.f_env @@ -853,15 +865,27 @@ def baz(n: int) -> int: def baz(n): n :: int r0 :: bool - r1, r2, r3 :: int + r1 :: native_int + r2, r3, r4 :: bool + r5, r6, r7 :: int L0: - r0 = CPyTagged_IsEq(n, 0) - if r0 goto L1 else goto L2 :: bool + r1 = n & 1 + r2 = r1 == 0 + if r2 goto L1 else goto L2 :: bool L1: - return 0 + r3 = n == 0 + r0 = r3 + goto L3 L2: - r1 = CPyTagged_Subtract(n, 2) - r2 = baz(r1) - r3 = CPyTagged_Add(n, r2) - return r3 + r4 = CPyTagged_IsEq_(n, 0) + r0 = r4 +L3: + if r0 goto L4 else goto L5 :: bool +L4: + return 0 +L5: + r5 = CPyTagged_Subtract(n, 2) + r6 = baz(r5) + r7 = CPyTagged_Add(n, r6) + return r7 diff --git a/mypyc/test-data/irbuild-optional.test b/mypyc/test-data/irbuild-optional.test index de61c60cf1d1..8e70c91ae09f 100644 --- a/mypyc/test-data/irbuild-optional.test +++ b/mypyc/test-data/irbuild-optional.test @@ -233,33 +233,45 @@ def f(y): x :: union[int, None] r1 :: object r2 :: bool - r3 :: object - r4 :: None - r5 :: object - r6, r7 :: bool - r8 :: int - r9 :: None + r3 :: native_int + r4, r5, r6 :: bool + r7 :: object + r8 :: None + r9 :: object + r10, r11 :: bool + r12 :: int + r13 :: None L0: r0 = None r1 = box(None, r0) x = r1 - r2 = CPyTagged_IsEq(y, 2) - if r2 goto L1 else goto L2 :: bool + r3 = y & 1 + r4 = r3 == 0 + if r4 goto L1 else goto L2 :: bool L1: - r3 = box(int, y) - x = r3 + r5 = y == 2 + r2 = r5 + goto L3 L2: - r4 = None - r5 = box(None, r4) - r6 = x is r5 - r7 = !r6 - if r7 goto L3 else goto L4 :: bool + r6 = CPyTagged_IsEq_(y, 2) + r2 = r6 L3: - r8 = unbox(int, x) - y = r8 + if r2 goto L4 else goto L5 :: bool L4: - r9 = None - return r9 + r7 = box(int, y) + x = r7 +L5: + r8 = None + r9 = box(None, r8) + r10 = x is r9 + r11 = !r10 + if r11 goto L6 else goto L7 :: bool +L6: + r12 = unbox(int, x) + y = r12 +L7: + r13 = None + return r13 [case testUnionType] from typing import Union diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index ec681d0f1376..fd6f63c15e1e 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -67,16 +67,33 @@ def f() -> None: def f(): n :: int r0 :: bool - r1 :: None + r1 :: native_int + r2 :: bool + r3 :: native_int + r4, r5, r6, r7 :: bool + r8 :: None L0: n = 0 L1: - r0 = CPyTagged_IsLt(n, 10) - if r0 goto L2 else goto L3 :: bool + r1 = n & 1 + r2 = r1 == 0 + r3 = 10 & 1 + r4 = r3 == 0 + r5 = r2 & r4 + if r5 goto L2 else goto L3 :: bool L2: + r6 = n < 10 :: signed + r0 = r6 + goto L4 L3: - r1 = None - return r1 + r7 = CPyTagged_IsLt_(n, 10) + r0 = r7 +L4: + if r0 goto L5 else goto L6 :: bool +L5: +L6: + r8 = None + return r8 [case testBreakFor] def f() -> None: @@ -117,22 +134,56 @@ def f() -> None: def f(): n :: int r0 :: bool - r1 :: bool - r2 :: None + r1 :: native_int + r2 :: bool + r3 :: native_int + r4, r5, r6, r7 :: bool + r8 :: bool + r9 :: native_int + r10 :: bool + r11 :: native_int + r12, r13, r14, r15 :: bool + r16 :: None L0: n = 0 L1: - r0 = CPyTagged_IsLt(n, 10) - if r0 goto L2 else goto L6 :: bool + r1 = n & 1 + r2 = r1 == 0 + r3 = 10 & 1 + r4 = r3 == 0 + r5 = r2 & r4 + if r5 goto L2 else goto L3 :: bool L2: + r6 = n < 10 :: signed + r0 = r6 + goto L4 L3: - r1 = CPyTagged_IsLt(n, 8) - if r1 goto L4 else goto L5 :: bool + r7 = CPyTagged_IsLt_(n, 10) + r0 = r7 L4: + if r0 goto L5 else goto L12 :: bool L5: L6: - r2 = None - return r2 + r9 = n & 1 + r10 = r9 == 0 + r11 = 8 & 1 + r12 = r11 == 0 + r13 = r10 & r12 + if r13 goto L7 else goto L8 :: bool +L7: + r14 = n < 8 :: signed + r8 = r14 + goto L9 +L8: + r15 = CPyTagged_IsLt_(n, 8) + r8 = r15 +L9: + if r8 goto L10 else goto L11 :: bool +L10: +L11: +L12: + r16 = None + return r16 [case testContinue] def f() -> None: @@ -143,17 +194,34 @@ def f() -> None: def f(): n :: int r0 :: bool - r1 :: None + r1 :: native_int + r2 :: bool + r3 :: native_int + r4, r5, r6, r7 :: bool + r8 :: None L0: n = 0 L1: - r0 = CPyTagged_IsLt(n, 10) - if r0 goto L2 else goto L3 :: bool + r1 = n & 1 + r2 = r1 == 0 + r3 = 10 & 1 + r4 = r3 == 0 + r5 = r2 & r4 + if r5 goto L2 else goto L3 :: bool L2: - goto L1 + r6 = n < 10 :: signed + r0 = r6 + goto L4 L3: - r1 = None - return r1 + r7 = CPyTagged_IsLt_(n, 10) + r0 = r7 +L4: + if r0 goto L5 else goto L6 :: bool +L5: + goto L1 +L6: + r8 = None + return r8 [case testContinueFor] def f() -> None: @@ -193,24 +261,58 @@ def f() -> None: def f(): n :: int r0 :: bool - r1 :: bool - r2 :: None + r1 :: native_int + r2 :: bool + r3 :: native_int + r4, r5, r6, r7 :: bool + r8 :: bool + r9 :: native_int + r10 :: bool + r11 :: native_int + r12, r13, r14, r15 :: bool + r16 :: None L0: n = 0 L1: - r0 = CPyTagged_IsLt(n, 10) - if r0 goto L2 else goto L6 :: bool + r1 = n & 1 + r2 = r1 == 0 + r3 = 10 & 1 + r4 = r3 == 0 + r5 = r2 & r4 + if r5 goto L2 else goto L3 :: bool L2: + r6 = n < 10 :: signed + r0 = r6 + goto L4 L3: - r1 = CPyTagged_IsLt(n, 8) - if r1 goto L4 else goto L5 :: bool + r7 = CPyTagged_IsLt_(n, 10) + r0 = r7 L4: - goto L3 + if r0 goto L5 else goto L12 :: bool L5: - goto L1 L6: - r2 = None - return r2 + r9 = n & 1 + r10 = r9 == 0 + r11 = 8 & 1 + r12 = r11 == 0 + r13 = r10 & r12 + if r13 goto L7 else goto L8 :: bool +L7: + r14 = n < 8 :: signed + r8 = r14 + goto L9 +L8: + r15 = CPyTagged_IsLt_(n, 8) + r8 = r15 +L9: + if r8 goto L10 else goto L11 :: bool +L10: + goto L6 +L11: + goto L1 +L12: + r16 = None + return r16 [case testForList] from typing import List @@ -322,9 +424,11 @@ def sum_over_even_values(d): r10 :: int r11 :: int r12 :: bool - r13, r14 :: object - r15, r16 :: int - r17, r18 :: bool + r13 :: native_int + r14, r15, r16, r17 :: bool + r18, r19 :: object + r20, r21 :: int + r22, r23 :: bool L0: s = 0 r0 = 0 @@ -335,7 +439,7 @@ L1: r4 = r3[1] r0 = r4 r5 = r3[0] - if r5 goto L2 else goto L6 :: bool + if r5 goto L2 else goto L9 :: bool L2: r6 = r3[2] r7 = unbox(int, r6) @@ -344,22 +448,33 @@ L2: r9 = CPyDict_GetItem(d, r8) r10 = unbox(int, r9) r11 = CPyTagged_Remainder(r10, 4) - r12 = CPyTagged_IsNe(r11, 0) - if r12 goto L3 else goto L4 :: bool + r13 = r11 & 1 + r14 = r13 == 0 + if r14 goto L3 else goto L4 :: bool L3: + r15 = r11 != 0 + r12 = r15 goto L5 L4: - r13 = box(int, key) - r14 = CPyDict_GetItem(d, r13) - r15 = unbox(int, r14) - r16 = CPyTagged_Add(s, r15) - s = r16 + r16 = CPyTagged_IsEq_(r11, 0) + r17 = !r16 + r12 = r17 L5: - r17 = CPyDict_CheckSize(d, r1) - goto L1 + if r12 goto L6 else goto L7 :: bool L6: - r18 = CPy_NoErrOccured() + goto L8 L7: + r18 = box(int, key) + r19 = CPyDict_GetItem(d, r18) + r20 = unbox(int, r19) + r21 = CPyTagged_Add(s, r20) + s = r21 +L8: + r22 = CPyDict_CheckSize(d, r1) + goto L1 +L9: + r23 = CPy_NoErrOccured() +L10: return s [case testMultipleAssignment] diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index d641827f37de..1c626955e33d 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -123,27 +123,44 @@ def f(xs): r0 :: short_int r1 :: int r2 :: bool - r3 :: object - x, r4 :: str - r5 :: short_int - r6 :: None + r3 :: native_int + r4 :: bool + r5 :: native_int + r6, r7, r8, r9 :: bool + r10 :: object + x, r11 :: str + r12 :: short_int + r13 :: None L0: r0 = 0 L1: r1 = len xs :: tuple - r2 = CPyTagged_IsLt(r0, r1) - if r2 goto L2 else goto L4 :: bool + r3 = r0 & 1 + r4 = r3 == 0 + r5 = r1 & 1 + r6 = r5 == 0 + r7 = r4 & r6 + if r7 goto L2 else goto L3 :: bool L2: - r3 = CPySequenceTuple_GetItem(xs, r0) - r4 = cast(str, r3) - x = r4 + r8 = r0 < r1 :: signed + r2 = r8 + goto L4 L3: - r5 = r0 + 2 - r0 = r5 - goto L1 + r9 = CPyTagged_IsLt_(r0, r1) + r2 = r9 L4: - r6 = None - return r6 + if r2 goto L5 else goto L7 :: bool +L5: + r10 = CPySequenceTuple_GetItem(xs, r0) + r11 = cast(str, r10) + x = r11 +L6: + r12 = r0 + 2 + r0 = r12 + goto L1 +L7: + r13 = None + return r13 [case testNamedTupleAttribute] from typing import NamedTuple diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index 0e634145ae41..b88becbb7b1f 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -65,21 +65,33 @@ def f(): x :: int y :: int r0 :: bool + r1 :: native_int + r2, r3, r4 :: bool L0: x = 2 y = 4 - r0 = CPyTagged_IsEq(x, 2) - if r0 goto L3 else goto L4 :: bool + r1 = x & 1 + r2 = r1 == 0 + if r2 goto L1 else goto L2 :: bool L1: - return x + r3 = x == 2 + r0 = r3 + goto L3 L2: - return y + r4 = CPyTagged_IsEq_(x, 2) + r0 = r4 L3: - dec_ref y :: int - goto L1 + if r0 goto L6 else goto L7 :: bool L4: + return x +L5: + return y +L6: + dec_ref y :: int + goto L4 +L7: dec_ref x :: int - goto L2 + goto L5 [case testArgumentsInOps] def f(a: int, b: int) -> int: From d8d82e20d096176fdbee3a33b01a9985bdaecb1c Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Wed, 22 Jul 2020 21:02:17 +0800 Subject: [PATCH 076/138] [mypyc] Complete support of int comparison ops (#9189) Related to mypyc/mypyc#741, merges all remaining integer comparison ops. --- mypyc/primitives/int_ops.py | 9 +- mypyc/test-data/analysis.test | 58 ++++-- mypyc/test-data/irbuild-basic.test | 231 +++++++++++++++++++----- mypyc/test-data/irbuild-statements.test | 2 +- mypyc/test-data/refcount.test | 40 ++-- 5 files changed, 264 insertions(+), 76 deletions(-) diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index 11b0a28c7bc4..b2e6960c378a 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -114,10 +114,6 @@ def int_compare_op(name: str, c_function_name: str) -> None: int_binary_op('//=', 'CPyTagged_FloorDivide', error_kind=ERR_MAGIC) int_binary_op('%=', 'CPyTagged_Remainder', error_kind=ERR_MAGIC) -int_compare_op('<=', 'CPyTagged_IsLe') -int_compare_op('>', 'CPyTagged_IsGt') -int_compare_op('>=', 'CPyTagged_IsGe') - # Add short integers and assume that it doesn't overflow or underflow. # Assume that the operands are not big integers. unsafe_short_add = custom_op( @@ -170,5 +166,8 @@ def int_unary_op(name: str, c_function_name: str) -> CFunctionDescription: int_logical_op_mapping = { '==': IntLogicalOpDescrption(BinaryIntOp.EQ, int_equal_, False, False), '!=': IntLogicalOpDescrption(BinaryIntOp.NEQ, int_equal_, True, False), - '<': IntLogicalOpDescrption(BinaryIntOp.SLT, int_less_than_, False, False) + '<': IntLogicalOpDescrption(BinaryIntOp.SLT, int_less_than_, False, False), + '<=': IntLogicalOpDescrption(BinaryIntOp.SLE, int_less_than_, True, True), + '>': IntLogicalOpDescrption(BinaryIntOp.SGT, int_less_than_, False, True), + '>=': IntLogicalOpDescrption(BinaryIntOp.SGE, int_less_than_, True, False), } # type: Dict[str, IntLogicalOpDescrption] diff --git a/mypyc/test-data/analysis.test b/mypyc/test-data/analysis.test index c8fece89b0ba..c800c43bed5e 100644 --- a/mypyc/test-data/analysis.test +++ b/mypyc/test-data/analysis.test @@ -658,21 +658,39 @@ def f(a): sum :: int i :: int r0 :: bool - r1 :: int - r2 :: int + r1 :: native_int + r2 :: bool + r3 :: native_int + r4, r5, r6, r7, r8 :: bool + r9 :: int + r10 :: int L0: sum = 0 i = 0 L1: - r0 = CPyTagged_IsLe(i, a) - if r0 goto L2 else goto L3 :: bool + r1 = i & 1 + r2 = r1 == 0 + r3 = a & 1 + r4 = r3 == 0 + r5 = r2 & r4 + if r5 goto L2 else goto L3 :: bool L2: - r1 = CPyTagged_Add(sum, i) - sum = r1 - r2 = CPyTagged_Add(i, 2) - i = r2 - goto L1 + r6 = i <= a :: signed + r0 = r6 + goto L4 L3: + r7 = CPyTagged_IsLt_(a, i) + r8 = !r7 + r0 = r8 +L4: + if r0 goto L5 else goto L6 :: bool +L5: + r9 = CPyTagged_Add(sum, i) + sum = r9 + r10 = CPyTagged_Add(i, 2) + i = r10 + goto L1 +L6: return sum (0, 0) {a} {a} (0, 1) {a} {a} @@ -681,13 +699,29 @@ L3: (0, 4) {a} {a} (1, 0) {a} {a} (1, 1) {a} {a} +(1, 2) {a} {a} +(1, 3) {a} {a} +(1, 4) {a} {a} +(1, 5) {a} {a} +(1, 6) {a} {a} +(1, 7) {a} {a} +(1, 8) {a} {a} +(1, 9) {a} {a} (2, 0) {a} {a} (2, 1) {a} {a} (2, 2) {a} {a} -(2, 3) {a} {a} -(2, 4) {a} {a} -(2, 5) {a} {a} (3, 0) {a} {a} +(3, 1) {a} {a} +(3, 2) {a} {a} +(3, 3) {a} {a} +(4, 0) {a} {a} +(5, 0) {a} {a} +(5, 1) {a} {a} +(5, 2) {a} {a} +(5, 3) {a} {a} +(5, 4) {a} {a} +(5, 5) {a} {a} +(6, 0) {a} {a} [case testError] def f(x: List[int]) -> None: pass # E: Name 'List' is not defined \ diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index df072332afc4..307e9c85b858 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -164,6 +164,10 @@ def f(x, y): r2 :: bool r3 :: native_int r4, r5, r6, r7, r8 :: bool + r9 :: native_int + r10 :: bool + r11 :: native_int + r12, r13, r14, r15 :: bool L0: r1 = x & 1 r2 = r1 == 0 @@ -179,16 +183,29 @@ L2: r7 = CPyTagged_IsLt_(x, y) r0 = r7 L3: - if r0 goto L4 else goto L6 :: bool + if r0 goto L4 else goto L9 :: bool L4: - r8 = CPyTagged_IsGt(x, y) - if r8 goto L5 else goto L6 :: bool + r9 = x & 1 + r10 = r9 == 0 + r11 = y & 1 + r12 = r11 == 0 + r13 = r10 & r12 + if r13 goto L5 else goto L6 :: bool L5: - x = 2 + r14 = x > y :: signed + r8 = r14 goto L7 L6: - x = 4 + r15 = CPyTagged_IsLt_(y, x) + r8 = r15 L7: + if r8 goto L8 else goto L9 :: bool +L8: + x = 2 + goto L10 +L9: + x = 4 +L10: return x [case testAnd2] @@ -228,6 +245,10 @@ def f(x, y): r2 :: bool r3 :: native_int r4, r5, r6, r7, r8 :: bool + r9 :: native_int + r10 :: bool + r11 :: native_int + r12, r13, r14, r15 :: bool L0: r1 = x & 1 r2 = r1 == 0 @@ -243,16 +264,29 @@ L2: r7 = CPyTagged_IsLt_(x, y) r0 = r7 L3: - if r0 goto L5 else goto L4 :: bool + if r0 goto L8 else goto L4 :: bool L4: - r8 = CPyTagged_IsGt(x, y) - if r8 goto L5 else goto L6 :: bool + r9 = x & 1 + r10 = r9 == 0 + r11 = y & 1 + r12 = r11 == 0 + r13 = r10 & r12 + if r13 goto L5 else goto L6 :: bool L5: - x = 2 + r14 = x > y :: signed + r8 = r14 goto L7 L6: - x = 4 + r15 = CPyTagged_IsLt_(y, x) + r8 = r15 L7: + if r8 goto L8 else goto L9 :: bool +L8: + x = 2 + goto L10 +L9: + x = 4 +L10: return x [case testOr2] @@ -324,6 +358,10 @@ def f(x, y): r2 :: bool r3 :: native_int r4, r5, r6, r7, r8 :: bool + r9 :: native_int + r10 :: bool + r11 :: native_int + r12, r13, r14, r15 :: bool L0: r1 = x & 1 r2 = r1 == 0 @@ -339,13 +377,26 @@ L2: r7 = CPyTagged_IsLt_(x, y) r0 = r7 L3: - if r0 goto L4 else goto L5 :: bool + if r0 goto L4 else goto L8 :: bool L4: - r8 = CPyTagged_IsGt(x, y) - if r8 goto L6 else goto L5 :: bool + r9 = x & 1 + r10 = r9 == 0 + r11 = y & 1 + r12 = r11 == 0 + r13 = r10 & r12 + if r13 goto L5 else goto L6 :: bool L5: - x = 2 + r14 = x > y :: signed + r8 = r14 + goto L7 L6: + r15 = CPyTagged_IsLt_(y, x) + r8 = r15 +L7: + if r8 goto L9 else goto L8 :: bool +L8: + x = 2 +L9: return x [case testWhile] @@ -357,16 +408,33 @@ def f(x: int, y: int) -> int: def f(x, y): x, y :: int r0 :: bool - r1 :: int + r1 :: native_int + r2 :: bool + r3 :: native_int + r4, r5, r6, r7 :: bool + r8 :: int L0: L1: - r0 = CPyTagged_IsGt(x, y) - if r0 goto L2 else goto L3 :: bool + r1 = x & 1 + r2 = r1 == 0 + r3 = y & 1 + r4 = r3 == 0 + r5 = r2 & r4 + if r5 goto L2 else goto L3 :: bool L2: - r1 = CPyTagged_Subtract(x, y) - x = r1 - goto L1 + r6 = x > y :: signed + r0 = r6 + goto L4 L3: + r7 = CPyTagged_IsLt_(y, x) + r0 = r7 +L4: + if r0 goto L5 else goto L6 :: bool +L5: + r8 = CPyTagged_Subtract(x, y) + x = r8 + goto L1 +L6: return x [case testWhile2] @@ -379,17 +447,34 @@ def f(x: int, y: int) -> int: def f(x, y): x, y :: int r0 :: bool - r1 :: int + r1 :: native_int + r2 :: bool + r3 :: native_int + r4, r5, r6, r7 :: bool + r8 :: int L0: x = 2 L1: - r0 = CPyTagged_IsGt(x, y) - if r0 goto L2 else goto L3 :: bool + r1 = x & 1 + r2 = r1 == 0 + r3 = y & 1 + r4 = r3 == 0 + r5 = r2 & r4 + if r5 goto L2 else goto L3 :: bool L2: - r1 = CPyTagged_Subtract(x, y) - x = r1 - goto L1 + r6 = x > y :: signed + r0 = r6 + goto L4 L3: + r7 = CPyTagged_IsLt_(y, x) + r0 = r7 +L4: + if r0 goto L5 else goto L6 :: bool +L5: + r8 = CPyTagged_Subtract(x, y) + x = r8 + goto L1 +L6: return x [case testImplicitNoneReturn] @@ -464,21 +549,39 @@ def f(n: int) -> int: def f(n): n :: int r0 :: bool - r1, r2 :: int - r3, r4, r5 :: int + r1 :: native_int + r2 :: bool + r3 :: native_int + r4, r5, r6, r7, r8 :: bool + r9, r10 :: int + r11, r12, r13 :: int L0: - r0 = CPyTagged_IsLe(n, 2) - if r0 goto L1 else goto L2 :: bool + r1 = n & 1 + r2 = r1 == 0 + r3 = 2 & 1 + r4 = r3 == 0 + r5 = r2 & r4 + if r5 goto L1 else goto L2 :: bool L1: - return 2 + r6 = n <= 2 :: signed + r0 = r6 + goto L3 L2: - r1 = CPyTagged_Subtract(n, 2) - r2 = f(r1) - r3 = CPyTagged_Subtract(n, 4) - r4 = f(r3) - r5 = CPyTagged_Add(r2, r4) - return r5 + r7 = CPyTagged_IsLt_(2, n) + r8 = !r7 + r0 = r8 L3: + if r0 goto L4 else goto L5 :: bool +L4: + return 2 +L5: + r9 = CPyTagged_Subtract(n, 2) + r10 = f(r9) + r11 = CPyTagged_Subtract(n, 4) + r12 = f(r11) + r13 = CPyTagged_Add(r10, r12) + return r13 +L6: unreachable [case testReportTypeCheckError] @@ -1101,18 +1204,35 @@ def call_callable_type() -> float: def absolute_value(x): x :: int r0 :: bool - r1, r2 :: int + r1 :: native_int + r2 :: bool + r3 :: native_int + r4, r5, r6, r7 :: bool + r8, r9 :: int L0: - r0 = CPyTagged_IsGt(x, 0) - if r0 goto L1 else goto L2 :: bool + r1 = x & 1 + r2 = r1 == 0 + r3 = 0 & 1 + r4 = r3 == 0 + r5 = r2 & r4 + if r5 goto L1 else goto L2 :: bool L1: - r1 = x + r6 = x > 0 :: signed + r0 = r6 goto L3 L2: - r2 = CPyTagged_Negate(x) - r1 = r2 + r7 = CPyTagged_IsLt_(0, x) + r0 = r7 L3: - return r1 + if r0 goto L4 else goto L5 :: bool +L4: + r8 = x + goto L6 +L5: + r9 = CPyTagged_Negate(x) + r8 = r9 +L6: + return r8 def call_native_function(x): x, r0 :: int L0: @@ -2622,6 +2742,10 @@ def f(x, y, z): r7, r8, r9, r10 :: bool r11 :: int r12 :: bool + r13 :: native_int + r14 :: bool + r15 :: native_int + r16, r17, r18, r19 :: bool L0: r0 = g(x) r1 = g(y) @@ -2642,12 +2766,25 @@ L3: if r3 goto L5 else goto L4 :: bool L4: r2 = r3 - goto L6 + goto L9 L5: r11 = g(z) - r12 = CPyTagged_IsGt(r1, r11) - r2 = r12 + r13 = r1 & 1 + r14 = r13 == 0 + r15 = r11 & 1 + r16 = r15 == 0 + r17 = r14 & r16 + if r17 goto L6 else goto L7 :: bool L6: + r18 = r1 > r11 :: signed + r12 = r18 + goto L8 +L7: + r19 = CPyTagged_IsLt_(r11, r1) + r12 = r19 +L8: + r2 = r12 +L9: return r2 [case testEq] diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index fd6f63c15e1e..cdac45f0557b 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -46,7 +46,7 @@ L0: r0 = 20 i = r0 L1: - r1 = CPyTagged_IsGt(r0, 0) + r1 = r0 > 0 :: signed if r1 goto L2 else goto L4 :: bool L2: L3: diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index b88becbb7b1f..d3f79322c8b2 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -540,27 +540,45 @@ def f(a): sum :: int i :: int r0 :: bool - r1 :: int - r2 :: int + r1 :: native_int + r2 :: bool + r3 :: native_int + r4, r5, r6, r7, r8 :: bool + r9 :: int + r10 :: int L0: sum = 0 i = 0 L1: - r0 = CPyTagged_IsLe(i, a) - if r0 goto L2 else goto L4 :: bool + r1 = i & 1 + r2 = r1 == 0 + r3 = a & 1 + r4 = r3 == 0 + r5 = r2 & r4 + if r5 goto L2 else goto L3 :: bool L2: - r1 = CPyTagged_Add(sum, i) + r6 = i <= a :: signed + r0 = r6 + goto L4 +L3: + r7 = CPyTagged_IsLt_(a, i) + r8 = !r7 + r0 = r8 +L4: + if r0 goto L5 else goto L7 :: bool +L5: + r9 = CPyTagged_Add(sum, i) dec_ref sum :: int - sum = r1 - r2 = CPyTagged_Add(i, 2) + sum = r9 + r10 = CPyTagged_Add(i, 2) dec_ref i :: int - i = r2 + i = r10 goto L1 -L3: +L6: return sum -L4: +L7: dec_ref i :: int - goto L3 + goto L6 [case testCall] def f(a: int) -> int: From 03815d6e3ed82af547e51440d3d846af234b7ded Mon Sep 17 00:00:00 2001 From: LiuYuhui Date: Wed, 22 Jul 2020 21:46:09 +0800 Subject: [PATCH 077/138] Add keyword arguments for Functional Enum API (#9123) Fix #9079. --- mypy/semanal_enum.py | 36 +++++++++++++++++++++++----------- test-data/unit/check-enum.test | 16 ++++++++++++--- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/mypy/semanal_enum.py b/mypy/semanal_enum.py index 8e62b0c247a9..eabd2bcdadea 100644 --- a/mypy/semanal_enum.py +++ b/mypy/semanal_enum.py @@ -8,7 +8,7 @@ from mypy.nodes import ( Expression, Context, TypeInfo, AssignmentStmt, NameExpr, CallExpr, RefExpr, StrExpr, UnicodeExpr, TupleExpr, ListExpr, DictExpr, Var, SymbolTableNode, MDEF, ARG_POS, - EnumCallExpr, MemberExpr + ARG_NAMED, EnumCallExpr, MemberExpr ) from mypy.semanal_shared import SemanticAnalyzerInterface from mypy.options import Options @@ -104,23 +104,37 @@ def parse_enum_call_args(self, call: CallExpr, Return a tuple of fields, values, was there an error. """ args = call.args + if not all([arg_kind in [ARG_POS, ARG_NAMED] for arg_kind in call.arg_kinds]): + return self.fail_enum_call_arg("Unexpected arguments to %s()" % class_name, call) if len(args) < 2: return self.fail_enum_call_arg("Too few arguments for %s()" % class_name, call) - if len(args) > 2: + if len(args) > 6: return self.fail_enum_call_arg("Too many arguments for %s()" % class_name, call) - if call.arg_kinds != [ARG_POS, ARG_POS]: - return self.fail_enum_call_arg("Unexpected arguments to %s()" % class_name, call) - if not isinstance(args[0], (StrExpr, UnicodeExpr)): + valid_name = [None, 'value', 'names', 'module', 'qualname', 'type', 'start'] + for arg_name in call.arg_names: + if arg_name not in valid_name: + self.fail_enum_call_arg("Unexpected keyword argument '{}'".format(arg_name), call) + value, names = None, None + for arg_name, arg in zip(call.arg_names, args): + if arg_name == 'value': + value = arg + if arg_name == 'names': + names = arg + if value is None: + value = args[0] + if names is None: + names = args[1] + if not isinstance(value, (StrExpr, UnicodeExpr)): return self.fail_enum_call_arg( "%s() expects a string literal as the first argument" % class_name, call) items = [] values = [] # type: List[Optional[Expression]] - if isinstance(args[1], (StrExpr, UnicodeExpr)): - fields = args[1].value + if isinstance(names, (StrExpr, UnicodeExpr)): + fields = names.value for field in fields.replace(',', ' ').split(): items.append(field) - elif isinstance(args[1], (TupleExpr, ListExpr)): - seq_items = args[1].items + elif isinstance(names, (TupleExpr, ListExpr)): + seq_items = names.items if all(isinstance(seq_item, (StrExpr, UnicodeExpr)) for seq_item in seq_items): items = [cast(Union[StrExpr, UnicodeExpr], seq_item).value for seq_item in seq_items] @@ -139,8 +153,8 @@ def parse_enum_call_args(self, call: CallExpr, "%s() with tuple or list expects strings or (name, value) pairs" % class_name, call) - elif isinstance(args[1], DictExpr): - for key, value in args[1].items: + elif isinstance(names, DictExpr): + for key, value in names.items: if not isinstance(key, (StrExpr, UnicodeExpr)): return self.fail_enum_call_arg( "%s() with dict literal requires string literals" % class_name, call) diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index cf9bb55a946c..e66fdfe277a1 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -316,17 +316,27 @@ main:5: note: Revealed type is 'Literal[__main__.F.baz]?' main:6: note: Revealed type is 'Literal[1]?' main:7: note: Revealed type is 'Literal['bar']?' + +[case testEnumKeywordsArgs] +from enum import Enum, IntEnum + +PictureSize = Enum('PictureSize', 'P0 P1 P2 P3 P4 P5 P6 P7 P8', type=str, module=__name__) +fake_enum1 = Enum('fake_enum1', ['a', 'b']) +fake_enum2 = Enum('fake_enum1', names=['a', 'b']) +fake_enum3 = Enum(value='fake_enum1', names=['a', 'b']) +fake_enum4 = Enum(value='fake_enum1', names=['a', 'b'] , module=__name__) + [case testFunctionalEnumErrors] from enum import Enum, IntEnum A = Enum('A') B = Enum('B', 42) -C = Enum('C', 'a b', 'x') +C = Enum('C', 'a b', 'x', 'y', 'z', 'p', 'q') D = Enum('D', foo) bar = 'x y z' E = Enum('E', bar) I = IntEnum('I') J = IntEnum('I', 42) -K = IntEnum('I', 'p q', 'z') +K = IntEnum('I', 'p q', 'x', 'y', 'z', 'p', 'q') L = Enum('L', ' ') M = Enum('M', ()) N = IntEnum('M', []) @@ -357,7 +367,7 @@ main:14: error: Enum() with tuple or list expects strings or (name, value) pairs main:15: error: Enum() with tuple or list expects strings or (name, value) pairs main:16: error: IntEnum() with tuple or list expects strings or (name, value) pairs main:17: error: Enum() with dict literal requires string literals -main:18: error: Unexpected arguments to Enum() +main:18: error: Unexpected keyword argument 'keyword' main:19: error: Unexpected arguments to Enum() main:20: error: Unexpected arguments to Enum() main:22: error: "Type[W]" has no attribute "c" From 0a8967cefc9ac9c2153281f46a47ee18fe16938f Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 22 Jul 2020 15:49:29 +0200 Subject: [PATCH 078/138] Clean up terminal width handling (#9143) * Remove fixed_terminal_width from utils.colorize docstring This was added by 91adf615f98b110089bff842aafcdd7eb657093f, yet colorize() doesn't actually have a fixed_terminal_width argument. * Move MYPY_FORCE_TERMINAL_WIDTH handling into get_terminal_width() Those two callers are the only two, and both check MYPY_FORCE_TERMINAL_WIDTH before calling get_terminal_width(). * Use shutil.get_terminal_size() The documentation for Python's os.get_terminal_size() says: shutil.get_terminal_size() is the high-level function which should normally be used, os.get_terminal_size is the low-level implementation. https://docs.python.org/3/library/os.html#os.get_terminal_size shutil.get_terminal_size() already takes care of various corner cases such as: - Providing a fallback (to 80 columns, like the current DEFAULT_COLUMNS) - Returning that fallback if os.get_terminal_size() raises OSError - Doing the same if it returns a size of 0 In addition to that: - It takes care of some more corner cases ("stdout is None, closed, detached, or not a terminal, or os.get_terminal_size() is unsupported") - It adds support for the COLUMNS environment variable (rather than the non-standard MYPY_FORCE_TERMINAL_WIDTH) https://github.com/python/cpython/blob/v3.8.3/Lib/shutil.py#L1298-L1341 --- mypy/dmypy/client.py | 3 +-- mypy/util.py | 20 ++++---------------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/mypy/dmypy/client.py b/mypy/dmypy/client.py index b02ac23a0be9..141c18993fcc 100644 --- a/mypy/dmypy/client.py +++ b/mypy/dmypy/client.py @@ -475,8 +475,7 @@ def request(status_file: str, command: str, *, timeout: Optional[int] = None, # Tell the server whether this request was initiated from a human-facing terminal, # so that it can format the type checking output accordingly. args['is_tty'] = sys.stdout.isatty() or int(os.getenv('MYPY_FORCE_COLOR', '0')) > 0 - args['terminal_width'] = (int(os.getenv('MYPY_FORCE_TERMINAL_WIDTH', '0')) or - get_terminal_width()) + args['terminal_width'] = get_terminal_width() bdata = json.dumps(args).encode('utf8') _, name = get_status(status_file) try: diff --git a/mypy/util.py b/mypy/util.py index 6f6252136d64..85b07c1c6b34 100644 --- a/mypy/util.py +++ b/mypy/util.py @@ -7,6 +7,7 @@ import sys import hashlib import io +import shutil from typing import ( TypeVar, List, Tuple, Optional, Dict, Sequence, Iterable, Container, IO, Callable @@ -34,7 +35,6 @@ PLAIN_ANSI_DIM = '\x1b[2m' # type: Final DEFAULT_SOURCE_OFFSET = 4 # type: Final -DEFAULT_COLUMNS = 80 # type: Final # At least this number of columns will be shown on each side of # error location when printing source code snippet. @@ -424,14 +424,7 @@ def split_words(msg: str) -> List[str]: def get_terminal_width() -> int: """Get current terminal width if possible, otherwise return the default one.""" - try: - cols, _ = os.get_terminal_size() - except OSError: - return DEFAULT_COLUMNS - else: - if cols == 0: - return DEFAULT_COLUMNS - return cols + return int(os.getenv('MYPY_FORCE_TERMINAL_WIDTH', '0')) or shutil.get_terminal_size().columns def soft_wrap(msg: str, max_len: int, first_offset: int, @@ -594,8 +587,7 @@ def style(self, text: str, color: Literal['red', 'green', 'blue', 'yellow', 'non def fit_in_terminal(self, messages: List[str], fixed_terminal_width: Optional[int] = None) -> List[str]: """Improve readability by wrapping error messages and trimming source code.""" - width = (fixed_terminal_width or int(os.getenv('MYPY_FORCE_TERMINAL_WIDTH', '0')) or - get_terminal_width()) + width = fixed_terminal_width or get_terminal_width() new_messages = messages.copy() for i, error in enumerate(messages): if ': error:' in error: @@ -619,11 +611,7 @@ def fit_in_terminal(self, messages: List[str], return new_messages def colorize(self, error: str) -> str: - """Colorize an output line by highlighting the status and error code. - - If fixed_terminal_width is given, use it instead of calling get_terminal_width() - (used by the daemon). - """ + """Colorize an output line by highlighting the status and error code.""" if ': error:' in error: loc, msg = error.split('error:', maxsplit=1) if not self.show_error_codes: From 65186ae1e23fd44f1d7e6aa2c4458bdf55640742 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Wed, 22 Jul 2020 16:51:04 +0300 Subject: [PATCH 079/138] Remove note that Final is experimental and import from typing (#9138) Since Python 3.8/PEP591, `Final` and `final` are official and available from `typing`. --- docs/source/final_attrs.rst | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/source/final_attrs.rst b/docs/source/final_attrs.rst index a2ecf51fe15b..03abfe6051d6 100644 --- a/docs/source/final_attrs.rst +++ b/docs/source/final_attrs.rst @@ -15,14 +15,15 @@ They is no runtime enforcement by the Python runtime. .. note:: - These are experimental features. They might change in later - versions of mypy. The *final* qualifiers are available through the - ``typing_extensions`` package on PyPI. + The examples in this page import ``Final`` and ``final`` from the + ``typing`` module. These types were added to ``typing`` in Python 3.8, + but are also available for use in Python 2.7 and 3.4 - 3.7 via the + ``typing_extensions`` package. Final names ----------- -You can use the ``typing_extensions.Final`` qualifier to indicate that +You can use the ``typing.Final`` qualifier to indicate that a name or attribute should not be reassigned, redefined, or overridden. This is often useful for module and class level constants as a way to prevent unintended modification. Mypy will prevent @@ -30,7 +31,7 @@ further assignments to final names in type-checked code: .. code-block:: python - from typing_extensions import Final + from typing import Final RATE: Final = 3000 @@ -45,7 +46,7 @@ from being overridden in a subclass: .. code-block:: python - from typing_extensions import Final + from typing import Final class Window: BORDER_WIDTH: Final = 2.5 @@ -155,12 +156,11 @@ Final methods ------------- Like with attributes, sometimes it is useful to protect a method from -overriding. You can use the ``typing_extensions.final`` -decorator for this purpose: +overriding. You can use the ``typing.final`` decorator for this purpose: .. code-block:: python - from typing_extensions import final + from typing import final class Base: @final @@ -193,12 +193,12 @@ to make it final (or on the first overload in stubs): Final classes ------------- -You can apply the ``typing_extensions.final`` decorator to a class to indicate +You can apply the ``typing.final`` decorator to a class to indicate to mypy that it should not be subclassed: .. code-block:: python - from typing_extensions import final + from typing import final @final class Leaf: @@ -227,7 +227,7 @@ mypy, since those attributes could never be implemented. .. code-block:: python from abc import ABCMeta, abstractmethod - from typing_extensions import final + from typing import final @final class A(metaclass=ABCMeta): # error: Final class A has abstract attributes "f" From 997b5f254a1dc5289ca021fddf7edd9965c6288e Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Tue, 28 Jul 2020 14:59:56 +0800 Subject: [PATCH 080/138] [mypyc] Introduce LoadMem (#9211) Relates mypyc/mypyc#741 Introduce LoadMem IR to read a memory address and convert it into a designated-type value. (The address would mostly be in py_ssize_t for now.) Part of efforts to implement len primitives: *(Py_ssize_t*)(ob + size_offset). --- mypyc/analysis/dataflow.py | 5 ++++- mypyc/codegen/emitfunc.py | 9 ++++++++- mypyc/ir/ops.py | 31 ++++++++++++++++++++++++++++++- mypyc/ir/rtypes.py | 4 ++++ mypyc/test/test_emitfunc.py | 11 ++++++++++- 5 files changed, 56 insertions(+), 4 deletions(-) diff --git a/mypyc/analysis/dataflow.py b/mypyc/analysis/dataflow.py index 029056a15c38..75942cb04a34 100644 --- a/mypyc/analysis/dataflow.py +++ b/mypyc/analysis/dataflow.py @@ -9,7 +9,7 @@ BasicBlock, OpVisitor, Assign, LoadInt, LoadErrorValue, RegisterOp, Goto, Branch, Return, Call, Environment, Box, Unbox, Cast, Op, Unreachable, TupleGet, TupleSet, GetAttr, SetAttr, LoadStatic, InitStatic, PrimitiveOp, MethodCall, RaiseStandardError, CallC, LoadGlobal, - Truncate, BinaryIntOp + Truncate, BinaryIntOp, LoadMem ) @@ -208,6 +208,9 @@ def visit_load_global(self, op: LoadGlobal) -> GenAndKill: def visit_binary_int_op(self, op: BinaryIntOp) -> GenAndKill: return self.visit_register_op(op) + def visit_load_mem(self, op: LoadMem) -> GenAndKill: + return self.visit_register_op(op) + class DefinedVisitor(BaseAnalysisVisitor): """Visitor for finding defined registers. diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index 52a1ac2f3f90..0b560839c0b9 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -12,7 +12,7 @@ LoadStatic, InitStatic, TupleGet, TupleSet, Call, IncRef, DecRef, Box, Cast, Unbox, BasicBlock, Value, MethodCall, PrimitiveOp, EmitterInterface, Unreachable, NAMESPACE_STATIC, NAMESPACE_TYPE, NAMESPACE_MODULE, RaiseStandardError, CallC, LoadGlobal, Truncate, - BinaryIntOp + BinaryIntOp, LoadMem ) from mypyc.ir.rtypes import ( RType, RTuple, is_tagged, is_int32_rprimitive, is_int64_rprimitive @@ -464,6 +464,13 @@ def visit_binary_int_op(self, op: BinaryIntOp) -> None: self.emit_line('%s = %s%s %s %s%s;' % (dest, lhs_cast, lhs, op.op_str[op.op], rhs_cast, rhs)) + def visit_load_mem(self, op: LoadMem) -> None: + dest = self.reg(op) + src = self.reg(op.src) + # TODO: we shouldn't dereference to type that are pointer type so far + type = self.ctype(op.type) + self.emit_line('%s = *(%s *)%s;' % (dest, type, src)) + # Helpers def label(self, label: BasicBlock) -> str: diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index e4eeeba0a2bb..9b871ad6309c 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -25,7 +25,7 @@ from mypyc.ir.rtypes import ( RType, RInstance, RTuple, RVoid, is_bool_rprimitive, is_int_rprimitive, is_short_int_rprimitive, is_none_rprimitive, object_rprimitive, bool_rprimitive, - short_int_rprimitive, int_rprimitive, void_rtype + short_int_rprimitive, int_rprimitive, void_rtype, is_c_py_ssize_t_rprimitive ) from mypyc.common import short_name @@ -1347,6 +1347,31 @@ def accept(self, visitor: 'OpVisitor[T]') -> T: return visitor.visit_binary_int_op(self) +class LoadMem(RegisterOp): + """Reading a memory location + + type ret = *(type*)src + """ + error_kind = ERR_NEVER + + def __init__(self, type: RType, src: Value, line: int = -1) -> None: + super().__init__(line) + self.type = type + # TODO: for now we enforce that the src memory address should be Py_ssize_t + # later we should also support same width unsigned int + assert is_c_py_ssize_t_rprimitive(src.type) + self.src = src + + def sources(self) -> List[Value]: + return [self.src] + + def to_str(self, env: Environment) -> str: + return env.format("%r = load_mem %r :: %r*", self, self.src, self.type) + + def accept(self, visitor: 'OpVisitor[T]') -> T: + return visitor.visit_load_mem(self) + + @trait class OpVisitor(Generic[T]): """Generic visitor over ops (uses the visitor design pattern).""" @@ -1453,6 +1478,10 @@ def visit_load_global(self, op: LoadGlobal) -> T: def visit_binary_int_op(self, op: BinaryIntOp) -> T: raise NotImplementedError + @abstractmethod + def visit_load_mem(self, op: LoadMem) -> T: + raise NotImplementedError + # TODO: Should this live somewhere else? LiteralsMap = Dict[Tuple[Type[object], Union[int, float, str, bytes, complex]], str] diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index 944ddc0a3c40..7a05c42fd886 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -301,6 +301,10 @@ def is_int64_rprimitive(rtype: RType) -> bool: return rtype is int64_rprimitive +def is_c_py_ssize_t_rprimitive(rtype: RType) -> bool: + return rtype is c_pyssize_t_rprimitive + + def is_float_rprimitive(rtype: RType) -> bool: return isinstance(rtype, RPrimitive) and rtype.name == 'builtins.float' diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index 9d4702146113..6107d6cee580 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -10,7 +10,7 @@ from mypyc.ir.ops import ( Environment, BasicBlock, Goto, Return, LoadInt, Assign, IncRef, DecRef, Branch, Call, Unbox, Box, TupleGet, GetAttr, PrimitiveOp, RegisterOp, - SetAttr, Op, Value, CallC, BinaryIntOp + SetAttr, Op, Value, CallC, BinaryIntOp, LoadMem ) from mypyc.ir.rtypes import ( RTuple, RInstance, int_rprimitive, bool_rprimitive, list_rprimitive, @@ -33,6 +33,7 @@ from mypyc.primitives.int_ops import int_neg_op from mypyc.subtype import is_subtype from mypyc.namegen import NameGenerator +from mypyc.common import IS_32_BIT_PLATFORM class TestFunctionEmitterVisitor(unittest.TestCase): @@ -273,6 +274,14 @@ def test_binary_int_op(self) -> None: self.assert_emit(BinaryIntOp(bool_rprimitive, self.i64, self.i64_1, BinaryIntOp.ULT, 1), """cpy_r_r04 = (uint64_t)cpy_r_i64 < (uint64_t)cpy_r_i64_1;""") + def test_load_mem(self) -> None: + if IS_32_BIT_PLATFORM: + self.assert_emit(LoadMem(bool_rprimitive, self.i32), + """cpy_r_r0 = *(char *)cpy_r_i32;""") + else: + self.assert_emit(LoadMem(bool_rprimitive, self.i64), + """cpy_r_r0 = *(char *)cpy_r_i64;""") + def assert_emit(self, op: Op, expected: str) -> None: self.emitter.fragments = [] self.declarations.fragments = [] From fba63875677ae855c7cbaf8f4c45a6c256868b19 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Wed, 29 Jul 2020 00:01:03 +0800 Subject: [PATCH 081/138] merge some misc ops (#9224) Relates mypyc/mypyc#734 --- mypyc/irbuild/builder.py | 2 +- mypyc/irbuild/callable_class.py | 2 +- mypyc/irbuild/classdef.py | 2 +- mypyc/irbuild/expression.py | 4 +- mypyc/irbuild/function.py | 8 +-- mypyc/irbuild/statement.py | 2 +- mypyc/primitives/misc_ops.py | 80 ++++++++++++++-------------- mypyc/test-data/irbuild-basic.test | 22 ++++---- mypyc/test-data/irbuild-classes.test | 6 +-- mypyc/test-data/irbuild-nested.test | 36 ++++++------- mypyc/test-data/irbuild-try.test | 2 +- 11 files changed, 82 insertions(+), 84 deletions(-) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 1a465fa65e91..a39dcad7b082 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -259,7 +259,7 @@ def gen_import(self, id: str, line: int) -> None: self.add_bool_branch(comparison, out, needs_import) self.activate_block(needs_import) - value = self.primitive_op(import_op, [self.load_static_unicode(id)], line) + value = self.call_c(import_op, [self.load_static_unicode(id)], line) self.add(InitStatic(value, id, namespace=NAMESPACE_MODULE)) self.goto_and_activate(out) diff --git a/mypyc/irbuild/callable_class.py b/mypyc/irbuild/callable_class.py index c0f845543f2c..009b36eab004 100644 --- a/mypyc/irbuild/callable_class.py +++ b/mypyc/irbuild/callable_class.py @@ -128,7 +128,7 @@ def add_get_to_callable_class(builder: IRBuilder, fn_info: FuncInfo) -> None: builder.add(Return(vself)) builder.activate_block(instance_block) - builder.add(Return(builder.primitive_op(method_new_op, [vself, builder.read(instance)], line))) + builder.add(Return(builder.call_c(method_new_op, [vself, builder.read(instance)], line))) blocks, env, _, fn_info = builder.leave() diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index 8b5f1da98d26..ef6527e445a0 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -505,7 +505,7 @@ def dataclass_finalize( """ finish_non_ext_dict(builder, non_ext, cdef.line) dec = builder.accept(next(d for d in cdef.decorators if is_dataclass_decorator(d))) - builder.primitive_op( + builder.call_c( dataclass_sleight_of_hand, [dec, type_obj, non_ext.dict, non_ext.anns], cdef.line) diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index c8db606eb52a..3e20951d491f 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -279,7 +279,7 @@ def translate_super_method_call(builder: IRBuilder, expr: CallExpr, callee: Supe if decl.kind != FUNC_STATICMETHOD: vself = next(iter(builder.environment.indexes)) # grab first argument if decl.kind == FUNC_CLASSMETHOD: - vself = builder.primitive_op(type_op, [vself], expr.line) + vself = builder.call_c(type_op, [vself], expr.line) elif builder.fn_info.is_generator: # For generator classes, the self target is the 6th value # in the symbol table (which is an ordered dict). This is sort @@ -570,7 +570,7 @@ def get_arg(arg: Optional[Expression]) -> Value: args = [get_arg(expr.begin_index), get_arg(expr.end_index), get_arg(expr.stride)] - return builder.primitive_op(new_slice_op, args, expr.line) + return builder.call_c(new_slice_op, args, expr.line) def transform_generator_expr(builder: IRBuilder, o: GeneratorExpr) -> Value: diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index ad42a12584ee..121f28088da7 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -493,7 +493,7 @@ def handle_yield_from_and_await(builder: IRBuilder, o: Union[YieldFromExpr, Awai if isinstance(o, YieldFromExpr): iter_val = builder.primitive_op(iter_op, [builder.accept(o.expr)], o.line) else: - iter_val = builder.primitive_op(coro_op, [builder.accept(o.expr)], o.line) + iter_val = builder.call_c(coro_op, [builder.accept(o.expr)], o.line) iter_reg = builder.maybe_spill_assignable(iter_val) @@ -504,7 +504,7 @@ def handle_yield_from_and_await(builder: IRBuilder, o: Union[YieldFromExpr, Awai # Try extracting a return value from a StopIteration and return it. # If it wasn't, this reraises the exception. builder.activate_block(stop_block) - builder.assign(result, builder.primitive_op(check_stop_op, [], o.line), o.line) + builder.assign(result, builder.call_c(check_stop_op, [], o.line), o.line) builder.goto(done_block) builder.activate_block(main_block) @@ -543,7 +543,7 @@ def except_body() -> None: def else_body() -> None: # Do a next() or a .send(). It will return NULL on exception # but it won't automatically propagate. - _y = builder.primitive_op( + _y = builder.call_c( send_op, [builder.read(iter_reg), builder.read(received_reg)], o.line ) ok, stop = BasicBlock(), BasicBlock() @@ -557,7 +557,7 @@ def else_body() -> None: # Try extracting a return value from a StopIteration and return it. # If it wasn't, this rereaises the exception. builder.activate_block(stop) - builder.assign(result, builder.primitive_op(check_stop_op, [], o.line), o.line) + builder.assign(result, builder.call_c(check_stop_op, [], o.line), o.line) builder.nonlocal_control[-1].gen_break(builder, o.line) builder.push_loop_stack(loop_block, done_block) diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py index 184458b85f31..ecbfcd18ea9d 100644 --- a/mypyc/irbuild/statement.py +++ b/mypyc/irbuild/statement.py @@ -534,7 +534,7 @@ def transform_with(builder: IRBuilder, # We could probably optimize the case where the manager is compiled by us, # but that is not our common case at all, so. mgr_v = builder.accept(expr) - typ = builder.primitive_op(type_op, [mgr_v], line) + typ = builder.call_c(type_op, [mgr_v], line) exit_ = builder.maybe_spill(builder.py_get_attr(typ, '__exit__', line)) value = builder.py_call( builder.py_get_attr(typ, '__enter__', line), [mgr_v], line diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py index c7c2dc03e3c7..bab25ef58a07 100644 --- a/mypyc/primitives/misc_ops.py +++ b/mypyc/primitives/misc_ops.py @@ -7,7 +7,7 @@ ) from mypyc.primitives.registry import ( name_ref_op, simple_emit, unary_op, func_op, custom_op, call_emit, name_emit, - call_negative_magic_emit, c_function_op + call_negative_magic_emit, c_function_op, c_custom_op ) @@ -61,22 +61,22 @@ error_kind=ERR_NEVER) # Return the result of obj.__await()__ or obj.__iter__() (if no __await__ exists) -coro_op = custom_op(name='get_coroutine_obj', - arg_types=[object_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPy_GetCoro')) +coro_op = c_custom_op( + arg_types=[object_rprimitive], + return_type=object_rprimitive, + c_function_name='CPy_GetCoro', + error_kind=ERR_MAGIC) # Do obj.send(value), or a next(obj) if second arg is None. # (This behavior is to match the PEP 380 spec for yield from.) # Like next_raw_op, don't swallow StopIteration, # but also don't propagate an error. # Can return NULL: see next_op. -send_op = custom_op(name='send', - arg_types=[object_rprimitive, object_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_NEVER, - emit=call_emit('CPyIter_Send')) +send_op = c_custom_op( + arg_types=[object_rprimitive, object_rprimitive], + return_type=object_rprimitive, + c_function_name='CPyIter_Send', + error_kind=ERR_NEVER) # This is sort of unfortunate but oh well: yield_from_except performs most of the # error handling logic in `yield from` operations. It returns a bool and a value. @@ -96,20 +96,20 @@ emit=simple_emit('{dest}.f0 = CPy_YieldFromErrorHandle({args[0]}, &{dest}.f1);')) # Create method object from a callable object and self. -method_new_op = custom_op(name='method_new', - arg_types=[object_rprimitive, object_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('PyMethod_New')) +method_new_op = c_custom_op( + arg_types=[object_rprimitive, object_rprimitive], + return_type=object_rprimitive, + c_function_name='PyMethod_New', + error_kind=ERR_MAGIC) # Check if the current exception is a StopIteration and return its value if so. # Treats "no exception" as StopIteration with a None value. # If it is a different exception, re-reraise it. -check_stop_op = custom_op(name='check_stop_iteration', - arg_types=[], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPy_FetchStopIterationValue')) +check_stop_op = c_custom_op( + arg_types=[], + return_type=object_rprimitive, + c_function_name='CPy_FetchStopIterationValue', + error_kind=ERR_MAGIC) # Negate a primitive bool unary_op(op='not', @@ -133,12 +133,11 @@ ) # Import a module -import_op = custom_op( - name='import', +import_op = c_custom_op( arg_types=[str_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('PyImport_Import')) + return_type=object_rprimitive, + c_function_name='PyImport_Import', + error_kind=ERR_MAGIC) # Get the sys.modules dictionary get_module_dict_op = custom_op( @@ -186,20 +185,20 @@ emit=call_negative_magic_emit('PyObject_IsTrue')) # slice(start, stop, step) -new_slice_op = func_op( - 'builtins.slice', +new_slice_op = c_function_op( + name='builtins.slice', arg_types=[object_rprimitive, object_rprimitive, object_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('PySlice_New')) + c_function_name='PySlice_New', + return_type=object_rprimitive, + error_kind=ERR_MAGIC) # type(obj) -type_op = func_op( - 'builtins.type', +type_op = c_function_op( + name='builtins.type', arg_types=[object_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_NEVER, - emit=call_emit('PyObject_Type')) + c_function_name='PyObject_Type', + return_type=object_rprimitive, + error_kind=ERR_NEVER) # Get 'builtins.type' (base class of all classes) type_object_op = name_ref_op( @@ -221,9 +220,8 @@ # Create a dataclass from an extension class. See # CPyDataclass_SleightOfHand for more docs. -dataclass_sleight_of_hand = custom_op( +dataclass_sleight_of_hand = c_custom_op( arg_types=[object_rprimitive, object_rprimitive, dict_rprimitive, dict_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_FALSE, - format_str='{dest} = dataclass_sleight_of_hand({comma_args})', - emit=call_emit('CPyDataclass_SleightOfHand')) + return_type=bool_rprimitive, + c_function_name='CPyDataclass_SleightOfHand', + error_kind=ERR_FALSE) diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 307e9c85b858..96e39f2baadd 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -1585,7 +1585,7 @@ L0: if r2 goto L2 else goto L1 :: bool L1: r3 = unicode_0 :: static ('builtins') - r4 = import r3 :: str + r4 = PyImport_Import(r3) builtins = r4 :: module L2: r5 = __main__.globals :: static @@ -2636,7 +2636,7 @@ L0: if r2 goto L2 else goto L1 :: bool L1: r3 = unicode_0 :: static ('builtins') - r4 = import r3 :: str + r4 = PyImport_Import(r3) builtins = r4 :: module L2: r5 = typing :: module @@ -2645,7 +2645,7 @@ L2: if r7 goto L4 else goto L3 :: bool L3: r8 = unicode_1 :: static ('typing') - r9 = import r8 :: str + r9 = PyImport_Import(r8) typing = r9 :: module L4: r10 = typing :: module @@ -2854,7 +2854,7 @@ L0: L1: return __mypyc_self__ L2: - r2 = method_new __mypyc_self__, instance + r2 = PyMethod_New(__mypyc_self__, instance) return r2 def g_a_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.g_a_obj @@ -2913,7 +2913,7 @@ L0: L1: return __mypyc_self__ L2: - r2 = method_new __mypyc_self__, instance + r2 = PyMethod_New(__mypyc_self__, instance) return r2 def g_b_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.g_b_obj @@ -2972,7 +2972,7 @@ L0: L1: return __mypyc_self__ L2: - r2 = method_new __mypyc_self__, instance + r2 = PyMethod_New(__mypyc_self__, instance) return r2 def __mypyc_d_decorator_helper_____mypyc_c_decorator_helper___obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.__mypyc_d_decorator_helper_____mypyc_c_decorator_helper___obj @@ -3065,7 +3065,7 @@ L0: if r2 goto L2 else goto L1 :: bool L1: r3 = unicode_0 :: static ('builtins') - r4 = import r3 :: str + r4 = PyImport_Import(r3) builtins = r4 :: module L2: r5 = typing :: module @@ -3074,7 +3074,7 @@ L2: if r7 goto L4 else goto L3 :: bool L3: r8 = unicode_1 :: static ('typing') - r9 = import r8 :: str + r9 = PyImport_Import(r8) typing = r9 :: module L4: r10 = typing :: module @@ -3122,7 +3122,7 @@ L0: L1: return __mypyc_self__ L2: - r2 = method_new __mypyc_self__, instance + r2 = PyMethod_New(__mypyc_self__, instance) return r2 def g_a_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.g_a_obj @@ -3191,7 +3191,7 @@ L0: if r2 goto L2 else goto L1 :: bool L1: r3 = unicode_0 :: static ('builtins') - r4 = import r3 :: str + r4 = PyImport_Import(r3) builtins = r4 :: module L2: r5 = typing :: module @@ -3200,7 +3200,7 @@ L2: if r7 goto L4 else goto L3 :: bool L3: r8 = unicode_1 :: static ('typing') - r9 = import r8 :: str + r9 = PyImport_Import(r8) typing = r9 :: module L4: r10 = typing :: module diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index 50a079955c7a..70764a663df2 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -372,7 +372,7 @@ L0: if r2 goto L2 else goto L1 :: bool L1: r3 = unicode_0 :: static ('builtins') - r4 = import r3 :: str + r4 = PyImport_Import(r3) builtins = r4 :: module L2: r5 = typing :: module @@ -381,7 +381,7 @@ L2: if r7 goto L4 else goto L3 :: bool L3: r8 = unicode_1 :: static ('typing') - r9 = import r8 :: str + r9 = PyImport_Import(r8) typing = r9 :: module L4: r10 = typing :: module @@ -400,7 +400,7 @@ L4: if r22 goto L6 else goto L5 :: bool L5: r23 = unicode_4 :: static ('mypy_extensions') - r24 = import r23 :: str + r24 = PyImport_Import(r23) mypy_extensions = r24 :: module L6: r25 = mypy_extensions :: module diff --git a/mypyc/test-data/irbuild-nested.test b/mypyc/test-data/irbuild-nested.test index 8eade905606d..45e7d957feb9 100644 --- a/mypyc/test-data/irbuild-nested.test +++ b/mypyc/test-data/irbuild-nested.test @@ -45,7 +45,7 @@ L0: L1: return __mypyc_self__ L2: - r2 = method_new __mypyc_self__, instance + r2 = PyMethod_New(__mypyc_self__, instance) return r2 def inner_a_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.inner_a_obj @@ -83,7 +83,7 @@ L0: L1: return __mypyc_self__ L2: - r2 = method_new __mypyc_self__, instance + r2 = PyMethod_New(__mypyc_self__, instance) return r2 def second_b_first_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.second_b_first_obj @@ -109,7 +109,7 @@ L0: L1: return __mypyc_self__ L2: - r2 = method_new __mypyc_self__, instance + r2 = PyMethod_New(__mypyc_self__, instance) return r2 def first_b_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.first_b_obj @@ -154,7 +154,7 @@ L0: L1: return __mypyc_self__ L2: - r2 = method_new __mypyc_self__, instance + r2 = PyMethod_New(__mypyc_self__, instance) return r2 def inner_c_obj.__call__(__mypyc_self__, s): __mypyc_self__ :: __main__.inner_c_obj @@ -193,7 +193,7 @@ L0: L1: return __mypyc_self__ L2: - r2 = method_new __mypyc_self__, instance + r2 = PyMethod_New(__mypyc_self__, instance) return r2 def inner_d_obj.__call__(__mypyc_self__, s): __mypyc_self__ :: __main__.inner_d_obj @@ -288,7 +288,7 @@ L0: L1: return __mypyc_self__ L2: - r2 = method_new __mypyc_self__, instance + r2 = PyMethod_New(__mypyc_self__, instance) return r2 def inner_a_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.inner_a_obj @@ -330,7 +330,7 @@ L0: L1: return __mypyc_self__ L2: - r2 = method_new __mypyc_self__, instance + r2 = PyMethod_New(__mypyc_self__, instance) return r2 def inner_b_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.inner_b_obj @@ -376,7 +376,7 @@ L0: L1: return __mypyc_self__ L2: - r2 = method_new __mypyc_self__, instance + r2 = PyMethod_New(__mypyc_self__, instance) return r2 def inner_c_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.inner_c_obj @@ -400,7 +400,7 @@ L0: L1: return __mypyc_self__ L2: - r2 = method_new __mypyc_self__, instance + r2 = PyMethod_New(__mypyc_self__, instance) return r2 def inner_c_obj_0.__call__(__mypyc_self__): __mypyc_self__ :: __main__.inner_c_obj_0 @@ -462,7 +462,7 @@ L0: L1: return __mypyc_self__ L2: - r2 = method_new __mypyc_self__, instance + r2 = PyMethod_New(__mypyc_self__, instance) return r2 def c_a_b_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.c_a_b_obj @@ -488,7 +488,7 @@ L0: L1: return __mypyc_self__ L2: - r2 = method_new __mypyc_self__, instance + r2 = PyMethod_New(__mypyc_self__, instance) return r2 def b_a_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.b_a_obj @@ -558,7 +558,7 @@ L0: L1: return __mypyc_self__ L2: - r2 = method_new __mypyc_self__, instance + r2 = PyMethod_New(__mypyc_self__, instance) return r2 def inner_f_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.inner_f_obj @@ -582,7 +582,7 @@ L0: L1: return __mypyc_self__ L2: - r2 = method_new __mypyc_self__, instance + r2 = PyMethod_New(__mypyc_self__, instance) return r2 def inner_f_obj_0.__call__(__mypyc_self__): __mypyc_self__ :: __main__.inner_f_obj_0 @@ -651,7 +651,7 @@ L0: L1: return __mypyc_self__ L2: - r2 = method_new __mypyc_self__, instance + r2 = PyMethod_New(__mypyc_self__, instance) return r2 def foo_f_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.foo_f_obj @@ -677,7 +677,7 @@ L0: L1: return __mypyc_self__ L2: - r2 = method_new __mypyc_self__, instance + r2 = PyMethod_New(__mypyc_self__, instance) return r2 def bar_f_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.bar_f_obj @@ -703,7 +703,7 @@ L0: L1: return __mypyc_self__ L2: - r2 = method_new __mypyc_self__, instance + r2 = PyMethod_New(__mypyc_self__, instance) return r2 def baz_f_obj.__call__(__mypyc_self__, n): __mypyc_self__ :: __main__.baz_f_obj @@ -796,7 +796,7 @@ L0: L1: return __mypyc_self__ L2: - r2 = method_new __mypyc_self__, instance + r2 = PyMethod_New(__mypyc_self__, instance) return r2 def __mypyc_lambda__0_f_obj.__call__(__mypyc_self__, a, b): __mypyc_self__ :: __main__.__mypyc_lambda__0_f_obj @@ -818,7 +818,7 @@ L0: L1: return __mypyc_self__ L2: - r2 = method_new __mypyc_self__, instance + r2 = PyMethod_New(__mypyc_self__, instance) return r2 def __mypyc_lambda__1_f_obj.__call__(__mypyc_self__, a, b): __mypyc_self__ :: __main__.__mypyc_lambda__1_f_obj diff --git a/mypyc/test-data/irbuild-try.test b/mypyc/test-data/irbuild-try.test index 6183ab9de1da..f8b3bfca5c12 100644 --- a/mypyc/test-data/irbuild-try.test +++ b/mypyc/test-data/irbuild-try.test @@ -356,7 +356,7 @@ def foo(x): r29 :: None L0: r0 = py_call(x) - r1 = type r0 :: object + r1 = PyObject_Type(r0) r2 = unicode_3 :: static ('__exit__') r3 = getattr r1, r2 r4 = unicode_4 :: static ('__enter__') From cc46692020ef6b672b761512095fbba281fdefc7 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 31 Jul 2020 16:06:40 +0300 Subject: [PATCH 082/138] Fix untyped decorator overload error on class decorator with __call__ overloads (#9232) When --disallow-untyped-decorators is used, a callable-class decorator with a overloads on its __call__ implementation would incorrectly issue an "Untyped decorator makes function untyped" error, even when the overloads are typed properly. This has been reported in pytest, which does this: pytest-dev/pytest#7589. This fixes the problem by expanding on a previous fix in this area, #5509. --- mypy/checker.py | 5 ++++- test-data/unit/check-overloading.test | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/mypy/checker.py b/mypy/checker.py index 2558440168e3..75739fe87a00 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -5757,7 +5757,10 @@ def is_untyped_decorator(typ: Optional[Type]) -> bool: elif isinstance(typ, Instance): method = typ.type.get_method('__call__') if method: - return not is_typed_callable(method.type) + if isinstance(method.type, Overloaded): + return any(is_untyped_decorator(item) for item in method.type.items()) + else: + return not is_typed_callable(method.type) else: return False elif isinstance(typ, Overloaded): diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 4b3fbfdda851..959983db4495 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -4955,6 +4955,33 @@ def g(name: str) -> int: reveal_type(f) # N: Revealed type is 'def (name: builtins.str) -> builtins.int' reveal_type(g) # N: Revealed type is 'def (name: builtins.str) -> builtins.int' +[case testDisallowUntypedDecoratorsOverloadDunderCall] +# flags: --disallow-untyped-decorators +from typing import Any, Callable, overload, TypeVar + +F = TypeVar('F', bound=Callable[..., Any]) + +class Dec: + @overload + def __call__(self, x: F) -> F: ... + @overload + def __call__(self, x: str) -> Callable[[F], F]: ... + def __call__(self, x) -> Any: + pass + +dec = Dec() + +@dec +def f(name: str) -> int: + return 0 + +@dec('abc') +def g(name: str) -> int: + return 0 + +reveal_type(f) # N: Revealed type is 'def (name: builtins.str) -> builtins.int' +reveal_type(g) # N: Revealed type is 'def (name: builtins.str) -> builtins.int' + [case testOverloadBadArgumentsInferredToAny1] from typing import Union, Any, overload From a32d2f41304061639044eafc1a40d4fbb4ea49d4 Mon Sep 17 00:00:00 2001 From: Akuli Date: Fri, 31 Jul 2020 16:27:41 +0300 Subject: [PATCH 083/138] Speed up make_simplified_union for Literal[string]s (issue #9169) (#9192) make_simplified_union no longer runs for 0.5 seconds every time it's called with a Union containing 270 literals of strings. Like I explained in #9169, this only fixes half of the problem and I'm not capable of fixing the other half. This function is still called 1098 times for the "reduced" example code, and IMO it should be called only once. --- mypy/typeops.py | 44 +++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/mypy/typeops.py b/mypy/typeops.py index 0833b15a0bee..a31c07ae74a2 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -344,21 +344,35 @@ def make_simplified_union(items: Sequence[Type], from mypy.subtypes import is_proper_subtype removed = set() # type: Set[int] - for i, ti in enumerate(items): - if i in removed: continue - # Keep track of the truishness info for deleted subtypes which can be relevant - cbt = cbf = False - for j, tj in enumerate(items): - if i != j and is_proper_subtype(tj, ti, keep_erased_types=keep_erased): - # We found a redundant item in the union. - removed.add(j) - cbt = cbt or tj.can_be_true - cbf = cbf or tj.can_be_false - # if deleted subtypes had more general truthiness, use that - if not ti.can_be_true and cbt: - items[i] = true_or_false(ti) - elif not ti.can_be_false and cbf: - items[i] = true_or_false(ti) + + # Avoid slow nested for loop for Union of Literal of strings (issue #9169) + if all((isinstance(item, LiteralType) and + item.fallback.type.fullname == 'builtins.str') + for item in items): + seen = set() # type: Set[str] + for index, item in enumerate(items): + assert isinstance(item, LiteralType) + assert isinstance(item.value, str) + if item.value in seen: + removed.add(index) + seen.add(item.value) + + else: + for i, ti in enumerate(items): + if i in removed: continue + # Keep track of the truishness info for deleted subtypes which can be relevant + cbt = cbf = False + for j, tj in enumerate(items): + if i != j and is_proper_subtype(tj, ti, keep_erased_types=keep_erased): + # We found a redundant item in the union. + removed.add(j) + cbt = cbt or tj.can_be_true + cbf = cbf or tj.can_be_false + # if deleted subtypes had more general truthiness, use that + if not ti.can_be_true and cbt: + items[i] = true_or_false(ti) + elif not ti.can_be_false and cbf: + items[i] = true_or_false(ti) simplified_set = [items[i] for i in range(len(items)) if i not in removed] return UnionType.make_union(simplified_set, line, column) From 8b43fb28da5189eb24d7d97dc5f8779137da3e99 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Fri, 31 Jul 2020 22:23:11 +0800 Subject: [PATCH 084/138] [mypyc] Introduce RStruct (#9215) Related to #9211. This PR introduces RStruct to represent C structs. StructInfo is used to hold information about a unique struct-like type. This PR also introduces several functions to compute aligned offsets and size of structure types. --- mypyc/common.py | 2 + mypyc/ir/rtypes.py | 158 ++++++++++++++++++++++++++-- mypyc/primitives/struct_regsitry.py | 24 +++++ mypyc/rt_subtype.py | 5 +- mypyc/sametype.py | 5 +- mypyc/subtype.py | 5 +- mypyc/test/test_struct.py | 102 ++++++++++++++++++ 7 files changed, 291 insertions(+), 10 deletions(-) create mode 100644 mypyc/primitives/struct_regsitry.py create mode 100644 mypyc/test/test_struct.py diff --git a/mypyc/common.py b/mypyc/common.py index bc999f5b6ba1..9689e2c4aa18 100644 --- a/mypyc/common.py +++ b/mypyc/common.py @@ -31,6 +31,8 @@ IS_32_BIT_PLATFORM = sys.maxsize < (1 << 31) # type: Final +PLATFORM_SIZE = 4 if IS_32_BIT_PLATFORM else 8 + # Runtime C library files RUNTIME_C_FILES = [ 'init.c', diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index 7a05c42fd886..46be550cec05 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -21,11 +21,11 @@ """ from abc import abstractmethod -from typing import Optional, Union, List, Dict, Generic, TypeVar +from typing import Optional, Union, List, Dict, Generic, TypeVar, Tuple from typing_extensions import Final, ClassVar, TYPE_CHECKING -from mypyc.common import JsonDict, short_name, IS_32_BIT_PLATFORM +from mypyc.common import JsonDict, short_name, IS_32_BIT_PLATFORM, PLATFORM_SIZE from mypyc.namegen import NameGenerator if TYPE_CHECKING: @@ -119,6 +119,10 @@ def visit_runion(self, typ: 'RUnion') -> T: def visit_rtuple(self, typ: 'RTuple') -> T: raise NotImplementedError + @abstractmethod + def visit_rstruct(self, typ: 'RStruct') -> T: + raise NotImplementedError + @abstractmethod def visit_rvoid(self, typ: 'RVoid') -> T: raise NotImplementedError @@ -167,13 +171,15 @@ def __init__(self, name: str, is_unboxed: bool, is_refcounted: bool, - ctype: str = 'PyObject *') -> None: + ctype: str = 'PyObject *', + size: int = PLATFORM_SIZE) -> None: RPrimitive.primitive_map[name] = self self.name = name self.is_unboxed = is_unboxed self._ctype = ctype self.is_refcounted = is_refcounted + self.size = size # TODO: For low-level integers, they actually don't have undefined values # we need to figure out some way to represent here. if ctype in ('CPyTagged', 'int32_t', 'int64_t'): @@ -238,9 +244,9 @@ def __repr__(self) -> str: # low level integer (corresponds to C's 'int's). int32_rprimitive = RPrimitive('int32', is_unboxed=True, is_refcounted=False, - ctype='int32_t') # type: Final + ctype='int32_t', size=4) # type: Final int64_rprimitive = RPrimitive('int64', is_unboxed=True, is_refcounted=False, - ctype='int64_t') # type: Final + ctype='int64_t', size=8) # type: Final # integer alias c_int_rprimitive = int32_rprimitive if IS_32_BIT_PLATFORM: @@ -256,11 +262,11 @@ def __repr__(self) -> str: # An unboxed boolean value. This actually has three possible values # (0 -> False, 1 -> True, 2 -> error). bool_rprimitive = RPrimitive('builtins.bool', is_unboxed=True, is_refcounted=False, - ctype='char') # type: Final + ctype='char', size=1) # type: Final # The 'None' value. The possible values are 0 -> None and 2 -> error. none_rprimitive = RPrimitive('builtins.None', is_unboxed=True, is_refcounted=False, - ctype='char') # type: Final + ctype='char', size=1) # type: Final # Python list object (or an instance of a subclass of list). list_rprimitive = RPrimitive('builtins.list', is_unboxed=False, is_refcounted=True) # type: Final @@ -368,6 +374,10 @@ def visit_rtuple(self, t: 'RTuple') -> str: parts = [elem.accept(self) for elem in t.types] return 'T{}{}'.format(len(parts), ''.join(parts)) + def visit_rstruct(self, t: 'RStruct') -> str: + assert False + return "" + def visit_rvoid(self, t: 'RVoid') -> str: assert False, "rvoid in tuple?" @@ -440,6 +450,140 @@ def deserialize(cls, data: JsonDict, ctx: 'DeserMaps') -> 'RTuple': ) +def compute_rtype_alignment(typ: RType) -> int: + """Compute alignment of a given type based on platform alignment rule""" + platform_alignment = PLATFORM_SIZE + if isinstance(typ, RPrimitive): + return typ.size + elif isinstance(typ, RInstance): + return platform_alignment + elif isinstance(typ, RUnion): + return platform_alignment + else: + if isinstance(typ, RTuple): + items = list(typ.types) + elif isinstance(typ, RStruct): + items = typ.types + else: + assert False, "invalid rtype for computing alignment" + max_alignment = max([compute_rtype_alignment(item) for item in items]) + return max_alignment + + +def compute_rtype_size(typ: RType) -> int: + """Compute unaligned size of rtype""" + if isinstance(typ, RPrimitive): + return typ.size + elif isinstance(typ, RTuple): + return compute_aligned_offsets_and_size(list(typ.types))[1] + elif isinstance(typ, RUnion): + return PLATFORM_SIZE + elif isinstance(typ, RStruct): + return compute_aligned_offsets_and_size(typ.types)[1] + elif isinstance(typ, RInstance): + return PLATFORM_SIZE + else: + assert False, "invalid rtype for computing size" + + +def compute_aligned_offsets_and_size(types: List[RType]) -> Tuple[List[int], int]: + """Compute offsets and total size of a list of types after alignment + + Note that the types argument are types of values that are stored + sequentially with platform default alignment. + """ + unaligned_sizes = [compute_rtype_size(typ) for typ in types] + alignments = [compute_rtype_alignment(typ) for typ in types] + + current_offset = 0 + offsets = [] + final_size = 0 + for i in range(len(unaligned_sizes)): + offsets.append(current_offset) + if i + 1 < len(unaligned_sizes): + cur_size = unaligned_sizes[i] + current_offset += cur_size + next_alignment = alignments[i + 1] + # compute aligned offset, + # check https://en.wikipedia.org/wiki/Data_structure_alignment for more infomation + current_offset = (current_offset + (next_alignment - 1)) & -next_alignment + else: + struct_alignment = max(alignments) + final_size = current_offset + unaligned_sizes[i] + final_size = (final_size + (struct_alignment - 1)) & -struct_alignment + return offsets, final_size + + +class StructInfo: + """Struct-like type Infomation + + StructInfo should work with registry to ensure constraints like the unique naming + constraint for struct type + """ + def __init__(self, + name: str, + names: List[str], + types: List[RType]) -> None: + self.name = name + self.names = names + self.types = types + # generate dummy names + if len(self.names) < len(self.types): + for i in range(len(self.types) - len(self.names)): + self.names.append('_item' + str(i)) + self.offsets, self.size = compute_aligned_offsets_and_size(types) + + +class RStruct(RType): + """Represent CPython structs""" + def __init__(self, + info: StructInfo) -> None: + self.info = info + self.name = self.info.name + self._ctype = self.info.name + + @property + def names(self) -> List[str]: + return self.info.names + + @property + def types(self) -> List[RType]: + return self.info.types + + @property + def offsets(self) -> List[int]: + return self.info.offsets + + @property + def size(self) -> int: + return self.info.size + + def accept(self, visitor: 'RTypeVisitor[T]') -> T: + return visitor.visit_rstruct(self) + + def __str__(self) -> str: + # if not tuple(unamed structs) + return '%s{%s}' % (self.name, ', '.join(name + ":" + str(typ) + for name, typ in zip(self.names, self.types))) + + def __repr__(self) -> str: + return '' % (self.name, ', '.join(name + ":" + repr(typ) for name, typ + in zip(self.names, self.types))) + + def __eq__(self, other: object) -> bool: + return isinstance(other, RStruct) and self.info == other.info + + def __hash__(self) -> int: + return hash(self.info) + + def serialize(self) -> JsonDict: + assert False + + @classmethod + def deserialize(cls, data: JsonDict, ctx: 'DeserMaps') -> 'RStruct': + assert False + + class RInstance(RType): """Instance of user-defined class (compiled to C extension class). diff --git a/mypyc/primitives/struct_regsitry.py b/mypyc/primitives/struct_regsitry.py new file mode 100644 index 000000000000..4233889c8fb8 --- /dev/null +++ b/mypyc/primitives/struct_regsitry.py @@ -0,0 +1,24 @@ +"""Struct registries for C backend""" + +from typing import List, NamedTuple +from mypyc.ir.rtypes import RType + +CStructDescription = NamedTuple( + 'CStructDescription', [('name', str), + ('names', List[str]), + ('types', List[RType])]) + + +def c_struct(name: str, + names: List[str], + types: List[RType]) -> CStructDescription: + """Define a known C struct for generating IR to manipulate it + + name: The name of the C struct + types: type of each field + names: name of each field + TODO: the names list can be empty in the future when we merge Tuple as part of Struct + """ + return CStructDescription(name, names, types) + +# TODO: create PyVarObject, to do which we probably need PyObject diff --git a/mypyc/rt_subtype.py b/mypyc/rt_subtype.py index 922f9a4f3a82..d704ffb6e95a 100644 --- a/mypyc/rt_subtype.py +++ b/mypyc/rt_subtype.py @@ -14,7 +14,7 @@ """ from mypyc.ir.rtypes import ( - RType, RUnion, RInstance, RPrimitive, RTuple, RVoid, RTypeVisitor, + RType, RUnion, RInstance, RPrimitive, RTuple, RVoid, RTypeVisitor, RStruct, is_int_rprimitive, is_short_int_rprimitive, ) from mypyc.subtype import is_subtype @@ -51,5 +51,8 @@ def visit_rtuple(self, left: RTuple) -> bool: is_runtime_subtype(t1, t2) for t1, t2 in zip(left.types, self.right.types)) return False + def visit_rstruct(self, left: RStruct) -> bool: + return isinstance(self.right, RStruct) and self.right.name == left.name + def visit_rvoid(self, left: RVoid) -> bool: return isinstance(self.right, RVoid) diff --git a/mypyc/sametype.py b/mypyc/sametype.py index 33f8f86fba4d..18e7cef1c20b 100644 --- a/mypyc/sametype.py +++ b/mypyc/sametype.py @@ -1,7 +1,7 @@ """Same type check for RTypes.""" from mypyc.ir.rtypes import ( - RType, RTypeVisitor, RInstance, RPrimitive, RTuple, RVoid, RUnion + RType, RTypeVisitor, RInstance, RPrimitive, RTuple, RVoid, RUnion, RStruct ) from mypyc.ir.func_ir import FuncSignature @@ -52,5 +52,8 @@ def visit_rtuple(self, left: RTuple) -> bool: and len(self.right.types) == len(left.types) and all(is_same_type(t1, t2) for t1, t2 in zip(left.types, self.right.types))) + def visit_rstruct(self, left: RStruct) -> bool: + return isinstance(self.right, RStruct) and self.right.name == left.name + def visit_rvoid(self, left: RVoid) -> bool: return isinstance(self.right, RVoid) diff --git a/mypyc/subtype.py b/mypyc/subtype.py index c12a839b0f95..a493c7557264 100644 --- a/mypyc/subtype.py +++ b/mypyc/subtype.py @@ -1,7 +1,7 @@ """Subtype check for RTypes.""" from mypyc.ir.rtypes import ( - RType, RInstance, RPrimitive, RTuple, RVoid, RTypeVisitor, RUnion, + RType, RInstance, RPrimitive, RTuple, RVoid, RTypeVisitor, RUnion, RStruct, is_bool_rprimitive, is_int_rprimitive, is_tuple_rprimitive, is_short_int_rprimitive, is_object_rprimitive @@ -56,5 +56,8 @@ def visit_rtuple(self, left: RTuple) -> bool: is_subtype(t1, t2) for t1, t2 in zip(left.types, self.right.types)) return False + def visit_rstruct(self, left: RStruct) -> bool: + return isinstance(self.right, RStruct) and self.right.name == left.name + def visit_rvoid(self, left: RVoid) -> bool: return isinstance(self.right, RVoid) diff --git a/mypyc/test/test_struct.py b/mypyc/test/test_struct.py new file mode 100644 index 000000000000..0478b891063b --- /dev/null +++ b/mypyc/test/test_struct.py @@ -0,0 +1,102 @@ +import unittest + +from mypyc.ir.rtypes import ( + RStruct, bool_rprimitive, int64_rprimitive, int32_rprimitive, object_rprimitive, StructInfo, + int_rprimitive +) +from mypyc.rt_subtype import is_runtime_subtype + + +class TestStruct(unittest.TestCase): + def test_struct_offsets(self) -> None: + # test per-member alignment + info = StructInfo("", [], [bool_rprimitive, int32_rprimitive, int64_rprimitive]) + r = RStruct(info) + assert r.size == 16 + assert r.offsets == [0, 4, 8] + + # test final alignment + info1 = StructInfo("", [], [bool_rprimitive, bool_rprimitive]) + r1 = RStruct(info1) + assert r1.size == 2 + assert r1.offsets == [0, 1] + info2 = StructInfo("", [], [int32_rprimitive, bool_rprimitive]) + r2 = RStruct(info2) + info3 = StructInfo("", [], [int64_rprimitive, bool_rprimitive]) + r3 = RStruct(info3) + assert r2.offsets == [0, 4] + assert r3.offsets == [0, 8] + assert r2.size == 8 + assert r3.size == 16 + + info4 = StructInfo("", [], [bool_rprimitive, bool_rprimitive, + bool_rprimitive, int32_rprimitive]) + r4 = RStruct(info4) + assert r4.size == 8 + assert r4.offsets == [0, 1, 2, 4] + + # test nested struct + info5 = StructInfo("", [], [bool_rprimitive, r]) + r5 = RStruct(info5) + assert r5.offsets == [0, 8] + assert r5.size == 24 + info6 = StructInfo("", [], [int32_rprimitive, r5]) + r6 = RStruct(info6) + assert r6.offsets == [0, 8] + assert r6.size == 32 + # test nested struct with alignment less than 8 + info7 = StructInfo("", [], [bool_rprimitive, r4]) + r7 = RStruct(info7) + assert r7.offsets == [0, 4] + assert r7.size == 12 + + def test_struct_str(self) -> None: + info = StructInfo("Foo", ["a", "b"], + [bool_rprimitive, object_rprimitive]) + r = RStruct(info) + assert str(r) == "Foo{a:bool, b:object}" + assert repr(r) == ", " \ + "b:}>" + info1 = StructInfo("Bar", ["c"], [int32_rprimitive]) + r1 = RStruct(info1) + assert str(r1) == "Bar{c:int32}" + assert repr(r1) == "}>" + info2 = StructInfo("Baz", [], []) + r2 = RStruct(info2) + assert str(r2) == "Baz{}" + assert repr(r2) == "" + + def test_runtime_subtype(self) -> None: + # right type to check with + info = StructInfo("Foo", ["a", "b"], + [bool_rprimitive, int_rprimitive]) + r = RStruct(info) + + # using the same StructInfo + r1 = RStruct(info) + + # names different + info2 = StructInfo("Bar", ["c", "b"], + [bool_rprimitive, int_rprimitive]) + r2 = RStruct(info2) + + # name different + info3 = StructInfo("Baz", ["a", "b"], + [bool_rprimitive, int_rprimitive]) + r3 = RStruct(info3) + + # type different + info4 = StructInfo("FooBar", ["a", "b"], + [bool_rprimitive, int32_rprimitive]) + r4 = RStruct(info4) + + # number of types different + info5 = StructInfo("FooBarBaz", ["a", "b", "c"], + [bool_rprimitive, int_rprimitive, bool_rprimitive]) + r5 = RStruct(info5) + + assert is_runtime_subtype(r1, r) is True + assert is_runtime_subtype(r2, r) is False + assert is_runtime_subtype(r3, r) is False + assert is_runtime_subtype(r4, r) is False + assert is_runtime_subtype(r5, r) is False From e87bf24521e6727910bdd0330c4a3cb0c926c937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oleg=20H=C3=B6fling?= Date: Fri, 31 Jul 2020 15:52:11 +0100 Subject: [PATCH 085/138] References for config file values (#7859) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds crossrefs to the docs, a follow-up of #7652 (crossrefs to standard library items) and #7784 (crossrefs for command line options), only now for the config file values. Signed-off-by: Oleg Höfling --- docs/source/conf.py | 16 ++ docs/source/config_file.rst | 413 +++++++++++++++++++++++++++++------- 2 files changed, 350 insertions(+), 79 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 3b547c9c745b..9f1ab882c5c8 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -15,6 +15,9 @@ import sys import os +from sphinx.application import Sphinx +from sphinx.util.docfields import Field + # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. @@ -275,3 +278,16 @@ 'monkeytype': ('https://monkeytype.readthedocs.io/en/latest', None), 'setuptools': ('https://setuptools.readthedocs.io/en/latest', None), } + + +def setup(app: Sphinx) -> None: + app.add_object_type( + 'confval', + 'confval', + objname='configuration value', + indextemplate='pair: %s; configuration value', + doc_field_types=[ + Field('type', label='Type', has_arg=False, names=('type',)), + Field('default', label='Default', has_arg=False, names=('default',)), + ] + ) diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index 07c3884de621..6e9593716924 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -57,6 +57,7 @@ section names in square brackets and flag settings of the form .. _config-precedence: When options conflict, the precedence order for configuration is: + 1. :ref:`Inline configuration ` in the source file 2. Sections with concrete module names (``foo.bar``) 3. Sections with "unstructured" wildcard patterns (``foo.*.baz``), @@ -73,7 +74,7 @@ unfortunate, and is subject to change in future versions. .. note:: - The ``warn_unused_configs`` flag may be useful to debug misspelled + The :confval:`warn_unused_configs` flag may be useful to debug misspelled section names. .. note:: @@ -167,7 +168,10 @@ Import discovery For more information, see the :ref:`Import discovery ` section of the command line docs. -``mypy_path`` (string) +.. confval:: mypy_path + + :type: string + Specifies the paths to use, after trying the paths from ``MYPYPATH`` environment variable. Useful if you'd like to keep stubs in your repo, along with the config file. Multiple paths are always separated with a ``:`` or ``,`` regardless of the platform. @@ -176,8 +180,11 @@ section of the command line docs. This option may only be set in the global section (``[mypy]``). **Note:** On Windows, use UNC paths to avoid using ``:`` (e.g. ``\\127.0.0.1\X$\MyDir`` where ``X`` is the drive letter). - -``files`` (comma-separated list of strings) + +.. confval:: files + + :type: comma-separated list of strings + A comma-separated list of paths which should be checked by mypy if none are given on the command line. Supports recursive file globbing using :py:mod:`glob`, where ``*`` (e.g. ``*.py``) matches files in the current directory and ``**/`` (e.g. ``**/*.py``) matches files in any directories below @@ -185,44 +192,63 @@ section of the command line docs. This option may only be set in the global section (``[mypy]``). -``namespace_packages`` (bool, default False) - Enables :pep:`420` style namespace packages. See :ref:`the - corresponding flag ` for more information. +.. confval:: namespace_packages + + :type: boolean + :default: False + + Enables :pep:`420` style namespace packages. See the + corresponding flag :option:`--namespace-packages ` for more information. This option may only be set in the global section (``[mypy]``). -``ignore_missing_imports`` (bool, default False) +.. confval:: ignore_missing_imports + + :type: boolean + :default: False + Suppresses error messages about imports that cannot be resolved. If this option is used in a per-module section, the module name should match the name of the *imported* module, not the module containing the import statement. -``follow_imports`` (string, default ``normal``) +.. confval:: follow_imports + + :type: string + :default: ``normal`` + Directs what to do with imports when the imported module is found as a ``.py`` file and not part of the files, modules and packages provided on the command line. The four possible values are ``normal``, ``silent``, ``skip`` and ``error``. For explanations see the discussion for the - :ref:`--follow-imports ` command line flag. + :option:`--follow-imports ` command line flag. If this option is used in a per-module section, the module name should match the name of the *imported* module, not the module containing the import statement. -``follow_imports_for_stubs`` (bool, default False) - Determines whether to respect the ``follow_imports`` setting even for +.. confval:: follow_imports_for_stubs + + :type: boolean + :default: False + + Determines whether to respect the :confval:`follow_imports` setting even for stub (``.pyi``) files. - Used in conjunction with ``follow_imports=skip``, this can be used + Used in conjunction with :confval:`follow_imports=skip `, this can be used to suppress the import of a module from ``typeshed``, replacing it with ``Any``. - Used in conjunction with ``follow_imports=error``, this can be used + Used in conjunction with :confval:`follow_imports=error `, this can be used to make any use of a particular ``typeshed`` module an error. -``python_executable`` (string) +.. confval:: python_executable + + :type: string + Specifies the path to the Python executable to inspect to collect a list of available :ref:`PEP 561 packages `. User home directory and environment variables will be expanded. Defaults to @@ -230,13 +256,21 @@ section of the command line docs. This option may only be set in the global section (``[mypy]``). -``no_site_packages`` (bool, default False) +.. confval:: no_site_packages + + :type: bool + :default: False + Disables using type information in installed packages (see :pep:`561`). This will also disable searching for a usable Python executable. This acts the same as :option:`--no-site-packages ` command line flag. -``no_silence_site_packages`` (bool, default False) +.. confval:: no_silence_site_packages + + :type: boolean + :default: False + Enables reporting error messages generated within installed packages (see :pep:`561` for more details on distributing type information). Those error messages are suppressed by default, since you are usually not able to @@ -248,7 +282,10 @@ section of the command line docs. Platform configuration ********************** -``python_version`` (string) +.. confval:: python_version + + :type: string + Specifies the Python version used to parse and check the target program. The string should be in the format ``DIGIT.DIGIT`` -- for example ``2.7``. The default is the version of the Python @@ -256,7 +293,10 @@ Platform configuration This option may only be set in the global section (``[mypy]``). -``platform`` (string) +.. confval:: platform + + :type: string + Specifies the OS platform for the target program, for example ``darwin`` or ``win32`` (meaning OS X or Windows, respectively). The default is the current platform as revealed by Python's @@ -264,11 +304,17 @@ Platform configuration This option may only be set in the global section (``[mypy]``). -``always_true`` (comma-separated list of strings) +.. confval:: always_true + + :type: comma-separated list of strings + Specifies a list of variables that mypy will treat as compile-time constants that are always true. -``always_false`` (comma-separated list of strings) +.. confval:: always_false + + :type: comma-separated list of strings + Specifies a list of variables that mypy will treat as compile-time constants that are always false. @@ -279,24 +325,48 @@ Disallow dynamic typing For more information, see the :ref:`Disallow dynamic typing ` section of the command line docs. -``disallow_any_unimported`` (bool, default False) +.. confval:: disallow_any_unimported + + :type: boolean + :default: False + Disallows usage of types that come from unfollowed imports (anything imported from an unfollowed import is automatically given a type of ``Any``). -``disallow_any_expr`` (bool, default False) +.. confval:: disallow_any_expr + + :type: boolean + :default: False + Disallows all expressions in the module that have type ``Any``. -``disallow_any_decorated`` (bool, default False) +.. confval:: disallow_any_decorated + + :type: boolean + :default: False + Disallows functions that have ``Any`` in their signature after decorator transformation. -``disallow_any_explicit`` (bool, default False) +.. confval:: disallow_any_explicit + + :type: boolean + :default: False + Disallows explicit ``Any`` in type positions such as type annotations and generic type parameters. -``disallow_any_generics`` (bool, default False) +.. confval:: disallow_any_generics + + :type: boolean + :default: False + Disallows usage of generic types that do not specify explicit type parameters. -``disallow_subclassing_any`` (bool, default False) +.. confval:: disallow_subclassing_any + + :type: boolean + :default: False + Disallows subclassing a value of type ``Any``. @@ -306,21 +376,41 @@ Untyped definitions and calls For more information, see the :ref:`Untyped definitions and calls ` section of the command line docs. -``disallow_untyped_calls`` (bool, default False) +.. confval:: disallow_untyped_calls + + :type: boolean + :default: False + Disallows calling functions without type annotations from functions with type annotations. -``disallow_untyped_defs`` (bool, default False) +.. confval:: disallow_untyped_defs + + :type: boolean + :default: False + Disallows defining functions without type annotations or with incomplete type annotations. -``disallow_incomplete_defs`` (bool, default False) +.. confval:: disallow_incomplete_defs + + :type: boolean + :default: False + Disallows defining functions with incomplete type annotations. -``check_untyped_defs`` (bool, default False) +.. confval:: check_untyped_defs + + :type: boolean + :default: False + Type-checks the interior of functions without type annotations. -``disallow_untyped_decorators`` (bool, default False) +.. confval:: disallow_untyped_decorators + + :type: boolean + :default: False + Reports an error whenever a function with type annotations is decorated with a decorator without annotations. @@ -333,11 +423,19 @@ None and Optional handling For more information, see the :ref:`None and Optional handling ` section of the command line docs. -``no_implicit_optional`` (bool, default False) +.. confval:: no_implicit_optional + + :type: boolean + :default: False + Changes the treatment of arguments with a default value of ``None`` by not implicitly making their type :py:data:`~typing.Optional`. -``strict_optional`` (bool, default True) +.. confval:: strict_optional + + :type: boolean + :default: True + Enables or disables strict Optional checks. If False, mypy treats ``None`` as compatible with every type. @@ -350,22 +448,42 @@ Configuring warnings For more information, see the :ref:`Configuring warnings ` section of the command line docs. -``warn_redundant_casts`` (bool, default False) +.. confval:: warn_redundant_casts + + :type: boolean + :default: False + Warns about casting an expression to its inferred type. This option may only be set in the global section (``[mypy]``). -``warn_unused_ignores`` (bool, default False) +.. confval:: warn_unused_ignores + + :type: boolean + :default: False + Warns about unneeded ``# type: ignore`` comments. -``warn_no_return`` (bool, default True) +.. confval:: warn_no_return + + :type: boolean + :default: True + Shows errors for missing return statements on some execution paths. -``warn_return_any`` (bool, default False) +.. confval:: warn_return_any + + :type: boolean + :default: False + Shows a warning when returning a value with type ``Any`` from a function declared with a non- ``Any`` return type. -``warn_unreachable`` (bool, default False) +.. confval:: warn_unreachable + + :type: boolean + :default: False + Shows a warning when encountering any code inferred to be unreachable or redundant after performing type analysis. @@ -376,26 +494,46 @@ Suppressing errors Note: these configuration options are available in the config file only. There is no analog available via the command line options. -``show_none_errors`` (bool, default True) - Shows errors related to strict ``None`` checking, if the global ``strict_optional`` +.. confval:: show_none_errors + + :type: boolean + :default: True + + Shows errors related to strict ``None`` checking, if the global :confval:`strict_optional` flag is enabled. -``ignore_errors`` (bool, default False) +.. confval:: ignore_errors + + :type: boolean + :default: False + Ignores all non-fatal errors. Miscellaneous strictness flags ****************************** -``allow_untyped_globals`` (bool, default False) +.. confval:: allow_untyped_globals + + :type: boolean + :default: False + Causes mypy to suppress errors caused by not being able to fully infer the types of global and class variables. -``allow_redefinition`` (bool, default False) +.. confval:: allow_redefinition + + :type: boolean + :default: False + Allows variables to be redefined with an arbitrary type, as long as the redefinition is in the same block and nesting level as the original definition. -``implicit_reexport`` (bool, default True) +.. confval:: implicit_reexport + + :type: boolean + :default: True + By default, imported values to a module are treated as exported and mypy allows other modules to import them. When false, mypy will not re-export unless the item is imported using from-as or is included in ``__all__``. Note that mypy @@ -411,7 +549,11 @@ Miscellaneous strictness flags from foo import bar __all__ = ['bar'] -``strict_equality`` (bool, default False) +.. confval:: strict_equality + + :type: boolean + :default: False + Prohibit equality checks, identity checks, and container checks between non-overlapping types. @@ -424,26 +566,54 @@ section of the command line docs. These options may only be set in the global section (``[mypy]``). -``show_error_context`` (bool, default False) +.. confval:: show_error_context + + :type: boolean + :default: False + Prefixes each error with the relevant context. -``show_column_numbers`` (bool, default False) +.. confval:: show_column_numbers + + :type: boolean + :default: False + Shows column numbers in error messages. -``show_error_codes`` (bool, default False) +.. confval:: show_error_codes + + :type: boolean + :default: False + Shows error codes in error messages. See :ref:`error-codes` for more information. -``pretty`` (bool, default False) +.. confval:: pretty + + :type: boolean + :default: False + Use visually nicer output in error messages: use soft word wrap, show source code snippets, and show error location markers. -``color_output`` (bool, default True) +.. confval:: color_output + + :type: boolean + :default: True + Shows error messages with color enabled. -``error_summary`` (bool, default True) +.. confval:: error_summary + + :type: boolean + :default: True + Shows a short summary line after error messages. -``show_absolute_path`` (bool, default False) +.. confval:: show_absolute_path + + :type: boolean + :default: False + Show absolute paths to files. @@ -452,10 +622,18 @@ Incremental mode These options may only be set in the global section (``[mypy]``). -``incremental`` (bool, default True) +.. confval:: incremental + + :type: boolean + :default: True + Enables :ref:`incremental mode `. -``cache_dir`` (string, default ``.mypy_cache``) +.. confval:: cache_dir + + :type: string + :default: ``.mypy_cache`` + Specifies the location where mypy stores incremental cache info. User home directory and environment variables will be expanded. This setting will be overridden by the ``MYPY_CACHE_DIR`` environment @@ -465,18 +643,34 @@ These options may only be set in the global section (``[mypy]``). but is always written to, unless the value is set to ``/dev/null`` (UNIX) or ``nul`` (Windows). -``sqlite_cache`` (bool, default False) +.. confval:: sqlite_cache + + :type: boolean + :default: False + Use an `SQLite`_ database to store the cache. -``cache_fine_grained`` (bool, default False) +.. confval:: cache_fine_grained + + :type: boolean + :default: False + Include fine-grained dependency information in the cache for the mypy daemon. -``skip_version_check`` (bool, default False) +.. confval:: skip_version_check + + :type: boolean + :default: False + Makes mypy use incremental cache data even if it was generated by a different version of mypy. (By default, mypy will perform a version check and regenerate the cache if it was written by older versions of mypy.) -``skip_cache_mtime_checks`` (bool, default False) +.. confval:: skip_cache_mtime_checks + + :type: boolean + :default: False + Skip cache internal consistency checks based on mtime. @@ -485,26 +679,48 @@ Advanced options These options may only be set in the global section (``[mypy]``). -``pdb`` (bool, default False) +.. confval:: pdb + + :type: boolean + :default: False + Invokes pdb on fatal error. -``show_traceback`` (bool, default False) +.. confval:: show_traceback + + :type: boolean + :default: False + Shows traceback on fatal error. -``raise_exceptions`` (bool, default False) +.. confval:: raise_exceptions + + :type: boolean + :default: False + Raise exception on fatal error. -``custom_typing_module`` (string) +.. confval:: custom_typing_module + + :type: string + Specifies a custom module to use as a substitute for the :py:mod:`typing` module. -``custom_typeshed_dir`` (string) +.. confval:: custom_typeshed_dir + + :type: string + Specifies an alternative directory to look for stubs instead of the default ``typeshed`` directory. User home directory and environment variables will be expanded. -``warn_incomplete_stub`` (bool, default False) +.. confval:: warn_incomplete_stub + + :type: boolean + :default: False + Warns about missing type annotations in typeshed. This is only relevant - in combination with ``disallow_untyped_defs`` or ``disallow_incomplete_defs``. + in combination with :confval:`disallow_untyped_defs` or :confval:`disallow_incomplete_defs`. Report generation @@ -513,39 +729,63 @@ Report generation If these options are set, mypy will generate a report in the specified format into the specified directory. -``any_exprs_report`` (string) +.. confval:: any_exprs_report + + :type: string + Causes mypy to generate a text file report documenting how many expressions of type ``Any`` are present within your codebase. -``cobertura_xml_report`` (string) +.. confval:: cobertura_xml_report + + :type: string + Causes mypy to generate a Cobertura XML type checking coverage report. You must install the `lxml`_ library to generate this report. -``html_report`` / ``xslt_html_report`` (string) +.. confval:: html_report / xslt_html_report + + :type: string + Causes mypy to generate an HTML type checking coverage report. You must install the `lxml`_ library to generate this report. -``linecount_report`` (string) +.. confval:: linecount_report + + :type: string + Causes mypy to generate a text file report documenting the functions and lines that are typed and untyped within your codebase. -``linecoverage_report`` (string) +.. confval:: linecoverage_report + + :type: string + Causes mypy to generate a JSON file that maps each source file's absolute filename to a list of line numbers that belong to typed functions in that file. -``lineprecision_report`` (string) +.. confval:: lineprecision_report + + :type: string + Causes mypy to generate a flat text file report with per-module statistics of how many lines are typechecked etc. -``txt_report`` / ``xslt_txt_report`` (string) +.. confval:: txt_report / xslt_txt_report + + :type: string + Causes mypy to generate a text file type checking coverage report. You must install the `lxml`_ library to generate this report. -``xml_report`` (string) +.. confval:: xml_report + + :type: string + Causes mypy to generate an XML type checking coverage report. You must install the `lxml`_ library to generate this report. @@ -556,21 +796,36 @@ Miscellaneous These options may only be set in the global section (``[mypy]``). -``junit_xml`` (string) +.. confval:: junit_xml + + :type: string + Causes mypy to generate a JUnit XML test result document with type checking results. This can make it easier to integrate mypy with continuous integration (CI) tools. -``scripts_are_modules`` (bool, default False) +.. confval:: scripts_are_modules + + :type: boolean + :default: False + Makes script ``x`` become module ``x`` instead of ``__main__``. This is useful when checking multiple scripts in a single run. -``warn_unused_configs`` (bool, default False) +.. confval:: warn_unused_configs + + :type: boolean + :default: False + Warns about per-module sections in the config file that do not match any files processed when invoking mypy. - (This requires turning off incremental mode using ``incremental = False``.) + (This requires turning off incremental mode using :confval:`incremental = False `.) + +.. confval:: verbosity + + :type: integer + :default: 0 -``verbosity`` (integer, default 0) Controls how much debug output will be generated. Higher numbers are more verbose. .. _lxml: https://pypi.org/project/lxml/ From ea9291de044ee8930abbc58fded23fe413b91210 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Fri, 31 Jul 2020 11:09:50 -0400 Subject: [PATCH 086/138] Fix type expected by stubgenc (#8888) In investigating a crash when running on a PyBind11 module, I noticed a mismatch in the types; infer_prop_type_from_docstring can clearly get None, but it has a type of plain str. Fix the annotation to match the expectation. Now, when rebuilding Mypy, the error goes away and this now works without crashing, and can completely generate stubs. --- mypy/stubdoc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index d2fd85914009..d8c5426a5315 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -339,7 +339,7 @@ def find_unique_signatures(sigs: Sequence[Sig]) -> List[Sig]: return sorted(result) -def infer_prop_type_from_docstring(docstr: str) -> Optional[str]: +def infer_prop_type_from_docstring(docstr: Optional[str]) -> Optional[str]: """Check for Google/Numpy style docstring type annotation for a property. The docstring has the format ": ". From cf52cfe57a91ff54296924f0aef7447dcd4a37ce Mon Sep 17 00:00:00 2001 From: Antoine Prouvost Date: Fri, 31 Jul 2020 11:15:46 -0400 Subject: [PATCH 087/138] Improve stubgenc property type detection (#8999) Close #8995. There are two things at play here. * Read the docstring signature in the first line of C type properties; * Read the docstring on property.fget if the type cannot be inferred from the property doc itself. The solution is a bit tailored to PyBind, in particular it can read signature that do not have names like (self: TestType) -> PropType. --- mypy/stubdoc.py | 10 +++++++++- mypy/stubgenc.py | 22 +++++++++++++++++----- mypy/test/teststubgen.py | 26 +++++++++++++++++++++----- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index d8c5426a5315..1baaaecfbdc8 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -239,7 +239,7 @@ def is_unique_args(sig: FunctionSig) -> bool: return [sig for sig in sigs if is_unique_args(sig)] -def infer_arg_sig_from_docstring(docstr: str) -> List[ArgSig]: +def infer_arg_sig_from_anon_docstring(docstr: str) -> List[ArgSig]: """Convert signature in form of "(self: TestClass, arg0: str='ada')" to List[TypedArgList].""" ret = infer_sig_from_docstring("stub" + docstr, "stub") if ret: @@ -247,6 +247,14 @@ def infer_arg_sig_from_docstring(docstr: str) -> List[ArgSig]: return [] +def infer_ret_type_sig_from_anon_docstring(docstr: str) -> Optional[str]: + """Convert signature in form of "(self: TestClass, arg0) -> int" to their return type.""" + ret = infer_sig_from_docstring("stub" + docstr.strip(), "stub") + if ret: + return ret[0].ret_type + return None + + def parse_signature(sig: str) -> Optional[Tuple[str, List[str], List[str]]]: diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index a9c87da7e95d..e3f29636bf9c 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -14,7 +14,7 @@ from mypy.moduleinspect import is_c_module from mypy.stubdoc import ( infer_sig_from_docstring, infer_prop_type_from_docstring, ArgSig, - infer_arg_sig_from_docstring, FunctionSig + infer_arg_sig_from_anon_docstring, infer_ret_type_sig_from_anon_docstring, FunctionSig ) @@ -144,7 +144,7 @@ def generate_c_function_stub(module: ModuleType, if (name in ('__new__', '__init__') and name not in sigs and class_name and class_name in class_sigs): inferred = [FunctionSig(name=name, - args=infer_arg_sig_from_docstring(class_sigs[class_name]), + args=infer_arg_sig_from_anon_docstring(class_sigs[class_name]), ret_type=ret_type)] # type: Optional[List[FunctionSig]] else: docstr = getattr(obj, '__doc__', None) @@ -154,7 +154,7 @@ def generate_c_function_stub(module: ModuleType, inferred = [FunctionSig(name, args=infer_method_sig(name), ret_type=ret_type)] else: inferred = [FunctionSig(name=name, - args=infer_arg_sig_from_docstring( + args=infer_arg_sig_from_anon_docstring( sigs.get(name, '(*args, **kwargs)')), ret_type=ret_type)] @@ -217,8 +217,20 @@ def generate_c_property_stub(name: str, obj: object, output: List[str], readonly Try to infer type from docstring, append resulting lines to 'output'. """ - docstr = getattr(obj, '__doc__', None) - inferred = infer_prop_type_from_docstring(docstr) + def infer_prop_type(docstr: Optional[str]) -> Optional[str]: + """Infer property type from docstring or docstring signature.""" + if docstr is not None: + inferred = infer_ret_type_sig_from_anon_docstring(docstr) + if not inferred: + inferred = infer_prop_type_from_docstring(docstr) + return inferred + else: + return None + + inferred = infer_prop_type(getattr(obj, '__doc__', None)) + if not inferred: + fget = getattr(obj, 'fget', None) + inferred = infer_prop_type(getattr(fget, '__doc__', None)) if not inferred: inferred = 'Any' diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index e77c83070bfd..3566f03fb9a1 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -19,11 +19,13 @@ mypy_options, is_blacklisted_path, is_non_library_module ) from mypy.stubutil import walk_packages, remove_misplaced_type_comments, common_dir_prefix -from mypy.stubgenc import generate_c_type_stub, infer_method_sig, generate_c_function_stub +from mypy.stubgenc import ( + generate_c_type_stub, infer_method_sig, generate_c_function_stub, generate_c_property_stub +) from mypy.stubdoc import ( parse_signature, parse_all_signatures, build_signature, find_unique_signatures, infer_sig_from_docstring, infer_prop_type_from_docstring, FunctionSig, ArgSig, - infer_arg_sig_from_docstring, is_valid_type + infer_arg_sig_from_anon_docstring, is_valid_type ) from mypy.moduleinspect import ModuleInspect, InspectError @@ -270,12 +272,12 @@ def test_infer_sig_from_docstring_bad_indentation(self) -> None: x """, 'func'), None) - def test_infer_arg_sig_from_docstring(self) -> None: - assert_equal(infer_arg_sig_from_docstring("(*args, **kwargs)"), + def test_infer_arg_sig_from_anon_docstring(self) -> None: + assert_equal(infer_arg_sig_from_anon_docstring("(*args, **kwargs)"), [ArgSig(name='*args'), ArgSig(name='**kwargs')]) assert_equal( - infer_arg_sig_from_docstring( + infer_arg_sig_from_anon_docstring( "(x: Tuple[int, Tuple[str, int], str]=(1, ('a', 2), 'y'), y: int=4)"), [ArgSig(name='x', type='Tuple[int,Tuple[str,int],str]', default=True), ArgSig(name='y', type='int', default=True)]) @@ -778,6 +780,20 @@ def test(arg0: str) -> None: assert_equal(output, ['def test(arg0: str) -> Action: ...']) assert_equal(imports, []) + def test_generate_c_property_with_pybind11(self) -> None: + """Signatures included by PyBind11 inside property.fget are read.""" + class TestClass: + def get_attribute(self) -> None: + """ + (self: TestClass) -> str + """ + pass + attribute = property(get_attribute, doc="") + + output = [] # type: List[str] + generate_c_property_stub('attribute', TestClass.attribute, output, readonly=True) + assert_equal(output, ['@property', 'def attribute(self) -> str: ...']) + def test_generate_c_type_with_overload_pybind11(self) -> None: class TestClass: def __init__(self, arg0: str) -> None: From f049560c7b80884b1c361948cafdf972e3f30ef8 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 31 Jul 2020 16:19:44 +0100 Subject: [PATCH 088/138] Sync typeshed (#9235) --- mypy/typeshed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/typeshed b/mypy/typeshed index fe58699ca5c9..8bf7efe94e1f 160000 --- a/mypy/typeshed +++ b/mypy/typeshed @@ -1 +1 @@ -Subproject commit fe58699ca5c9ee4838378adb88aaf9323e9bbcf0 +Subproject commit 8bf7efe94e1fc0f43aaf352b3c2260bd24073f5c From da4430119255ac9205c96d54deb2e2ebed0ce8ce Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Fri, 31 Jul 2020 09:58:15 -0700 Subject: [PATCH 089/138] mypyc: ignore deprecation (#9107) PyUnicode_AsUnicodeAndSize has been deprecated since 3.3 Python 3.9 has compiler warnings for this deprecated function, and we compile with Werror, causing Python 3.9 builds to fail. I've just copied over the relevant deprecation ignoring code from the original getargs.c (including the TODO, but I can remove that) Co-authored-by: hauntsaninja <> --- mypyc/lib-rt/getargs.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/mypyc/lib-rt/getargs.c b/mypyc/lib-rt/getargs.c index 32b387c8ab7b..e6b1a0c93705 100644 --- a/mypyc/lib-rt/getargs.c +++ b/mypyc/lib-rt/getargs.c @@ -18,6 +18,29 @@ * and is responsible for decrefing them. */ +// These macro definitions are copied from pyport.h in Python 3.9 and later +// https://bugs.python.org/issue19569 +#if defined(__clang__) +#define _Py_COMP_DIAG_PUSH _Pragma("clang diagnostic push") +#define _Py_COMP_DIAG_IGNORE_DEPR_DECLS \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#define _Py_COMP_DIAG_POP _Pragma("clang diagnostic pop") +#elif defined(__GNUC__) \ + && ((__GNUC__ >= 5) || (__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)) +#define _Py_COMP_DIAG_PUSH _Pragma("GCC diagnostic push") +#define _Py_COMP_DIAG_IGNORE_DEPR_DECLS \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#define _Py_COMP_DIAG_POP _Pragma("GCC diagnostic pop") +#elif defined(_MSC_VER) +#define _Py_COMP_DIAG_PUSH __pragma(warning(push)) +#define _Py_COMP_DIAG_IGNORE_DEPR_DECLS __pragma(warning(disable: 4996)) +#define _Py_COMP_DIAG_POP __pragma(warning(pop)) +#else +#define _Py_COMP_DIAG_PUSH +#define _Py_COMP_DIAG_IGNORE_DEPR_DECLS +#define _Py_COMP_DIAG_POP +#endif + #include "Python.h" #include "pythonsupport.h" @@ -756,6 +779,9 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, case 'u': /* raw unicode buffer (Py_UNICODE *) */ case 'Z': /* raw unicode buffer or None */ { + // TODO: Raise DeprecationWarning +_Py_COMP_DIAG_PUSH +_Py_COMP_DIAG_IGNORE_DEPR_DECLS Py_UNICODE **p = va_arg(*p_va, Py_UNICODE **); if (*format == '#') { @@ -795,6 +821,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, arg, msgbuf, bufsize); } break; +_Py_COMP_DIAG_POP } case 'e': {/* encoded string */ From 12855d4edd6051b03cb528ce2719ac49eac65c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oleg=20H=C3=B6fling?= Date: Sat, 1 Aug 2020 21:54:22 +0100 Subject: [PATCH 090/138] added crossrefs to config values, added missing plugins config value, documented help option for stubgen (#9241) Signed-off-by: oleg.hoefling --- docs/source/command_line.rst | 3 +-- docs/source/common_issues.rst | 4 ++-- docs/source/config_file.rst | 8 +++++++- docs/source/extending_mypy.rst | 5 ++++- docs/source/kinds_of_types.rst | 4 ++-- docs/source/running_mypy.rst | 26 +++++++++++++------------- docs/source/stubgen.rst | 8 ++++++-- 7 files changed, 35 insertions(+), 23 deletions(-) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 9cd8ed6cd900..10760f418026 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -108,8 +108,7 @@ imports. prefers "classic" packages over namespace packages) along the module search path -- this is primarily set from the source files passed on the command line, the ``MYPYPATH`` environment variable, - and the :ref:`mypy_path config option - `. + and the :confval:`mypy_path` config option. Note that this only affects import discovery -- for modules and packages explicitly passed on the command line, mypy still diff --git a/docs/source/common_issues.rst b/docs/source/common_issues.rst index 8b326408abc6..3867e168bd6a 100644 --- a/docs/source/common_issues.rst +++ b/docs/source/common_issues.rst @@ -454,8 +454,8 @@ whose name is passed to :option:`--always-true ` or :option: check to a variable. This may change in future versions of mypy. By default, mypy will use your current version of Python and your current -operating system as default values for ``sys.version_info`` and -``sys.platform``. +operating system as default values for :py:data:`sys.version_info` and +:py:data:`sys.platform`. To target a different Python version, use the :option:`--python-version X.Y ` flag. For example, to verify your code typechecks if were run using Python 2, pass diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index 6e9593716924..f45eceacbe67 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -679,12 +679,18 @@ Advanced options These options may only be set in the global section (``[mypy]``). +.. confval:: plugins + + :type: comma-separated list of strings + + A comma-separated list of mypy plugins. See :ref:`extending-mypy-using-plugins`. + .. confval:: pdb :type: boolean :default: False - Invokes pdb on fatal error. + Invokes :mod:`pdb` on fatal error. .. confval:: show_traceback diff --git a/docs/source/extending_mypy.rst b/docs/source/extending_mypy.rst index 679c9b24e5b8..43d16491f1f1 100644 --- a/docs/source/extending_mypy.rst +++ b/docs/source/extending_mypy.rst @@ -37,6 +37,9 @@ A trivial example of using the api is the following print('\nExit status:', result[2]) + +.. _extending-mypy-using-plugins: + Extending mypy using plugins **************************** @@ -69,7 +72,7 @@ Configuring mypy to use plugins ******************************* Plugins are Python files that can be specified in a mypy -:ref:`config file ` using one of the two formats: relative or +:ref:`config file ` using the :confval:`plugins` option and one of the two formats: relative or absolute path to the plugin file, or a module name (if the plugin is installed using ``pip install`` in the same virtual environment where mypy is running). The two formats can be mixed, for example: diff --git a/docs/source/kinds_of_types.rst b/docs/source/kinds_of_types.rst index acb8a3edff72..ea78ca938a1f 100644 --- a/docs/source/kinds_of_types.rst +++ b/docs/source/kinds_of_types.rst @@ -437,8 +437,8 @@ this example -- it's not recommended if you can avoid it: However, making code "optional clean" can take some work! You can also use :ref:`the mypy configuration file ` to migrate your code to strict optional checking one file at a time, since there exists -the :ref:`per-module flag ` -``strict_optional`` to control strict optional mode. +the per-module flag +:confval:`strict_optional` to control strict optional mode. Often it's still useful to document whether a variable can be ``None``. For example, this function accepts a ``None`` argument, diff --git a/docs/source/running_mypy.rst b/docs/source/running_mypy.rst index 69690afe906e..028f50d9862a 100644 --- a/docs/source/running_mypy.rst +++ b/docs/source/running_mypy.rst @@ -200,8 +200,8 @@ If you are getting this error, try: 3. :ref:`Writing your own stub files ` containing type hints for the library. You can point mypy at your type hints either by passing - them in via the command line, by using the ``files`` or ``mypy_path`` - :ref:`config file options `, or by + them in via the command line, by using the :confval:`files` or :confval:`mypy_path` + config file options, or by adding the location to the ``MYPYPATH`` environment variable. These stub files do not need to be complete! A good strategy is to use @@ -223,7 +223,7 @@ will continue to be of type ``Any``. 2. To suppress *all* missing import imports errors from a single library, add a section to your :ref:`mypy config file ` for that library setting - ``ignore_missing_imports`` to True. For example, suppose your codebase + :confval:`ignore_missing_imports` to True. For example, suppose your codebase makes heavy use of an (untyped) library named ``foobar``. You can silence all import errors associated with that library and that library alone by adding the following section to your config file:: @@ -240,8 +240,8 @@ will continue to be of type ``Any``. 3. To suppress *all* missing import errors for *all* libraries in your codebase, invoke mypy with the :option:`--ignore-missing-imports ` command line flag or set - the ``ignore_missing_imports`` - :ref:`config file option ` to True + the :confval:`ignore_missing_imports` + config file option to True in the *global* section of your mypy config file:: [mypy] @@ -275,8 +275,8 @@ this error, try: how you're invoking mypy accordingly. 3. Directly specifying the directory containing the module you want to - type check from the command line, by using the ``files`` or - ``mypy_path`` :ref:`config file options `, + type check from the command line, by using the :confval:`files` or + :confval:`mypy_path` config file options, or by using the ``MYPYPATH`` environment variable. Note: if the module you are trying to import is actually a *submodule* of @@ -309,7 +309,7 @@ even if the imported module is not a file you explicitly wanted mypy to check. For example, suppose we have two modules ``mycode.foo`` and ``mycode.bar``: the former has type hints and the latter does not. We run -``mypy -m mycode.foo`` and mypy discovers that ``mycode.foo`` imports +:option:`mypy -m mycode.foo ` and mypy discovers that ``mycode.foo`` imports ``mycode.bar``. How do we want mypy to type check ``mycode.bar``? We can configure the @@ -426,7 +426,7 @@ This is computed from the following items: - The ``MYPYPATH`` environment variable (a colon-separated list of directories). -- The ``mypy_path`` :ref:`config file option `. +- The :confval:`mypy_path` config file option. - The directories containing the sources given on the command line (see below). - The installed packages marked as safe for type checking (see @@ -470,15 +470,15 @@ Other advice and best practices ******************************* There are multiple ways of telling mypy what files to type check, ranging -from passing in command line arguments to using the ``files`` or ``mypy_path`` -:ref:`config file options ` to setting the +from passing in command line arguments to using the :confval:`files` or :confval:`mypy_path` +config file options to setting the ``MYPYPATH`` environment variable. However, in practice, it is usually sufficient to just use either -command line arguments or the ``files`` config file option (the two +command line arguments or the :confval:`files` config file option (the two are largely interchangeable). -Setting ``mypy_path``/``MYPYPATH`` is mostly useful in the case +Setting :confval:`mypy_path`/``MYPYPATH`` is mostly useful in the case where you want to try running mypy against multiple distinct sets of files that happen to share some common dependencies. diff --git a/docs/source/stubgen.rst b/docs/source/stubgen.rst index 38cd7b835b99..a58a022e6c67 100644 --- a/docs/source/stubgen.rst +++ b/docs/source/stubgen.rst @@ -53,7 +53,7 @@ The stubs will be much more useful if you add more precise type annotations, at least for the most commonly used functionality. The rest of this section documents the command line interface of stubgen. -Run ``stubgen --help`` for a quick summary of options. +Run :option:`stubgen --help` for a quick summary of options. .. note:: @@ -76,7 +76,7 @@ them for any ``.py`` files and generate stubs for all of them:: $ stubgen my_pkg_dir Alternatively, you can give module or package names using the -``-m`` or ``-p`` options:: +:option:`-m` or :option:`-p` options:: $ stubgen -m foo -m bar -p my_pkg_dir @@ -143,6 +143,10 @@ alter the default behavior: Additional flags **************** +.. option:: -h, --help + + Show help message and exit. + .. option:: --py2 Run stubgen in Python 2 mode (the default is Python 3 mode). From e6615c97d32574f048a73e0638d68d2c3d7e3785 Mon Sep 17 00:00:00 2001 From: Ashley Whetter Date: Sat, 1 Aug 2020 20:32:26 -0700 Subject: [PATCH 091/138] stubgen can import Iterable and Iterator from typing (#9088) --- mypy/stubgenc.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/mypy/stubgenc.py b/mypy/stubgenc.py index e3f29636bf9c..72477a2ce300 100755 --- a/mypy/stubgenc.py +++ b/mypy/stubgenc.py @@ -18,6 +18,19 @@ ) +# Members of the typing module to consider for importing by default. +_DEFAULT_TYPING_IMPORTS = ( + 'Any' + 'Dict', + 'Iterable', + 'Iterator', + 'List', + 'Optional', + 'Tuple', + 'Union', +) + + def generate_stub_for_c_module(module_name: str, target: str, sigs: Optional[Dict[str, str]] = None, @@ -82,7 +95,7 @@ def generate_stub_for_c_module(module_name: str, def add_typing_import(output: List[str]) -> List[str]: """Add typing imports for collections/types that occur in the generated stub.""" names = [] - for name in ['Any', 'Union', 'Tuple', 'Optional', 'List', 'Dict']: + for name in _DEFAULT_TYPING_IMPORTS: if any(re.search(r'\b%s\b' % name, line) for line in output): names.append(name) if names: From a9c9a411bb4fc92078e8d9a25846d90adeb91d34 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sat, 1 Aug 2020 20:56:38 -0700 Subject: [PATCH 092/138] messages: clarify bytes repr errors (#9243) Resolves #9236 Co-authored-by: hauntsaninja <> --- mypy/checkstrformat.py | 14 ++++++++------ test-data/unit/check-errorcodes.test | 4 ++-- test-data/unit/check-expressions.test | 14 +++++++------- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/mypy/checkstrformat.py b/mypy/checkstrformat.py index f3081a2fa491..b9a2a4099e52 100644 --- a/mypy/checkstrformat.py +++ b/mypy/checkstrformat.py @@ -384,9 +384,10 @@ def perform_special_format_checks(self, spec: ConversionSpecifier, call: CallExp if self.chk.options.python_version >= (3, 0): if (has_type_component(actual_type, 'builtins.bytes') and not custom_special_method(actual_type, '__str__')): - self.msg.fail("On Python 3 '{}'.format(b'abc') produces \"b'abc'\";" - " use !r if this is a desired behavior", call, - code=codes.STR_BYTES_PY3) + self.msg.fail( + "On Python 3 '{}'.format(b'abc') produces \"b'abc'\", not 'abc'; " + "use '{!r}'.format(b'abc') if this is desired behavior", + call, code=codes.STR_BYTES_PY3) if spec.flags: numeric_types = UnionType([self.named_type('builtins.int'), self.named_type('builtins.float')]) @@ -843,9 +844,10 @@ def check_s_special_cases(self, expr: FormatStringExpr, typ: Type, context: Cont # Couple special cases for string formatting. if self.chk.options.python_version >= (3, 0): if has_type_component(typ, 'builtins.bytes'): - self.msg.fail("On Python 3 '%s' % b'abc' produces \"b'abc'\";" - " use %r if this is a desired behavior", context, - code=codes.STR_BYTES_PY3) + self.msg.fail( + "On Python 3 '%s' % b'abc' produces \"b'abc'\", not 'abc'; " + "use '%r' % b'abc' if this is desired behavior", + context, code=codes.STR_BYTES_PY3) if self.chk.options.python_version < (3, 0): if has_type_component(typ, 'builtins.unicode'): self.unicode_upcast = True diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index 1be5bd507aea..c325f568081d 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -617,8 +617,8 @@ def g() -> int: '%d' % 'no' # E: Incompatible types in string interpolation (expression has type "str", placeholder has type "Union[int, float, SupportsInt]") [str-format] '%d + %d' % (1, 2, 3) # E: Not all arguments converted during string formatting [str-format] -'{}'.format(b'abc') # E: On Python 3 '{}'.format(b'abc') produces "b'abc'"; use !r if this is a desired behavior [str-bytes-safe] -'%s' % b'abc' # E: On Python 3 '%s' % b'abc' produces "b'abc'"; use %r if this is a desired behavior [str-bytes-safe] +'{}'.format(b'abc') # E: On Python 3 '{}'.format(b'abc') produces "b'abc'", not 'abc'; use '{!r}'.format(b'abc') if this is desired behavior [str-bytes-safe] +'%s' % b'abc' # E: On Python 3 '%s' % b'abc' produces "b'abc'", not 'abc'; use '%r' % b'abc' if this is desired behavior [str-bytes-safe] [builtins fixtures/primitives.pyi] [typing fixtures/typing-medium.pyi] diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index 9d423d10806d..f5073e2d261a 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -1183,8 +1183,8 @@ xb: bytes xs: str '%s' % xs # OK -'%s' % xb # E: On Python 3 '%s' % b'abc' produces "b'abc'"; use %r if this is a desired behavior -'%(name)s' % {'name': b'value'} # E: On Python 3 '%s' % b'abc' produces "b'abc'"; use %r if this is a desired behavior +'%s' % xb # E: On Python 3 '%s' % b'abc' produces "b'abc'", not 'abc'; use '%r' % b'abc' if this is desired behavior +'%(name)s' % {'name': b'value'} # E: On Python 3 '%s' % b'abc' produces "b'abc'", not 'abc'; use '%r' % b'abc' if this is desired behavior [builtins fixtures/primitives.pyi] [case testStringInterpolationSBytesVsStrResultsPy2] @@ -1576,17 +1576,17 @@ N = NewType('N', bytes) n: N '{}'.format(a) -'{}'.format(b) # E: On Python 3 '{}'.format(b'abc') produces "b'abc'"; use !r if this is a desired behavior -'{}'.format(x) # E: On Python 3 '{}'.format(b'abc') produces "b'abc'"; use !r if this is a desired behavior -'{}'.format(n) # E: On Python 3 '{}'.format(b'abc') produces "b'abc'"; use !r if this is a desired behavior +'{}'.format(b) # E: On Python 3 '{}'.format(b'abc') produces "b'abc'", not 'abc'; use '{!r}'.format(b'abc') if this is desired behavior +'{}'.format(x) # E: On Python 3 '{}'.format(b'abc') produces "b'abc'", not 'abc'; use '{!r}'.format(b'abc') if this is desired behavior +'{}'.format(n) # E: On Python 3 '{}'.format(b'abc') produces "b'abc'", not 'abc'; use '{!r}'.format(b'abc') if this is desired behavior class C(Generic[B]): x: B def meth(self) -> None: - '{}'.format(self.x) # E: On Python 3 '{}'.format(b'abc') produces "b'abc'"; use !r if this is a desired behavior + '{}'.format(self.x) # E: On Python 3 '{}'.format(b'abc') produces "b'abc'", not 'abc'; use '{!r}'.format(b'abc') if this is desired behavior def func(x: A) -> A: - '{}'.format(x) # E: On Python 3 '{}'.format(b'abc') produces "b'abc'"; use !r if this is a desired behavior + '{}'.format(x) # E: On Python 3 '{}'.format(b'abc') produces "b'abc'", not 'abc'; use '{!r}'.format(b'abc') if this is desired behavior return x '{!r}'.format(b) From 20b46286a17185c25639b0181c50a174e05b4e11 Mon Sep 17 00:00:00 2001 From: Weiss Date: Sun, 2 Aug 2020 12:15:35 +0300 Subject: [PATCH 093/138] Fix crash when super outside of method is called (#9173) * Fix crash when super outside of method is called * Change error message * empty --- mypy/checkexpr.py | 4 ++++ mypy/message_registry.py | 1 + test-data/unit/check-super.test | 14 ++++++++++++++ test-data/unit/fixtures/__new__.pyi | 2 ++ 4 files changed, 21 insertions(+) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 5af114767357..aa371548127e 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3464,6 +3464,10 @@ def visit_super_expr(self, e: SuperExpr) -> Type: self.chk.fail(message_registry.SUPER_ARG_2_NOT_INSTANCE_OF_ARG_1, e) return AnyType(TypeOfAny.from_error) + if len(mro) == index + 1: + self.chk.fail(message_registry.TARGET_CLASS_HAS_NO_BASE_CLASS, e) + return AnyType(TypeOfAny.from_error) + for base in mro[index+1:]: if e.name in base.names or base == mro[-1]: if e.info and e.info.fallback_to_any and base == mro[-1]: diff --git a/mypy/message_registry.py b/mypy/message_registry.py index e7bc3f2e3bb0..b25f055bccf8 100644 --- a/mypy/message_registry.py +++ b/mypy/message_registry.py @@ -109,6 +109,7 @@ SUPER_POSITIONAL_ARGS_REQUIRED = '"super" only accepts positional arguments' # type: Final SUPER_ARG_2_NOT_INSTANCE_OF_ARG_1 = \ 'Argument 2 for "super" not an instance of argument 1' # type: Final +TARGET_CLASS_HAS_NO_BASE_CLASS = 'Target class has no base class' # type: Final SUPER_OUTSIDE_OF_METHOD_NOT_SUPPORTED = \ 'super() outside of a method is not supported' # type: Final SUPER_ENCLOSING_POSITIONAL_ARGS_REQUIRED = \ diff --git a/test-data/unit/check-super.test b/test-data/unit/check-super.test index 2cccfd3d6127..c5184df2e36f 100644 --- a/test-data/unit/check-super.test +++ b/test-data/unit/check-super.test @@ -280,6 +280,20 @@ class B(A): class C: a = super().whatever # E: super() outside of a method is not supported +[case testSuperWithObjectClassAsFirstArgument] +class A: + def f(self) -> None: + super(object, self).f() # E: Target class has no base class + +[case testSuperWithTypeVarAsFirstArgument] +from typing import TypeVar + +T = TypeVar('T') + +def f(obj: T) -> None: + super(obj.__class__, obj).f() # E: Target class has no base class +[builtins fixtures/__new__.pyi] + [case testSuperWithSingleArgument] class B: def f(self) -> None: pass diff --git a/test-data/unit/fixtures/__new__.pyi b/test-data/unit/fixtures/__new__.pyi index 7e31ee05bce4..bb4788df8fe9 100644 --- a/test-data/unit/fixtures/__new__.pyi +++ b/test-data/unit/fixtures/__new__.pyi @@ -5,6 +5,8 @@ from typing import Any class object: def __init__(self) -> None: pass + __class__ = object + def __new__(cls) -> Any: pass class type: From 5d2983dbb3f44a19e2e58feb067f82d5cc443071 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Mon, 3 Aug 2020 18:48:40 +0800 Subject: [PATCH 094/138] [mypyc] Fix optimization for 'for x in reversed(list)' (#9245) Closes mypyc/mypyc#730. --- mypyc/irbuild/for_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 65e6f97a7e89..9b5e24b97911 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -243,7 +243,7 @@ def make_for_loop_generator(builder: IRBuilder, if (expr.callee.fullname == 'builtins.reversed' and len(expr.args) == 1 and expr.arg_kinds == [ARG_POS] - and is_sequence_rprimitive(rtyp)): + and is_sequence_rprimitive(builder.node_type(expr.args[0]))): # Special case "for x in reversed()". expr_reg = builder.accept(expr.args[0]) target_type = builder.get_sequence_type(expr) From 4d42f0307554ca02edb407b993b82629acb2db18 Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Mon, 3 Aug 2020 03:58:58 -0700 Subject: [PATCH 095/138] Make TypeQuery and TypeTranslator not traits (#9249) This makes them usable by non-compiled plugins. This fixes the user-blocking issue in #9001. --- mypy/type_visitor.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/mypy/type_visitor.py b/mypy/type_visitor.py index 905f46a92576..934a4421362f 100644 --- a/mypy/type_visitor.py +++ b/mypy/type_visitor.py @@ -134,7 +134,6 @@ def visit_placeholder_type(self, t: PlaceholderType) -> T: pass -@trait class TypeTranslator(TypeVisitor[Type]): """Identity type transformation. @@ -242,7 +241,6 @@ def visit_type_alias_type(self, t: TypeAliasType) -> Type: pass -@trait class TypeQuery(SyntheticTypeVisitor[T]): """Visitor for performing queries of types. From da4099c71f9b4c16b1388e25e9f6089d368411dd Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Mon, 3 Aug 2020 04:04:36 -0700 Subject: [PATCH 096/138] Dynamically disallow instantiating traits/interpreted subclasses (#9248) This prevents a bunch of segfaults. Closes #9001. Closes #8360. It doesn't close either of them in a satisfactory way, though. Really they would like actual support, which I've opened as mypyc/mypyc#754. Related to mypyc/mypyc#655. (At some point there used to be working dynamic checks for at least one of these cases. Not sure when that broke.) --- mypyc/codegen/emitclass.py | 29 ++++++++++++++++++++++++++++- mypyc/test-data/run-traits.test | 26 ++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/mypyc/codegen/emitclass.py b/mypyc/codegen/emitclass.py index 7ec749341aff..fe9bd28f10a5 100644 --- a/mypyc/codegen/emitclass.py +++ b/mypyc/codegen/emitclass.py @@ -129,8 +129,10 @@ def generate_class(cl: ClassIR, module: str, emitter: Emitter) -> None: generate_full = not cl.is_trait and not cl.builtin_base needs_getseters = not cl.is_generated - if generate_full: + if not cl.builtin_base: fields['tp_new'] = new_name + + if generate_full: fields['tp_dealloc'] = '(destructor){}_dealloc'.format(name_prefix) fields['tp_traverse'] = '(traverseproc){}_traverse'.format(name_prefix) fields['tp_clear'] = '(inquiry){}_clear'.format(name_prefix) @@ -229,6 +231,10 @@ def emit_line() -> None: emit_line() generate_getseters_table(cl, getseters_name, emitter) emit_line() + + if cl.is_trait: + generate_new_for_trait(cl, new_name, emitter) + generate_methods_table(cl, methods_name, emitter) emit_line() @@ -545,6 +551,27 @@ def generate_new_for_class(cl: ClassIR, emitter.emit_line('}') +def generate_new_for_trait(cl: ClassIR, + func_name: str, + emitter: Emitter) -> None: + emitter.emit_line('static PyObject *') + emitter.emit_line( + '{}(PyTypeObject *type, PyObject *args, PyObject *kwds)'.format(func_name)) + emitter.emit_line('{') + emitter.emit_line('if (type != {}) {{'.format(emitter.type_struct_name(cl))) + emitter.emit_line( + 'PyErr_SetString(PyExc_TypeError, ' + '"interpreted classes cannot inherit from compiled traits");' + ) + emitter.emit_line('} else {') + emitter.emit_line( + 'PyErr_SetString(PyExc_TypeError, "traits may not be directly created");' + ) + emitter.emit_line('}') + emitter.emit_line('return NULL;') + emitter.emit_line('}') + + def generate_traverse_for_class(cl: ClassIR, func_name: str, emitter: Emitter) -> None: diff --git a/mypyc/test-data/run-traits.test b/mypyc/test-data/run-traits.test index 98520f490db1..d1410a3d1e86 100644 --- a/mypyc/test-data/run-traits.test +++ b/mypyc/test-data/run-traits.test @@ -383,3 +383,29 @@ g(c1, c2) assert c1.x == 1 assert c2.x == 2 [out] + +[case testTraitErrorMessages] +from mypy_extensions import trait + +@trait +class Trait: + pass + +def create() -> Trait: + return Trait() + +[file driver.py] +from native import Trait, create +from testutil import assertRaises + +with assertRaises(TypeError, "traits may not be directly created"): + Trait() + +with assertRaises(TypeError, "traits may not be directly created"): + create() + +class Sub(Trait): + pass + +with assertRaises(TypeError, "interpreted classes cannot inherit from compiled traits"): + Sub() From 274a9e8d7dc6739cf9d64bd8b3186909072b7f3d Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Mon, 3 Aug 2020 20:45:16 +0800 Subject: [PATCH 097/138] [mypyc] Allow extra integer constant as the last argument to a C call (#9251) Related to mypyc/mypyc#734 and mypyc/mypyc#753. --- mypyc/irbuild/ll_builder.py | 5 +++++ mypyc/primitives/generic_ops.py | 30 +++++++++++++++------------- mypyc/primitives/registry.py | 24 ++++++++++++++++------ mypyc/test-data/irbuild-classes.test | 8 ++++---- mypyc/test/test_emitfunc.py | 4 ++-- 5 files changed, 45 insertions(+), 26 deletions(-) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 38b18d80f7e9..a3b6652c4d3f 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -761,6 +761,11 @@ def call_c(self, arg = args[i] arg = self.coerce(arg, desc.var_arg_type, line) coerced.append(arg) + # add extra integer constant if any + if desc.extra_int_constant is not None: + val, typ = desc.extra_int_constant + extra_int_constant = self.add(LoadInt(val, line, rtype=typ)) + coerced.append(extra_int_constant) target = self.add(CallC(desc.c_function_name, coerced, desc.return_type, desc.steals, desc.error_kind, line, var_arg_idx)) if desc.truncated_type is None: diff --git a/mypyc/primitives/generic_ops.py b/mypyc/primitives/generic_ops.py index 3acf99cd99de..e5d3e93bcab1 100644 --- a/mypyc/primitives/generic_ops.py +++ b/mypyc/primitives/generic_ops.py @@ -10,28 +10,30 @@ """ from mypyc.ir.ops import ERR_NEVER, ERR_MAGIC, ERR_FALSE -from mypyc.ir.rtypes import object_rprimitive, int_rprimitive, bool_rprimitive +from mypyc.ir.rtypes import object_rprimitive, int_rprimitive, bool_rprimitive, c_int_rprimitive from mypyc.primitives.registry import ( binary_op, unary_op, func_op, method_op, custom_op, call_emit, simple_emit, - call_negative_bool_emit, call_negative_magic_emit, negative_int_emit + call_negative_bool_emit, call_negative_magic_emit, negative_int_emit, + c_binary_op ) # Binary operations -for op, opid in [('==', 'Py_EQ'), - ('!=', 'Py_NE'), - ('<', 'Py_LT'), - ('<=', 'Py_LE'), - ('>', 'Py_GT'), - ('>=', 'Py_GE')]: +for op, opid in [('==', 2), # PY_EQ + ('!=', 3), # PY_NE + ('<', 0), # PY_LT + ('<=', 1), # PY_LE + ('>', 4), # PY_GT + ('>=', 5)]: # PY_GE # The result type is 'object' since that's what PyObject_RichCompare returns. - binary_op(op=op, - arg_types=[object_rprimitive, object_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=simple_emit('{dest} = PyObject_RichCompare({args[0]}, {args[1]}, %s);' % opid), - priority=0) + c_binary_op(name=op, + arg_types=[object_rprimitive, object_rprimitive], + return_type=object_rprimitive, + c_function_name='PyObject_RichCompare', + error_kind=ERR_MAGIC, + extra_int_constant=(opid, c_int_rprimitive), + priority=0) for op, funcname in [('+', 'PyNumber_Add'), ('-', 'PyNumber_Subtract'), diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index 726f3b28c5ad..16890889532a 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -35,7 +35,7 @@ optimized implementations of all ops. """ -from typing import Dict, List, Optional, NamedTuple +from typing import Dict, List, Optional, NamedTuple, Tuple from mypyc.ir.ops import ( OpDescription, EmitterInterface, EmitCallback, StealsDescription, short_name @@ -52,6 +52,7 @@ ('error_kind', int), ('steals', StealsDescription), ('ordering', Optional[List[int]]), + ('extra_int_constant', Optional[Tuple[int, RType]]), ('priority', int)]) # A description for C load operations including LoadGlobal and LoadAddress @@ -354,6 +355,7 @@ def c_method_op(name: str, var_arg_type: Optional[RType] = None, truncated_type: Optional[RType] = None, ordering: Optional[List[int]] = None, + extra_int_constant: Optional[Tuple[int, RType]] = None, steals: StealsDescription = False, priority: int = 1) -> CFunctionDescription: """Define a c function call op that replaces a method call. @@ -375,12 +377,14 @@ def c_method_op(name: str, should never be used together with var_arg_type. all the other arguments(such as arg_types) are in the order accepted by the python syntax(before reordering) + extra_int_constant: optional extra integer constant as the last argument to a C call steals: description of arguments that this steals (ref count wise) priority: if multiple ops match, the one with the highest priority is picked """ ops = c_method_call_ops.setdefault(name, []) desc = CFunctionDescription(name, arg_types, return_type, var_arg_type, truncated_type, - c_function_name, error_kind, steals, ordering, priority) + c_function_name, error_kind, steals, ordering, extra_int_constant, + priority) ops.append(desc) return desc @@ -393,6 +397,7 @@ def c_function_op(name: str, var_arg_type: Optional[RType] = None, truncated_type: Optional[RType] = None, ordering: Optional[List[int]] = None, + extra_int_constant: Optional[Tuple[int, RType]] = None, steals: StealsDescription = False, priority: int = 1) -> CFunctionDescription: """Define a c function call op that replaces a function call. @@ -407,7 +412,8 @@ def c_function_op(name: str, """ ops = c_function_ops.setdefault(name, []) desc = CFunctionDescription(name, arg_types, return_type, var_arg_type, truncated_type, - c_function_name, error_kind, steals, ordering, priority) + c_function_name, error_kind, steals, ordering, extra_int_constant, + priority) ops.append(desc) return desc @@ -420,6 +426,7 @@ def c_binary_op(name: str, var_arg_type: Optional[RType] = None, truncated_type: Optional[RType] = None, ordering: Optional[List[int]] = None, + extra_int_constant: Optional[Tuple[int, RType]] = None, steals: StealsDescription = False, priority: int = 1) -> CFunctionDescription: """Define a c function call op for a binary operation. @@ -431,7 +438,8 @@ def c_binary_op(name: str, """ ops = c_binary_ops.setdefault(name, []) desc = CFunctionDescription(name, arg_types, return_type, var_arg_type, truncated_type, - c_function_name, error_kind, steals, ordering, priority) + c_function_name, error_kind, steals, ordering, extra_int_constant, + priority) ops.append(desc) return desc @@ -443,13 +451,15 @@ def c_custom_op(arg_types: List[RType], var_arg_type: Optional[RType] = None, truncated_type: Optional[RType] = None, ordering: Optional[List[int]] = None, + extra_int_constant: Optional[Tuple[int, RType]] = None, steals: StealsDescription = False) -> CFunctionDescription: """Create a one-off CallC op that can't be automatically generated from the AST. Most arguments are similar to c_method_op(). """ return CFunctionDescription('', arg_types, return_type, var_arg_type, truncated_type, - c_function_name, error_kind, steals, ordering, 0) + c_function_name, error_kind, steals, ordering, + extra_int_constant, 0) def c_unary_op(name: str, @@ -459,6 +469,7 @@ def c_unary_op(name: str, error_kind: int, truncated_type: Optional[RType] = None, ordering: Optional[List[int]] = None, + extra_int_constant: Optional[Tuple[int, RType]] = None, steals: StealsDescription = False, priority: int = 1) -> CFunctionDescription: """Define a c function call op for an unary operation. @@ -470,7 +481,8 @@ def c_unary_op(name: str, """ ops = c_unary_ops.setdefault(name, []) desc = CFunctionDescription(name, [arg_type], return_type, None, truncated_type, - c_function_name, error_kind, steals, ordering, priority) + c_function_name, error_kind, steals, ordering, extra_int_constant, + priority) ops.append(desc) return desc diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index 70764a663df2..51e53f91a0dc 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -855,7 +855,7 @@ def f(a, b): r0 :: object r1 :: bool L0: - r0 = a == b + r0 = PyObject_RichCompare(a, b, 2) r1 = unbox(bool, r0) return r1 def f2(a, b): @@ -863,7 +863,7 @@ def f2(a, b): r0 :: object r1 :: bool L0: - r0 = a != b + r0 = PyObject_RichCompare(a, b, 3) r1 = unbox(bool, r0) return r1 def fOpt(a, b): @@ -908,7 +908,7 @@ def f(a, b): r0 :: object r1 :: bool L0: - r0 = a == b + r0 = PyObject_RichCompare(a, b, 2) r1 = unbox(bool, r0) return r1 def f2(a, b): @@ -916,7 +916,7 @@ def f2(a, b): r0 :: object r1 :: bool L0: - r0 = a != b + r0 = PyObject_RichCompare(a, b, 3) r1 = unbox(bool, r0) return r1 def fOpt(a, b): diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index 6107d6cee580..e83c604df850 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -339,7 +339,7 @@ def test_simple(self) -> None: fn = FuncIR(FuncDecl('myfunc', None, 'mod', FuncSignature([self.arg], int_rprimitive)), [self.block], self.env) emitter = Emitter(EmitterContext(NameGenerator([['mod']]))) - generate_native_function(fn, emitter, 'prog.py', 'prog', False) + generate_native_function(fn, emitter, 'prog.py', 'prog', optimize_int=False) result = emitter.fragments assert_string_arrays_equal( [ @@ -358,7 +358,7 @@ def test_register(self) -> None: fn = FuncIR(FuncDecl('myfunc', None, 'mod', FuncSignature([self.arg], list_rprimitive)), [self.block], self.env) emitter = Emitter(EmitterContext(NameGenerator([['mod']]))) - generate_native_function(fn, emitter, 'prog.py', 'prog', False) + generate_native_function(fn, emitter, 'prog.py', 'prog', optimize_int=False) result = emitter.fragments assert_string_arrays_equal( [ From 21e160e5310a4c1fc8375558cba4ffdc399dd96c Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 3 Aug 2020 05:49:43 -0700 Subject: [PATCH 098/138] Pass module-specific Options to fastparse.parse() (#9247) Before this, per-module flags that are handled in fastparse (in particular no_implicit_optional) were seeing the flag's global value. Fixes #9208. --- mypy/build.py | 8 +++++--- test-data/unit/check-flags.test | 29 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/mypy/build.py b/mypy/build.py index f372f3d087a1..8d84196af642 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -762,13 +762,14 @@ def is_module(self, id: str) -> bool: """Is there a file in the file system corresponding to module id?""" return find_module_simple(id, self) is not None - def parse_file(self, id: str, path: str, source: str, ignore_errors: bool) -> MypyFile: + def parse_file(self, id: str, path: str, source: str, ignore_errors: bool, + options: Options) -> MypyFile: """Parse the source of a file with the given name. Raise CompileError if there is a parse error. """ t0 = time.time() - tree = parse(source, path, id, self.errors, options=self.options) + tree = parse(source, path, id, self.errors, options=options) tree._fullname = id self.add_stats(files_parsed=1, modules_parsed=int(not tree.is_stub), @@ -2001,7 +2002,8 @@ def parse_file(self) -> None: self.parse_inline_configuration(source) self.tree = manager.parse_file(self.id, self.xpath, source, - self.ignore_all or self.options.ignore_errors) + self.ignore_all or self.options.ignore_errors, + self.options) modules[self.id] = self.tree diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 37b2c2a92e68..0e23476c4d0e 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -1501,3 +1501,32 @@ class ShouldNotBeFine(x): ... # E: Class cannot subclass 'x' (has type 'Any') disallow_subclassing_any = True \[mypy-m] disallow_subclassing_any = False + +[case testNoImplicitOptionalPerModule] +# flags: --config-file tmp/mypy.ini +import m + +[file m.py] +def f(a: str = None) -> int: + return 0 + +[file mypy.ini] +\[mypy] +no_implicit_optional = True +\[mypy-m] +no_implicit_optional = False + +[case testNoImplicitOptionalPerModulePython2] +# flags: --config-file tmp/mypy.ini --python-version 2.7 +import m + +[file m.py] +def f(a = None): + # type: (str) -> int + return 0 + +[file mypy.ini] +\[mypy] +no_implicit_optional = True +\[mypy-m] +no_implicit_optional = False From fd995441f5258f4d383696b36724520d4018e739 Mon Sep 17 00:00:00 2001 From: Ethan Leba Date: Mon, 3 Aug 2020 16:33:44 -0400 Subject: [PATCH 099/138] Improve missing module error for subdirectories (#8927) Fixes #7405 --- mypy/modulefinder.py | 25 +++++++++++++++++++++++++ mypy/test/testcmdline.py | 22 +++++++++++++++++++++- test-data/unit/cmdline.test | 31 +++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/mypy/modulefinder.py b/mypy/modulefinder.py index 3ca1a8db1c30..dd801d213064 100644 --- a/mypy/modulefinder.py +++ b/mypy/modulefinder.py @@ -49,10 +49,18 @@ class ModuleNotFoundReason(Enum): # corresponding *-stubs package. FOUND_WITHOUT_TYPE_HINTS = 1 + # The module was not found in the current working directory, but + # was able to be found in the parent directory. + WRONG_WORKING_DIRECTORY = 2 + def error_message_templates(self) -> Tuple[str, str]: if self is ModuleNotFoundReason.NOT_FOUND: msg = "Cannot find implementation or library stub for module named '{}'" note = "See https://mypy.readthedocs.io/en/latest/running_mypy.html#missing-imports" + elif self is ModuleNotFoundReason.WRONG_WORKING_DIRECTORY: + msg = "Cannot find implementation or library stub for module named '{}'" + note = ("You may be running mypy in a subpackage, " + "mypy should be run on the package root") elif self is ModuleNotFoundReason.FOUND_WITHOUT_TYPE_HINTS: msg = "Skipping analyzing '{}': found module but no type hints or library stubs" note = "See https://mypy.readthedocs.io/en/latest/running_mypy.html#missing-imports" @@ -166,6 +174,9 @@ def find_module(self, id: str) -> ModuleSearchResult: """Return the path of the module source file or why it wasn't found.""" if id not in self.results: self.results[id] = self._find_module(id) + if (self.results[id] is ModuleNotFoundReason.NOT_FOUND + and self._can_find_module_in_parent_dir(id)): + self.results[id] = ModuleNotFoundReason.WRONG_WORKING_DIRECTORY return self.results[id] def _find_module_non_stub_helper(self, components: List[str], @@ -192,6 +203,20 @@ def _update_ns_ancestors(self, components: List[str], match: Tuple[str, bool]) - self.ns_ancestors[pkg_id] = path path = os.path.dirname(path) + def _can_find_module_in_parent_dir(self, id: str) -> bool: + """Test if a module can be found by checking the parent directories + of the current working directory. + """ + working_dir = os.getcwd() + parent_search = FindModuleCache(SearchPaths((), (), (), ())) + while any(file.endswith(("__init__.py", "__init__.pyi")) + for file in os.listdir(working_dir)): + working_dir = os.path.dirname(working_dir) + parent_search.search_paths = SearchPaths((working_dir,), (), (), ()) + if not isinstance(parent_search._find_module(id), ModuleNotFoundReason): + return True + return False + def _find_module(self, id: str) -> ModuleSearchResult: fscache = self.fscache diff --git a/mypy/test/testcmdline.py b/mypy/test/testcmdline.py index dbefd2893b57..8d6a0d1fdc96 100644 --- a/mypy/test/testcmdline.py +++ b/mypy/test/testcmdline.py @@ -10,6 +10,7 @@ import sys from typing import List +from typing import Optional from mypy.test.config import test_temp_dir, PREFIX from mypy.test.data import DataDrivenTestCase, DataSuite @@ -45,6 +46,7 @@ def test_python_cmdline(testcase: DataDrivenTestCase, step: int) -> None: for s in testcase.input: file.write('{}\n'.format(s)) args = parse_args(testcase.input[0]) + custom_cwd = parse_cwd(testcase.input[1]) if len(testcase.input) > 1 else None args.append('--show-traceback') args.append('--no-site-packages') if '--error-summary' not in args: @@ -56,7 +58,10 @@ def test_python_cmdline(testcase: DataDrivenTestCase, step: int) -> None: process = subprocess.Popen(fixed + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - cwd=test_temp_dir, + cwd=os.path.join( + test_temp_dir, + custom_cwd or "" + ), env=env) outb, errb = process.communicate() result = process.returncode @@ -112,3 +117,18 @@ def parse_args(line: str) -> List[str]: if not m: return [] # No args; mypy will spit out an error. return m.group(1).split() + + +def parse_cwd(line: str) -> Optional[str]: + """Parse the second line of the program for the command line. + + This should have the form + + # cwd: + + For example: + + # cwd: main/subdir + """ + m = re.match('# cwd: (.*)$', line) + return m.group(1) if m else None diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index c8fbb512da01..c388725eec84 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -486,6 +486,37 @@ ignore_missing_imports = True [out] main.py:2: note: Revealed type is 'Any' + +[case testFailedImportOnWrongCWD] +# cmd: mypy main.py +# cwd: main/subdir1/subdir2 +[file main/subdir1/subdir2/main.py] +import parent +import grandparent +import missing +[file main/subdir1/subdir2/__init__.py] +[file main/subdir1/parent.py] +[file main/subdir1/__init__.py] +[file main/grandparent.py] +[file main/__init__.py] +[out] +main.py:1: error: Cannot find implementation or library stub for module named 'parent' +main.py:1: note: You may be running mypy in a subpackage, mypy should be run on the package root +main.py:2: error: Cannot find implementation or library stub for module named 'grandparent' +main.py:3: error: Cannot find implementation or library stub for module named 'missing' +main.py:3: note: See https://mypy.readthedocs.io/en/latest/running_mypy.html#missing-imports + +[case testImportInParentButNoInit] +# cmd: mypy main.py +# cwd: main/not_a_package +[file main/not_a_package/main.py] +import needs_init +[file main/needs_init.py] +[file main/__init__.py] +[out] +main.py:1: error: Cannot find implementation or library stub for module named 'needs_init' +main.py:1: note: See https://mypy.readthedocs.io/en/latest/running_mypy.html#missing-imports + [case testConfigNoErrorForUnknownXFlagInSubsection] # cmd: mypy -c pass [file mypy.ini] From 5a9d022cee6ef51fe5e9eb534682ef8bc9a229f2 Mon Sep 17 00:00:00 2001 From: dhood Date: Tue, 4 Aug 2020 14:05:31 +1000 Subject: [PATCH 100/138] Fix internal error on list/dict comprehension with walrus operator in global scope (#9062) * Add tests for dicts using assignment expression * Confirm the symbol table retrieved is not None It may be None in the case of a list comprehension being declared in a class scope (not in a function of a class). This, however, is not valid python syntax. * Add tests for assignment expression in class scope --- mypy/semanal.py | 11 +++++- test-data/unit/check-python38.test | 56 ++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 24c9cb7a9e5f..c3b9f8e91817 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -4542,9 +4542,18 @@ def current_symbol_table(self, escape_comprehensions: bool = False) -> SymbolTab if self.is_func_scope(): assert self.locals[-1] is not None if escape_comprehensions: + assert len(self.locals) == len(self.is_comprehension_stack) + # Retrieve the symbol table from the enclosing non-comprehension scope. for i, is_comprehension in enumerate(reversed(self.is_comprehension_stack)): if not is_comprehension: - names = self.locals[-1 - i] + if i == len(self.locals) - 1: # The last iteration. + # The caller of the comprehension is in the global space. + names = self.globals + else: + names_candidate = self.locals[-1 - i] + assert names_candidate is not None, \ + "Escaping comprehension from invalid scope" + names = names_candidate break else: assert False, "Should have at least one non-comprehension scope" diff --git a/test-data/unit/check-python38.test b/test-data/unit/check-python38.test index 12a060525820..78d62ae43ba4 100644 --- a/test-data/unit/check-python38.test +++ b/test-data/unit/check-python38.test @@ -198,6 +198,27 @@ if a := 2: while b := "x": reveal_type(b) # N: Revealed type is 'builtins.str' +l = [y2 := 1, y2 + 2, y2 + 3] +reveal_type(y2) # N: Revealed type is 'builtins.int' +reveal_type(l) # N: Revealed type is 'builtins.list[builtins.int*]' + +filtered_data = [y3 for x in l if (y3 := a) is not None] +reveal_type(filtered_data) # N: Revealed type is 'builtins.list[builtins.int*]' +reveal_type(y3) # N: Revealed type is 'builtins.int' + +d = {'a': (a2 := 1), 'b': a2 + 1, 'c': a2 + 2} +reveal_type(d) # N: Revealed type is 'builtins.dict[builtins.str*, builtins.int*]' +reveal_type(a2) # N: Revealed type is 'builtins.int' + +d2 = {(prefix := 'key_') + 'a': (start_val := 1), prefix + 'b': start_val + 1, prefix + 'c': start_val + 2} +reveal_type(d2) # N: Revealed type is 'builtins.dict[builtins.str*, builtins.int*]' +reveal_type(prefix) # N: Revealed type is 'builtins.str' +reveal_type(start_val) # N: Revealed type is 'builtins.int' + +filtered_dict = {k: new_v for k, v in [('a', 1), ('b', 2), ('c', 3)] if (new_v := v + 1) == 2} +reveal_type(filtered_dict) # N: Revealed type is 'builtins.dict[builtins.str*, builtins.int*]' +reveal_type(new_v) # N: Revealed type is 'builtins.int' + def f(x: int = (c := 4)) -> int: if a := 2: reveal_type(a) # N: Revealed type is 'builtins.int' @@ -218,6 +239,19 @@ def f(x: int = (c := 4)) -> int: reveal_type(filtered_data) # N: Revealed type is 'builtins.list[builtins.int*]' reveal_type(y3) # N: Revealed type is 'builtins.int' + d = {'a': (a2 := 1), 'b': a2 + 1, 'c': a2 + 2} + reveal_type(d) # N: Revealed type is 'builtins.dict[builtins.str*, builtins.int*]' + reveal_type(a2) # N: Revealed type is 'builtins.int' + + d2 = {(prefix := 'key_') + 'a': (start_val := 1), prefix + 'b': start_val + 1, prefix + 'c': start_val + 2} + reveal_type(d2) # N: Revealed type is 'builtins.dict[builtins.str*, builtins.int*]' + reveal_type(prefix) # N: Revealed type is 'builtins.str' + reveal_type(start_val) # N: Revealed type is 'builtins.int' + + filtered_dict = {k: new_v for k, v in [('a', 1), ('b', 2), ('c', 3)] if (new_v := v + 1) == 2} + reveal_type(filtered_dict) # N: Revealed type is 'builtins.dict[builtins.str*, builtins.int*]' + reveal_type(new_v) # N: Revealed type is 'builtins.int' + # https://www.python.org/dev/peps/pep-0572/#exceptional-cases (y4 := 3) reveal_type(y4) # N: Revealed type is 'builtins.int' @@ -304,6 +338,28 @@ def check_narrow(x: Optional[int], s: List[int]) -> None: if isinstance((y := x), int): reveal_type(y) # N: Revealed type is 'builtins.int' +class AssignmentExpressionsClass: + x = (y := 1) + (z := 2) + reveal_type(z) # N: Revealed type is 'builtins.int' + + l = [x2 := 1, 2, 3] + reveal_type(x2) # N: Revealed type is 'builtins.int' + + def __init__(self) -> None: + reveal_type(self.z) # N: Revealed type is 'builtins.int' + + l = [z2 := 1, z2 + 2, z2 + 3] + reveal_type(z2) # N: Revealed type is 'builtins.int' + reveal_type(l) # N: Revealed type is 'builtins.list[builtins.int*]' + + filtered_data = [z3 for x in l if (z3 := 1) is not None] + reveal_type(filtered_data) # N: Revealed type is 'builtins.list[builtins.int*]' + reveal_type(z3) # N: Revealed type is 'builtins.int' + +# Assignment expressions from inside the class should not escape the class scope. +reveal_type(x2) # E: Name 'x2' is not defined # N: Revealed type is 'Any' +reveal_type(z2) # E: Name 'z2' is not defined # N: Revealed type is 'Any' + [builtins fixtures/isinstancelist.pyi] [case testWalrusPartialTypes] From 7938f5d0025bf82c21bfe72b8bcb9add7cd58a77 Mon Sep 17 00:00:00 2001 From: ishaan Date: Tue, 4 Aug 2020 03:46:31 -0400 Subject: [PATCH 101/138] Adding config file for stubtest.py (#9203) --- mypy/stubtest.py | 20 +++++++++++++++++++ mypy/test/teststubtest.py | 25 ++++++++++++++++++++++-- test-data/unit/plugins/decimal_to_int.py | 18 +++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 test-data/unit/plugins/decimal_to_int.py diff --git a/mypy/stubtest.py b/mypy/stubtest.py index 535f049d9b2e..09eca8ff06b2 100644 --- a/mypy/stubtest.py +++ b/mypy/stubtest.py @@ -23,6 +23,7 @@ import mypy.modulefinder import mypy.types from mypy import nodes +from mypy.config_parser import parse_config_file from mypy.options import Options from mypy.util import FancyFormatter @@ -1040,6 +1041,12 @@ def test_stubs(args: argparse.Namespace) -> int: options = Options() options.incremental = False options.custom_typeshed_dir = args.custom_typeshed_dir + options.config_file = args.mypy_config_file + + if options.config_file: + def set_strict_flags() -> None: # not needed yet + return + parse_config_file(options, set_strict_flags, options.config_file, sys.stdout, sys.stderr) try: modules = build_stubs(modules, options, find_submodules=not args.check_typeshed) @@ -1133,6 +1140,19 @@ def parse_options(args: List[str]) -> argparse.Namespace: action="store_true", help="Ignore unused whitelist entries", ) + config_group = parser.add_argument_group( + title='mypy config file', + description="Use a config file instead of command line arguments. " + "Plugins and mypy path are the only supported " + "configurations.", + ) + config_group.add_argument( + '--mypy-config-file', + help=( + "An existing mypy configuration file, currently used by stubtest to help " + "determine mypy path and plugins" + ), + ) return parser.parse_args(args) diff --git a/mypy/test/teststubtest.py b/mypy/test/teststubtest.py index fe7c67261ded..50f417e920c8 100644 --- a/mypy/test/teststubtest.py +++ b/mypy/test/teststubtest.py @@ -11,6 +11,7 @@ import mypy.stubtest from mypy.stubtest import parse_options, test_stubs +from mypy.test.data import root_dir @contextlib.contextmanager @@ -27,13 +28,18 @@ def use_tmp_dir() -> Iterator[None]: TEST_MODULE_NAME = "test_module" -def run_stubtest(stub: str, runtime: str, options: List[str]) -> str: +def run_stubtest( + stub: str, runtime: str, options: List[str], config_file: Optional[str] = None, +) -> str: with use_tmp_dir(): with open("{}.pyi".format(TEST_MODULE_NAME), "w") as f: f.write(stub) with open("{}.py".format(TEST_MODULE_NAME), "w") as f: f.write(runtime) - + if config_file: + with open("{}_config.ini".format(TEST_MODULE_NAME), "w") as f: + f.write(config_file) + options = options + ["--mypy-config-file", "{}_config.ini".format(TEST_MODULE_NAME)] if sys.path[0] != ".": sys.path.insert(0, ".") if TEST_MODULE_NAME in sys.modules: @@ -753,3 +759,18 @@ class StubtestIntegration(unittest.TestCase): def test_typeshed(self) -> None: # check we don't crash while checking typeshed test_stubs(parse_options(["--check-typeshed"])) + + def test_config_file(self) -> None: + runtime = "temp = 5\n" + stub = "from decimal import Decimal\ntemp: Decimal\n" + config_file = ( + "[mypy]\n" + "plugins={}/test-data/unit/plugins/decimal_to_int.py\n".format(root_dir) + ) + output = run_stubtest(stub=stub, runtime=runtime, options=[]) + assert output == ( + "error: test_module.temp variable differs from runtime type Literal[5]\n" + "Stub: at line 2\ndecimal.Decimal\nRuntime:\n5\n\n" + ) + output = run_stubtest(stub=stub, runtime=runtime, options=[], config_file=config_file) + assert output == "" diff --git a/test-data/unit/plugins/decimal_to_int.py b/test-data/unit/plugins/decimal_to_int.py new file mode 100644 index 000000000000..98e747ed74c0 --- /dev/null +++ b/test-data/unit/plugins/decimal_to_int.py @@ -0,0 +1,18 @@ +import builtins +from typing import Optional, Callable + +from mypy.plugin import Plugin, AnalyzeTypeContext +from mypy.types import CallableType, Type + + +class MyPlugin(Plugin): + def get_type_analyze_hook(self, fullname): + if fullname == "decimal.Decimal": + return decimal_to_int_hook + return None + +def plugin(version): + return MyPlugin + +def decimal_to_int_hook(ctx): + return ctx.api.named_type('builtins.int', []) From ffd9d1cdff4af3b482d4dd1f871fd1dc5b39eebb Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Tue, 4 Aug 2020 23:48:03 +0800 Subject: [PATCH 102/138] [mypyc] Introduce GetElementPtr (#9260) Related to mypyc/mypyc#741. This PR introduces GetElementPtr, which computes the address of an element in an aggregate type (currently RStruct is supported). Part of efforts to support the len and other macro-related primitives. --- mypyc/analysis/dataflow.py | 5 ++++- mypyc/codegen/emitfunc.py | 12 ++++++++++-- mypyc/ir/ops.py | 29 ++++++++++++++++++++++++++++- mypyc/test/test_emitfunc.py | 15 +++++++++++++-- 4 files changed, 55 insertions(+), 6 deletions(-) diff --git a/mypyc/analysis/dataflow.py b/mypyc/analysis/dataflow.py index 75942cb04a34..90a2e4d20a31 100644 --- a/mypyc/analysis/dataflow.py +++ b/mypyc/analysis/dataflow.py @@ -9,7 +9,7 @@ BasicBlock, OpVisitor, Assign, LoadInt, LoadErrorValue, RegisterOp, Goto, Branch, Return, Call, Environment, Box, Unbox, Cast, Op, Unreachable, TupleGet, TupleSet, GetAttr, SetAttr, LoadStatic, InitStatic, PrimitiveOp, MethodCall, RaiseStandardError, CallC, LoadGlobal, - Truncate, BinaryIntOp, LoadMem + Truncate, BinaryIntOp, LoadMem, GetElementPtr ) @@ -211,6 +211,9 @@ def visit_binary_int_op(self, op: BinaryIntOp) -> GenAndKill: def visit_load_mem(self, op: LoadMem) -> GenAndKill: return self.visit_register_op(op) + def visit_get_element_ptr(self, op: GetElementPtr) -> GenAndKill: + return self.visit_register_op(op) + class DefinedVisitor(BaseAnalysisVisitor): """Visitor for finding defined registers. diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index 0b560839c0b9..0dc0dd96900c 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -12,10 +12,10 @@ LoadStatic, InitStatic, TupleGet, TupleSet, Call, IncRef, DecRef, Box, Cast, Unbox, BasicBlock, Value, MethodCall, PrimitiveOp, EmitterInterface, Unreachable, NAMESPACE_STATIC, NAMESPACE_TYPE, NAMESPACE_MODULE, RaiseStandardError, CallC, LoadGlobal, Truncate, - BinaryIntOp, LoadMem + BinaryIntOp, LoadMem, GetElementPtr ) from mypyc.ir.rtypes import ( - RType, RTuple, is_tagged, is_int32_rprimitive, is_int64_rprimitive + RType, RTuple, is_tagged, is_int32_rprimitive, is_int64_rprimitive, RStruct ) from mypyc.ir.func_ir import FuncIR, FuncDecl, FUNC_STATICMETHOD, FUNC_CLASSMETHOD from mypyc.ir.class_ir import ClassIR @@ -471,6 +471,14 @@ def visit_load_mem(self, op: LoadMem) -> None: type = self.ctype(op.type) self.emit_line('%s = *(%s *)%s;' % (dest, type, src)) + def visit_get_element_ptr(self, op: GetElementPtr) -> None: + dest = self.reg(op) + src = self.reg(op.src) + # TODO: support tuple type + assert isinstance(op.src_type, RStruct) + assert op.field in op.src_type.names, "Invalid field name." + self.emit_line('%s = &%s.%s;' % (dest, src, op.field)) + # Helpers def label(self, label: BasicBlock) -> str: diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 9b871ad6309c..12d7e32db230 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -25,7 +25,8 @@ from mypyc.ir.rtypes import ( RType, RInstance, RTuple, RVoid, is_bool_rprimitive, is_int_rprimitive, is_short_int_rprimitive, is_none_rprimitive, object_rprimitive, bool_rprimitive, - short_int_rprimitive, int_rprimitive, void_rtype, is_c_py_ssize_t_rprimitive + short_int_rprimitive, int_rprimitive, void_rtype, is_c_py_ssize_t_rprimitive, + c_pyssize_t_rprimitive ) from mypyc.common import short_name @@ -1372,6 +1373,28 @@ def accept(self, visitor: 'OpVisitor[T]') -> T: return visitor.visit_load_mem(self) +class GetElementPtr(RegisterOp): + """Get the address of a struct element""" + error_kind = ERR_NEVER + + def __init__(self, src: Value, src_type: RType, field: str, line: int = -1) -> None: + super().__init__(line) + self.type = c_pyssize_t_rprimitive + self.src = src + self.src_type = src_type + self.field = field + + def sources(self) -> List[Value]: + return [self.src] + + def to_str(self, env: Environment) -> str: + return env.format("%r = get_element_ptr %r %r :: %r", self, self.src, + self.field, self.src_type) + + def accept(self, visitor: 'OpVisitor[T]') -> T: + return visitor.visit_get_element_ptr(self) + + @trait class OpVisitor(Generic[T]): """Generic visitor over ops (uses the visitor design pattern).""" @@ -1482,6 +1505,10 @@ def visit_binary_int_op(self, op: BinaryIntOp) -> T: def visit_load_mem(self, op: LoadMem) -> T: raise NotImplementedError + @abstractmethod + def visit_get_element_ptr(self, op: GetElementPtr) -> T: + raise NotImplementedError + # TODO: Should this live somewhere else? LiteralsMap = Dict[Tuple[Type[object], Union[int, float, str, bytes, complex]], str] diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index e83c604df850..a2e821e9148b 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -10,12 +10,12 @@ from mypyc.ir.ops import ( Environment, BasicBlock, Goto, Return, LoadInt, Assign, IncRef, DecRef, Branch, Call, Unbox, Box, TupleGet, GetAttr, PrimitiveOp, RegisterOp, - SetAttr, Op, Value, CallC, BinaryIntOp, LoadMem + SetAttr, Op, Value, CallC, BinaryIntOp, LoadMem, GetElementPtr ) from mypyc.ir.rtypes import ( RTuple, RInstance, int_rprimitive, bool_rprimitive, list_rprimitive, dict_rprimitive, object_rprimitive, c_int_rprimitive, short_int_rprimitive, int32_rprimitive, - int64_rprimitive + int64_rprimitive, StructInfo, RStruct ) from mypyc.ir.func_ir import FuncIR, FuncDecl, RuntimeArg, FuncSignature from mypyc.ir.class_ir import ClassIR @@ -282,6 +282,17 @@ def test_load_mem(self) -> None: self.assert_emit(LoadMem(bool_rprimitive, self.i64), """cpy_r_r0 = *(char *)cpy_r_i64;""") + def test_get_element_ptr(self) -> None: + info = StructInfo("Foo", ["b", "i32", "i64"], [bool_rprimitive, + int32_rprimitive, int64_rprimitive]) + r = RStruct(info) + self.assert_emit(GetElementPtr(self.o, r, "b"), + """cpy_r_r0 = &cpy_r_o.b;""") + self.assert_emit(GetElementPtr(self.o, r, "i32"), + """cpy_r_r00 = &cpy_r_o.i32;""") + self.assert_emit(GetElementPtr(self.o, r, "i64"), + """cpy_r_r01 = &cpy_r_o.i64;""") + def assert_emit(self, op: Op, expected: str) -> None: self.emitter.fragments = [] self.declarations.fragments = [] From 3e77959eacf3d445a0cb4db5a4bc6dcf606fc040 Mon Sep 17 00:00:00 2001 From: Lawrence Chan Date: Tue, 4 Aug 2020 18:14:55 -0500 Subject: [PATCH 103/138] Use pytest Node.from_parent if available (#9263) * Use pytest Node.from_parent if available * Use pytest Node.from_parent unconditionally (requires pytest 5.4+) * Bump pytest test requirements * Require pytest 6.0 and remove unused type ignores * Make flake8 happy --- mypy/test/data.py | 37 ++++++++++++++++++++++-------------- mypy/test/helpers.py | 2 +- mypy/test/testfinegrained.py | 2 +- mypy/test/testipc.py | 2 +- mypy/test/testparse.py | 2 +- mypy/test/testpep561.py | 2 +- mypy/test/testpythoneval.py | 2 +- mypyc/test/testutil.py | 2 +- pytest.ini | 3 +-- test-requirements.txt | 9 ++++----- 10 files changed, 35 insertions(+), 28 deletions(-) diff --git a/mypy/test/data.py b/mypy/test/data.py index 5484fd99e944..a4f2d798b1b1 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -9,7 +9,7 @@ from abc import abstractmethod import sys -import pytest # type: ignore # no pytest in typeshed +import pytest from typing import List, Tuple, Set, Optional, Iterator, Any, Dict, NamedTuple, Union from mypy.test.config import test_data_prefix, test_temp_dir, PREFIX @@ -160,9 +160,12 @@ def parse_test_case(case: 'DataDrivenTestCase') -> None: case.expected_fine_grained_targets = targets -class DataDrivenTestCase(pytest.Item): # type: ignore # inheriting from Any +class DataDrivenTestCase(pytest.Item): """Holds parsed data-driven test cases, and handles directory setup and teardown.""" + # Override parent member type + parent = None # type: DataSuiteCollector + input = None # type: List[str] output = None # type: List[str] # Output for the first pass output2 = None # type: Dict[int, List[str]] # Output for runs 2+, indexed by run number @@ -266,7 +269,7 @@ def repr_failure(self, excinfo: Any, style: Optional[Any] = None) -> str: # call exit() and they already print out a stack trace. excrepr = excinfo.exconly() else: - self.parent._prunetraceback(excinfo) + self.parent._prunetraceback(excinfo) # type: ignore[no-untyped-call] excrepr = excinfo.getrepr(style='short') return "data: {}:{}:\n{}".format(self.file, self.line, excrepr) @@ -510,7 +513,9 @@ def pytest_pycollect_makeitem(collector: Any, name: str, # Non-None result means this obj is a test case. # The collect method of the returned DataSuiteCollector instance will be called later, # with self.obj being obj. - return DataSuiteCollector(name, parent=collector) + return DataSuiteCollector.from_parent( # type: ignore[no-untyped-call] + parent=collector, name=name + ) return None @@ -535,19 +540,23 @@ def split_test_cases(parent: 'DataSuiteCollector', suite: 'DataSuite', for i in range(1, len(cases), 6): name, writescache, only_when, platform_flag, skip, data = cases[i:i + 6] platform = platform_flag[1:] if platform_flag else None - yield DataDrivenTestCase(parent, suite, file, - name=add_test_name_suffix(name, suite.test_name_suffix), - writescache=bool(writescache), - only_when=only_when, - platform=platform, - skip=bool(skip), - data=data, - line=line_no) + yield DataDrivenTestCase.from_parent( + parent=parent, + suite=suite, + file=file, + name=add_test_name_suffix(name, suite.test_name_suffix), + writescache=bool(writescache), + only_when=only_when, + platform=platform, + skip=bool(skip), + data=data, + line=line_no, + ) line_no += data.count('\n') + 1 -class DataSuiteCollector(pytest.Class): # type: ignore # inheriting from Any - def collect(self) -> Iterator[pytest.Item]: # type: ignore +class DataSuiteCollector(pytest.Class): + def collect(self) -> Iterator[pytest.Item]: """Called by pytest on each of the object returned from pytest_pycollect_makeitem""" # obj is the object for which pytest_pycollect_makeitem returned self. diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index 46c01114c46c..91c5ff6ab2b4 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -10,7 +10,7 @@ from mypy import defaults import mypy.api as api -import pytest # type: ignore # no pytest in typeshed +import pytest # Exporting Suite as alias to TestCase for backwards compatibility # TODO: avoid aliasing - import and subclass TestCase directly diff --git a/mypy/test/testfinegrained.py b/mypy/test/testfinegrained.py index 596391da4474..d4ed18cab095 100644 --- a/mypy/test/testfinegrained.py +++ b/mypy/test/testfinegrained.py @@ -35,7 +35,7 @@ from mypy.config_parser import parse_config_file from mypy.find_sources import create_source_list -import pytest # type: ignore # no pytest in typeshed +import pytest # Set to True to perform (somewhat expensive) checks for duplicate AST nodes after merge CHECK_CONSISTENCY = False diff --git a/mypy/test/testipc.py b/mypy/test/testipc.py index 1d4829d56171..7dd829a59079 100644 --- a/mypy/test/testipc.py +++ b/mypy/test/testipc.py @@ -3,7 +3,7 @@ from mypy.ipc import IPCClient, IPCServer -import pytest # type: ignore +import pytest import sys import time diff --git a/mypy/test/testparse.py b/mypy/test/testparse.py index e990a403a52e..e9ff6839bc2c 100644 --- a/mypy/test/testparse.py +++ b/mypy/test/testparse.py @@ -2,7 +2,7 @@ import sys -from pytest import skip # type: ignore[import] +from pytest import skip from mypy import defaults from mypy.test.helpers import assert_string_arrays_equal, parse_options diff --git a/mypy/test/testpep561.py b/mypy/test/testpep561.py index a8eabd7702a1..aadf01ae5fa2 100644 --- a/mypy/test/testpep561.py +++ b/mypy/test/testpep561.py @@ -1,6 +1,6 @@ from contextlib import contextmanager import os -import pytest # type: ignore +import pytest import re import subprocess from subprocess import PIPE diff --git a/mypy/test/testpythoneval.py b/mypy/test/testpythoneval.py index 7586a3854eea..e7e9f1618388 100644 --- a/mypy/test/testpythoneval.py +++ b/mypy/test/testpythoneval.py @@ -18,7 +18,7 @@ import sys from tempfile import TemporaryDirectory -import pytest # type: ignore # no pytest in typeshed +import pytest from typing import List diff --git a/mypyc/test/testutil.py b/mypyc/test/testutil.py index 18ab39a103ad..c1ce8626badb 100644 --- a/mypyc/test/testutil.py +++ b/mypyc/test/testutil.py @@ -7,7 +7,7 @@ import shutil from typing import List, Callable, Iterator, Optional, Tuple -import pytest # type: ignore[import] +import pytest from mypy import build from mypy.errors import CompileError diff --git a/pytest.ini b/pytest.ini index 81586a23708f..ed76809091a1 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,6 +1,5 @@ [pytest] -# testpaths is new in 2.8 -minversion = 2.8 +minversion = 6.0.0 testpaths = mypy/test mypyc/test diff --git a/test-requirements.txt b/test-requirements.txt index 073b8cde5712..79eadca595de 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -5,11 +5,10 @@ flake8-bugbear; python_version >= '3.5' flake8-pyi>=20.5; python_version >= '3.6' lxml>=4.4.0 psutil>=4.0 -pytest==5.3.2 -pytest-xdist>=1.22 -# pytest-xdist depends on pytest-forked and 1.1.0 doesn't install clean on macOS 3.5 -pytest-forked>=1.0.0,<1.1.0 -pytest-cov>=2.4.0 +pytest>=6.0.0,<7.0.0 +pytest-xdist>=1.34.0,<2.0.0 +pytest-forked>=1.3.0,<2.0.0 +pytest-cov>=2.10.0,<3.0.0 typing>=3.5.2; python_version < '3.5' py>=1.5.2 virtualenv<20 From c5814e5cc9398a26ab01c14fadbcd688ac1674e2 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Wed, 5 Aug 2020 17:53:46 +0800 Subject: [PATCH 104/138] [mypyc] Merge most generic ops (#9258) This PR merges most of the remaining generic ops. --- mypyc/irbuild/builder.py | 10 +- mypyc/irbuild/classdef.py | 10 +- mypyc/irbuild/expression.py | 2 +- mypyc/irbuild/for_helpers.py | 4 +- mypyc/irbuild/function.py | 18 +-- mypyc/irbuild/ll_builder.py | 2 +- mypyc/irbuild/statement.py | 4 +- mypyc/primitives/generic_ops.py | 165 ++++++++++++------------ mypyc/test-data/analysis.test | 2 +- mypyc/test-data/exceptions.test | 18 +-- mypyc/test-data/irbuild-any.test | 25 ++-- mypyc/test-data/irbuild-basic.test | 72 +++++------ mypyc/test-data/irbuild-classes.test | 22 ++-- mypyc/test-data/irbuild-dict.test | 4 +- mypyc/test-data/irbuild-lists.test | 2 +- mypyc/test-data/irbuild-nested.test | 2 +- mypyc/test-data/irbuild-optional.test | 10 +- mypyc/test-data/irbuild-statements.test | 68 +++++----- mypyc/test-data/irbuild-try.test | 36 +++--- 19 files changed, 235 insertions(+), 241 deletions(-) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index a39dcad7b082..1b087c501aa3 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -34,7 +34,7 @@ BasicBlock, AssignmentTarget, AssignmentTargetRegister, AssignmentTargetIndex, AssignmentTargetAttr, AssignmentTargetTuple, Environment, LoadInt, Value, Register, Op, Assign, Branch, Unreachable, TupleGet, GetAttr, SetAttr, LoadStatic, - InitStatic, PrimitiveOp, OpDescription, NAMESPACE_MODULE, RaiseStandardError, + InitStatic, OpDescription, NAMESPACE_MODULE, RaiseStandardError, ) from mypyc.ir.rtypes import ( RType, RTuple, RInstance, int_rprimitive, dict_rprimitive, @@ -448,7 +448,7 @@ def assign(self, target: Union[Register, AssignmentTarget], else: key = self.load_static_unicode(target.attr) boxed_reg = self.builder.box(rvalue_reg) - self.add(PrimitiveOp([target.obj, key, boxed_reg], py_setattr_op, line)) + self.call_c(py_setattr_op, [target.obj, key, boxed_reg], line) elif isinstance(target, AssignmentTargetIndex): target_reg2 = self.gen_method_call( target.base, '__setitem__', [target.index, rvalue_reg], None, line) @@ -484,14 +484,14 @@ def process_iterator_tuple_assignment(self, rvalue_reg: Value, line: int) -> None: - iterator = self.primitive_op(iter_op, [rvalue_reg], line) + iterator = self.call_c(iter_op, [rvalue_reg], line) # This may be the whole lvalue list if there is no starred value split_idx = target.star_idx if target.star_idx is not None else len(target.items) # Assign values before the first starred value for litem in target.items[:split_idx]: - ritem = self.primitive_op(next_op, [iterator], line) + ritem = self.call_c(next_op, [iterator], line) error_block, ok_block = BasicBlock(), BasicBlock() self.add(Branch(ritem, error_block, ok_block, Branch.IS_ERROR)) @@ -532,7 +532,7 @@ def process_iterator_tuple_assignment(self, # There is no starred value, so check if there are extra values in rhs that # have not been assigned. else: - extra = self.primitive_op(next_op, [iterator], line) + extra = self.call_c(next_op, [iterator], line) error_block, ok_block = BasicBlock(), BasicBlock() self.add(Branch(extra, ok_block, error_block, Branch.IS_ERROR)) diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index ef6527e445a0..68d5ff3ee1bd 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -124,7 +124,7 @@ def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None: continue typ = builder.load_native_type_object(cdef.fullname) value = builder.accept(stmt.rvalue) - builder.primitive_op( + builder.call_c( py_setattr_op, [typ, builder.load_static_unicode(lvalue.name), value], stmt.line) if builder.non_function_scope() and stmt.is_final_def: builder.init_final_static(lvalue, value, cdef.name) @@ -182,7 +182,7 @@ def allocate_class(builder: IRBuilder, cdef: ClassDef) -> Value: None, builder.module_name, FuncSignature([], bool_rprimitive)), [], -1)) # Populate a '__mypyc_attrs__' field containing the list of attrs - builder.primitive_op(py_setattr_op, [ + builder.call_c(py_setattr_op, [ tp, builder.load_static_unicode('__mypyc_attrs__'), create_mypyc_attrs_tuple(builder, builder.mapper.type_to_ir[cdef.info], cdef.line)], cdef.line) @@ -241,9 +241,9 @@ def setup_non_ext_dict(builder: IRBuilder, This class dictionary is passed to the metaclass constructor. """ # Check if the metaclass defines a __prepare__ method, and if so, call it. - has_prepare = builder.primitive_op(py_hasattr_op, - [metaclass, - builder.load_static_unicode('__prepare__')], cdef.line) + has_prepare = builder.call_c(py_hasattr_op, + [metaclass, + builder.load_static_unicode('__prepare__')], cdef.line) non_ext_dict = builder.alloc_temp(dict_rprimitive) diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 3e20951d491f..f759ffeb329d 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -575,6 +575,6 @@ def get_arg(arg: Optional[Expression]) -> Value: def transform_generator_expr(builder: IRBuilder, o: GeneratorExpr) -> Value: builder.warning('Treating generator comprehension as list', o.line) - return builder.primitive_op( + return builder.call_c( iter_op, [translate_list_comprehension(builder, o)], o.line ) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 9b5e24b97911..56e8cf8b816f 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -352,7 +352,7 @@ def init(self, expr_reg: Value, target_type: RType) -> None: # for the for-loop. If we are inside of a generator function, spill these into the # environment class. builder = self.builder - iter_reg = builder.primitive_op(iter_op, [expr_reg], self.line) + iter_reg = builder.call_c(iter_op, [expr_reg], self.line) builder.maybe_spill(expr_reg) self.iter_target = builder.maybe_spill(iter_reg) self.target_type = target_type @@ -364,7 +364,7 @@ def gen_condition(self) -> None: # for NULL (an exception does not necessarily have to be raised). builder = self.builder line = self.line - self.next_reg = builder.primitive_op(next_op, [builder.read(self.iter_target, line)], line) + self.next_reg = builder.call_c(next_op, [builder.read(self.iter_target, line)], line) builder.add(Branch(self.next_reg, self.loop_exit, self.body_block, Branch.IS_ERROR)) def begin_body(self) -> None: diff --git a/mypyc/irbuild/function.py b/mypyc/irbuild/function.py index 121f28088da7..44928136c886 100644 --- a/mypyc/irbuild/function.py +++ b/mypyc/irbuild/function.py @@ -350,13 +350,13 @@ def handle_ext_method(builder: IRBuilder, cdef: ClassDef, fdef: FuncDef) -> None # Set the callable object representing the decorated method as an attribute of the # extension class. - builder.primitive_op(py_setattr_op, - [ - typ, - builder.load_static_unicode(name), - decorated_func - ], - fdef.line) + builder.call_c(py_setattr_op, + [ + typ, + builder.load_static_unicode(name), + decorated_func + ], + fdef.line) if fdef.is_property: # If there is a property setter, it will be processed after the getter, @@ -491,14 +491,14 @@ def handle_yield_from_and_await(builder: IRBuilder, o: Union[YieldFromExpr, Awai received_reg = builder.alloc_temp(object_rprimitive) if isinstance(o, YieldFromExpr): - iter_val = builder.primitive_op(iter_op, [builder.accept(o.expr)], o.line) + iter_val = builder.call_c(iter_op, [builder.accept(o.expr)], o.line) else: iter_val = builder.call_c(coro_op, [builder.accept(o.expr)], o.line) iter_reg = builder.maybe_spill_assignable(iter_val) stop_block, main_block, done_block = BasicBlock(), BasicBlock(), BasicBlock() - _y_init = builder.primitive_op(next_raw_op, [builder.read(iter_reg)], o.line) + _y_init = builder.call_c(next_raw_op, [builder.read(iter_reg)], o.line) builder.add(Branch(_y_init, stop_block, main_block, Branch.IS_ERROR)) # Try extracting a return value from a StopIteration and return it. diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index a3b6652c4d3f..f8d5e5d8e04f 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -186,7 +186,7 @@ def py_get_attr(self, obj: Value, attr: str, line: int) -> Value: Prefer get_attr() which generates optimized code for native classes. """ key = self.load_static_unicode(attr) - return self.add(PrimitiveOp([obj, key], py_getattr_op, line)) + return self.call_c(py_getattr_op, [obj, key], line) # isinstance() checks diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py index ecbfcd18ea9d..0898a0b19d4d 100644 --- a/mypyc/irbuild/statement.py +++ b/mypyc/irbuild/statement.py @@ -17,7 +17,7 @@ from mypyc.ir.ops import ( Assign, Unreachable, AssignmentTarget, AssignmentTargetRegister, AssignmentTargetIndex, - AssignmentTargetAttr, AssignmentTargetTuple, PrimitiveOp, RaiseStandardError, LoadErrorValue, + AssignmentTargetAttr, AssignmentTargetTuple, RaiseStandardError, LoadErrorValue, BasicBlock, TupleGet, Value, Register, Branch, NO_TRACEBACK_LINE_NO ) from mypyc.ir.rtypes import exc_rtuple @@ -634,7 +634,7 @@ def transform_del_item(builder: IRBuilder, target: AssignmentTarget, line: int) ) elif isinstance(target, AssignmentTargetAttr): key = builder.load_static_unicode(target.attr) - builder.add(PrimitiveOp([target.obj, key], py_delattr_op, line)) + builder.call_c(py_delattr_op, [target.obj, key], line) elif isinstance(target, AssignmentTargetRegister): # Delete a local by assigning an error value to it, which will # prompt the insertion of uninit checks. diff --git a/mypyc/primitives/generic_ops.py b/mypyc/primitives/generic_ops.py index e5d3e93bcab1..39d8f6cd279f 100644 --- a/mypyc/primitives/generic_ops.py +++ b/mypyc/primitives/generic_ops.py @@ -9,12 +9,12 @@ check that the priorities are configured properly. """ -from mypyc.ir.ops import ERR_NEVER, ERR_MAGIC, ERR_FALSE +from mypyc.ir.ops import ERR_NEVER, ERR_MAGIC, ERR_NEG_INT from mypyc.ir.rtypes import object_rprimitive, int_rprimitive, bool_rprimitive, c_int_rprimitive from mypyc.primitives.registry import ( - binary_op, unary_op, func_op, method_op, custom_op, call_emit, simple_emit, - call_negative_bool_emit, call_negative_magic_emit, negative_int_emit, - c_binary_op + binary_op, unary_op, func_op, custom_op, call_emit, simple_emit, + call_negative_magic_emit, negative_int_emit, + c_binary_op, c_unary_op, c_method_op, c_function_op, c_custom_op ) @@ -46,12 +46,12 @@ ('&', 'PyNumber_And'), ('^', 'PyNumber_Xor'), ('|', 'PyNumber_Or')]: - binary_op(op=op, - arg_types=[object_rprimitive, object_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit(funcname), - priority=0) + c_binary_op(name=op, + arg_types=[object_rprimitive, object_rprimitive], + return_type=object_rprimitive, + c_function_name=funcname, + error_kind=ERR_MAGIC, + priority=0) for op, funcname in [('+=', 'PyNumber_InPlaceAdd'), ('-=', 'PyNumber_InPlaceSubtract'), @@ -65,12 +65,12 @@ ('&=', 'PyNumber_InPlaceAnd'), ('^=', 'PyNumber_InPlaceXor'), ('|=', 'PyNumber_InPlaceOr')]: - binary_op(op=op, - arg_types=[object_rprimitive, object_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit(funcname), - priority=0) + c_binary_op(name=op, + arg_types=[object_rprimitive, object_rprimitive], + return_type=object_rprimitive, + c_function_name=funcname, + error_kind=ERR_MAGIC, + priority=0) binary_op(op='**', arg_types=[object_rprimitive, object_rprimitive], @@ -106,12 +106,12 @@ for op, funcname in [('-', 'PyNumber_Negative'), ('+', 'PyNumber_Positive'), ('~', 'PyNumber_Invert')]: - unary_op(op=op, - arg_type=object_rprimitive, - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit(funcname), - priority=0) + c_unary_op(name=op, + arg_type=object_rprimitive, + return_type=object_rprimitive, + c_function_name=funcname, + error_kind=ERR_MAGIC, + priority=0) unary_op(op='not', arg_type=object_rprimitive, @@ -123,81 +123,78 @@ # obj1[obj2] -method_op('__getitem__', - arg_types=[object_rprimitive, object_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('PyObject_GetItem'), - priority=0) +c_method_op(name='__getitem__', + arg_types=[object_rprimitive, object_rprimitive], + return_type=object_rprimitive, + c_function_name='PyObject_GetItem', + error_kind=ERR_MAGIC, + priority=0) # obj1[obj2] = obj3 -method_op('__setitem__', - arg_types=[object_rprimitive, object_rprimitive, object_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_FALSE, - emit=call_negative_bool_emit('PyObject_SetItem'), - priority=0) +c_method_op( + name='__setitem__', + arg_types=[object_rprimitive, object_rprimitive, object_rprimitive], + return_type=c_int_rprimitive, + c_function_name='PyObject_SetItem', + error_kind=ERR_NEG_INT, + priority=0) # del obj1[obj2] -method_op('__delitem__', - arg_types=[object_rprimitive, object_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_FALSE, - emit=call_negative_bool_emit('PyObject_DelItem'), - priority=0) +c_method_op( + name='__delitem__', + arg_types=[object_rprimitive, object_rprimitive], + return_type=c_int_rprimitive, + c_function_name='PyObject_DelItem', + error_kind=ERR_NEG_INT, + priority=0) # hash(obj) -func_op( +c_function_op( name='builtins.hash', arg_types=[object_rprimitive], - result_type=int_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyObject_Hash')) + return_type=int_rprimitive, + c_function_name='CPyObject_Hash', + error_kind=ERR_MAGIC) # getattr(obj, attr) -py_getattr_op = func_op( +py_getattr_op = c_function_op( name='builtins.getattr', arg_types=[object_rprimitive, object_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyObject_GetAttr') -) + return_type=object_rprimitive, + c_function_name='CPyObject_GetAttr', + error_kind=ERR_MAGIC) # getattr(obj, attr, default) -func_op( +c_function_op( name='builtins.getattr', arg_types=[object_rprimitive, object_rprimitive, object_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('CPyObject_GetAttr3') -) + return_type=object_rprimitive, + c_function_name='CPyObject_GetAttr3', + error_kind=ERR_MAGIC) # setattr(obj, attr, value) -py_setattr_op = func_op( +py_setattr_op = c_function_op( name='builtins.setattr', arg_types=[object_rprimitive, object_rprimitive, object_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_FALSE, - emit=call_negative_bool_emit('PyObject_SetAttr') -) + return_type=c_int_rprimitive, + c_function_name='PyObject_SetAttr', + error_kind=ERR_NEG_INT) # hasattr(obj, attr) -py_hasattr_op = func_op( +py_hasattr_op = c_function_op( name='builtins.hasattr', arg_types=[object_rprimitive, object_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_NEVER, - emit=call_emit('PyObject_HasAttr') -) + return_type=bool_rprimitive, + c_function_name='PyObject_HasAttr', + error_kind=ERR_NEVER) # del obj.attr -py_delattr_op = func_op( +py_delattr_op = c_function_op( name='builtins.delattr', arg_types=[object_rprimitive, object_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_FALSE, - emit=call_negative_bool_emit('PyObject_DelAttr') -) + return_type=c_int_rprimitive, + c_function_name='PyObject_DelAttr', + error_kind=ERR_NEG_INT) # Call callable object with N positional arguments: func(arg1, ..., argN) # Arguments are (func, arg1, ..., argN). @@ -237,22 +234,19 @@ priority=0) # iter(obj) -iter_op = func_op(name='builtins.iter', - arg_types=[object_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_MAGIC, - emit=call_emit('PyObject_GetIter')) - +iter_op = c_function_op(name='builtins.iter', + arg_types=[object_rprimitive], + return_type=object_rprimitive, + c_function_name='PyObject_GetIter', + error_kind=ERR_MAGIC) # next(iterator) # # Although the error_kind is set to be ERR_NEVER, this can actually # return NULL, and thus it must be checked using Branch.IS_ERROR. -next_op = custom_op(name='next', - arg_types=[object_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_NEVER, - emit=call_emit('PyIter_Next')) - +next_op = c_custom_op(arg_types=[object_rprimitive], + return_type=object_rprimitive, + c_function_name='PyIter_Next', + error_kind=ERR_NEVER) # next(iterator) # # Do a next, don't swallow StopIteration, but also don't propagate an @@ -260,8 +254,7 @@ # represent an implicit StopIteration, but if StopIteration is # *explicitly* raised this will not swallow it.) # Can return NULL: see next_op. -next_raw_op = custom_op(name='next', - arg_types=[object_rprimitive], - result_type=object_rprimitive, - error_kind=ERR_NEVER, - emit=call_emit('CPyIter_Next')) +next_raw_op = c_custom_op(arg_types=[object_rprimitive], + return_type=object_rprimitive, + c_function_name='CPyIter_Next', + error_kind=ERR_NEVER) diff --git a/mypyc/test-data/analysis.test b/mypyc/test-data/analysis.test index c800c43bed5e..c79c61bbcdeb 100644 --- a/mypyc/test-data/analysis.test +++ b/mypyc/test-data/analysis.test @@ -755,7 +755,7 @@ L2: r1 = CPy_CatchError() r2 = builtins :: module r3 = unicode_1 :: static ('Exception') - r4 = getattr r2, r3 + r4 = CPyObject_GetAttr(r2, r3) if is_error(r4) goto L8 (error at lol:4) else goto L3 L3: r5 = CPy_ExceptionMatches(r4) diff --git a/mypyc/test-data/exceptions.test b/mypyc/test-data/exceptions.test index 4b39cb2978b2..02db4aa9e789 100644 --- a/mypyc/test-data/exceptions.test +++ b/mypyc/test-data/exceptions.test @@ -199,7 +199,7 @@ L0: L1: r0 = builtins :: module r1 = unicode_1 :: static ('object') - r2 = getattr r0, r1 + r2 = CPyObject_GetAttr(r0, r1) if is_error(r2) goto L3 (error at g:3) else goto L2 L2: r3 = py_call(r2) @@ -210,7 +210,7 @@ L3: r5 = unicode_2 :: static ('weeee') r6 = builtins :: module r7 = unicode_3 :: static ('print') - r8 = getattr r6, r7 + r8 = CPyObject_GetAttr(r6, r7) if is_error(r8) goto L6 (error at g:5) else goto L4 L4: r9 = py_call(r8, r5) @@ -268,7 +268,7 @@ L0: L1: r0 = builtins :: module r1 = unicode_1 :: static ('print') - r2 = getattr r0, r1 + r2 = CPyObject_GetAttr(r0, r1) if is_error(r2) goto L5 (error at a:3) else goto L2 L2: r3 = py_call(r2) @@ -291,7 +291,7 @@ L6: r11 = unicode_3 :: static ('goodbye!') r12 = builtins :: module r13 = unicode_1 :: static ('print') - r14 = getattr r12, r13 + r14 = CPyObject_GetAttr(r12, r13) if is_error(r14) goto L13 (error at a:6) else goto L7 L7: r15 = py_call(r14, r11) @@ -371,7 +371,7 @@ def lol(x): L0: L1: r0 = unicode_3 :: static ('foo') - r1 = getattr x, r0 + r1 = CPyObject_GetAttr(x, r0) if is_error(r1) goto L3 (error at lol:4) else goto L2 L2: st = r1 @@ -411,12 +411,12 @@ def lol(x): L0: L1: r0 = unicode_3 :: static ('foo') - r1 = getattr x, r0 + r1 = CPyObject_GetAttr(x, r0) if is_error(r1) goto L4 (error at lol:4) else goto L15 L2: a = r1 r2 = unicode_4 :: static ('bar') - r3 = getattr x, r2 + r3 = CPyObject_GetAttr(x, r2) if is_error(r3) goto L4 (error at lol:5) else goto L16 L3: b = r3 @@ -441,7 +441,7 @@ L10: L11: unreachable L12: - r6 = a + b + r6 = PyNumber_Add(a, b) xdec_ref a xdec_ref b if is_error(r6) goto L14 (error at lol:9) else goto L13 @@ -498,7 +498,7 @@ L2: L3: r4 = builtins :: module r5 = unicode_3 :: static ('print') - r6 = getattr r4, r5 + r6 = CPyObject_GetAttr(r4, r5) if is_error(r6) goto L12 (error at f:7) else goto L4 L4: if is_error(v) goto L13 else goto L7 diff --git a/mypyc/test-data/irbuild-any.test b/mypyc/test-data/irbuild-any.test index ff5fe9272ad5..71ae2bfa457d 100644 --- a/mypyc/test-data/irbuild-any.test +++ b/mypyc/test-data/irbuild-any.test @@ -51,7 +51,7 @@ def f(a, n, c): r5 :: int r6 :: str r7 :: object - r8 :: bool + r8 :: int32 r9 :: None L0: r0 = box(int, n) @@ -64,7 +64,7 @@ L0: n = r5 r6 = unicode_6 :: static ('a') r7 = box(int, n) - r8 = setattr a, r6, r7 + r8 = PyObject_SetAttr(a, r6, r7) r9 = None return r9 @@ -91,9 +91,9 @@ def f1(a, n): r4 :: None L0: r0 = box(int, n) - r1 = a + r0 + r1 = PyNumber_Add(a, r0) r2 = box(int, n) - r3 = r2 + a + r3 = PyNumber_Add(r2, a) r4 = None return r4 def f2(a, n, l): @@ -101,21 +101,22 @@ def f2(a, n, l): n :: int l :: list r0, r1, r2, r3, r4 :: object - r5 :: bool + r5 :: int32 r6 :: object - r7, r8 :: bool + r7 :: int32 + r8 :: bool r9 :: object r10 :: list r11 :: None L0: r0 = box(int, n) - r1 = a[r0] :: object - r2 = l[a] :: object + r1 = PyObject_GetItem(a, r0) + r2 = PyObject_GetItem(l, a) r3 = box(int, n) r4 = box(int, n) - r5 = a.__setitem__(r3, r4) :: object + r5 = PyObject_SetItem(a, r3, r4) r6 = box(int, n) - r7 = l.__setitem__(a, r6) :: object + r7 = PyObject_SetItem(l, a, r6) r8 = CPyList_SetItem(l, n, a) r9 = box(int, n) r10 = [a, r9] @@ -129,10 +130,10 @@ def f3(a, n): r5 :: None L0: r0 = box(int, n) - r1 = a += r0 + r1 = PyNumber_InPlaceAdd(a, r0) a = r1 r2 = box(int, n) - r3 = r2 += a + r3 = PyNumber_InPlaceAdd(r2, a) r4 = unbox(int, r3) n = r4 r5 = None diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 96e39f2baadd..6a6cd086642c 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -776,7 +776,7 @@ def f(x): L0: r0 = testmodule :: module r1 = unicode_2 :: static ('factorial') - r2 = getattr r0, r1 + r2 = CPyObject_GetAttr(r0, r1) r3 = box(int, x) r4 = py_call(r2, r3) r5 = unbox(int, r4) @@ -821,7 +821,7 @@ def f(x): L0: r0 = builtins :: module r1 = unicode_1 :: static ('print') - r2 = getattr r0, r1 + r2 = CPyObject_GetAttr(r0, r1) r3 = box(short_int, 10) r4 = py_call(r2, r3) r5 = None @@ -841,7 +841,7 @@ def f(x): L0: r0 = builtins :: module r1 = unicode_1 :: static ('print') - r2 = getattr r0, r1 + r2 = CPyObject_GetAttr(r0, r1) r3 = box(short_int, 10) r4 = py_call(r2, r3) r5 = None @@ -1099,10 +1099,10 @@ def f(x: Any, y: Any, z: Any) -> None: [out] def f(x, y, z): x, y, z :: object - r0 :: bool + r0 :: int32 r1 :: None L0: - r0 = x.__setitem__(y, z) :: object + r0 = PyObject_SetItem(x, y, z) r1 = None return r1 @@ -1126,9 +1126,9 @@ L0: f2 = r1 r2 = float_3 :: static (3.0) f3 = r2 - r3 = f1 * f2 + r3 = PyNumber_Multiply(f1, f2) r4 = cast(float, r3) - r5 = r4 + f3 + r5 = PyNumber_Add(r4, f3) r6 = cast(float, r5) return r6 @@ -1143,7 +1143,7 @@ def load(): L0: r0 = complex_1 :: static (5j) r1 = float_2 :: static (1.0) - r2 = r0 + r1 + r2 = PyNumber_Add(r0, r1) return r2 [case testBigIntLiteral] @@ -1322,7 +1322,7 @@ def call_python_method_with_keyword_args(xs, first, second): r16 :: object L0: r0 = unicode_4 :: static ('insert') - r1 = getattr xs, r0 + r1 = CPyObject_GetAttr(xs, r0) r2 = unicode_5 :: static ('x') r3 = box(short_int, 0) r4 = (r3) :: tuple @@ -1330,7 +1330,7 @@ L0: r6 = CPyDict_Build(1, r2, r5) r7 = py_call_with_kwargs(r1, r4, r6) r8 = unicode_4 :: static ('insert') - r9 = getattr xs, r8 + r9 = CPyObject_GetAttr(xs, r8) r10 = unicode_5 :: static ('x') r11 = unicode_6 :: static ('i') r12 = () :: tuple @@ -1518,7 +1518,7 @@ def foo(): L0: r0 = builtins :: module r1 = unicode_1 :: static ('Exception') - r2 = getattr r0, r1 + r2 = CPyObject_GetAttr(r0, r1) r3 = py_call(r2) CPy_Raise(r3) unreachable @@ -1529,7 +1529,7 @@ def bar(): L0: r0 = builtins :: module r1 = unicode_1 :: static ('Exception') - r2 = getattr r0, r1 + r2 = CPyObject_GetAttr(r0, r1) CPy_Raise(r2) unreachable @@ -1556,7 +1556,7 @@ L0: r3 = unbox(int, r2) r4 = builtins :: module r5 = unicode_2 :: static ('print') - r6 = getattr r4, r5 + r6 = CPyObject_GetAttr(r4, r5) r7 = box(int, r3) r8 = py_call(r6, r7) r9 = None @@ -1598,7 +1598,7 @@ L2: r12 = unbox(int, r11) r13 = builtins :: module r14 = unicode_2 :: static ('print') - r15 = getattr r13, r14 + r15 = CPyObject_GetAttr(r13, r14) r16 = box(int, r12) r17 = py_call(r15, r16) r18 = None @@ -1624,7 +1624,7 @@ def f(): L0: r0 = m :: module r1 = unicode_2 :: static ('f') - r2 = getattr r0, r1 + r2 = CPyObject_GetAttr(r0, r1) r3 = box(short_int, 2) r4 = py_call(r2, r3) r5 = cast(str, r4) @@ -2651,15 +2651,15 @@ L4: r10 = typing :: module r11 = __main__.globals :: static r12 = unicode_2 :: static ('List') - r13 = getattr r10, r12 + r13 = CPyObject_GetAttr(r10, r12) r14 = unicode_2 :: static ('List') r15 = CPyDict_SetItem(r11, r14, r13) r16 = unicode_3 :: static ('NewType') - r17 = getattr r10, r16 + r17 = CPyObject_GetAttr(r10, r16) r18 = unicode_3 :: static ('NewType') r19 = CPyDict_SetItem(r11, r18, r17) r20 = unicode_4 :: static ('NamedTuple') - r21 = getattr r10, r20 + r21 = CPyObject_GetAttr(r10, r20) r22 = unicode_4 :: static ('NamedTuple') r23 = CPyDict_SetItem(r11, r22, r21) r24 = unicode_5 :: static ('Lol') @@ -2694,7 +2694,7 @@ L4: r53 = unicode_2 :: static ('List') r54 = CPyDict_GetItem(r52, r53) r55 = int - r56 = r54[r55] :: object + r56 = PyObject_GetItem(r54, r55) r57 = __main__.globals :: static r58 = unicode_10 :: static ('Foo') r59 = CPyDict_SetItem(r57, r58, r56) @@ -2876,14 +2876,14 @@ L0: r2 = unicode_3 :: static ('Entering') r3 = builtins :: module r4 = unicode_4 :: static ('print') - r5 = getattr r3, r4 + r5 = CPyObject_GetAttr(r3, r4) r6 = py_call(r5, r2) r7 = r0.f r8 = py_call(r7) r9 = unicode_5 :: static ('Exited') r10 = builtins :: module r11 = unicode_4 :: static ('print') - r12 = getattr r10, r11 + r12 = CPyObject_GetAttr(r10, r11) r13 = py_call(r12, r9) r14 = None return r14 @@ -2935,14 +2935,14 @@ L0: r2 = unicode_6 :: static ('---') r3 = builtins :: module r4 = unicode_4 :: static ('print') - r5 = getattr r3, r4 + r5 = CPyObject_GetAttr(r3, r4) r6 = py_call(r5, r2) r7 = r0.f r8 = py_call(r7) r9 = unicode_6 :: static ('---') r10 = builtins :: module r11 = unicode_4 :: static ('print') - r12 = getattr r10, r11 + r12 = CPyObject_GetAttr(r10, r11) r13 = py_call(r12, r9) r14 = None return r14 @@ -2990,7 +2990,7 @@ L0: r2 = unicode_7 :: static ('d') r3 = builtins :: module r4 = unicode_4 :: static ('print') - r5 = getattr r3, r4 + r5 = CPyObject_GetAttr(r3, r4) r6 = py_call(r5, r2) r7 = None return r7 @@ -3026,7 +3026,7 @@ L0: r12 = unicode_10 :: static ('c') r13 = builtins :: module r14 = unicode_4 :: static ('print') - r15 = getattr r13, r14 + r15 = CPyObject_GetAttr(r13, r14) r16 = py_call(r15, r12) r17 = r0.d r18 = py_call(r17) @@ -3080,7 +3080,7 @@ L4: r10 = typing :: module r11 = __main__.globals :: static r12 = unicode_2 :: static ('Callable') - r13 = getattr r10, r12 + r13 = CPyObject_GetAttr(r10, r12) r14 = unicode_2 :: static ('Callable') r15 = CPyDict_SetItem(r11, r14, r13) r16 = __main__.globals :: static @@ -3144,14 +3144,14 @@ L0: r2 = unicode_3 :: static ('Entering') r3 = builtins :: module r4 = unicode_4 :: static ('print') - r5 = getattr r3, r4 + r5 = CPyObject_GetAttr(r3, r4) r6 = py_call(r5, r2) r7 = r0.f r8 = py_call(r7) r9 = unicode_5 :: static ('Exited') r10 = builtins :: module r11 = unicode_4 :: static ('print') - r12 = getattr r10, r11 + r12 = CPyObject_GetAttr(r10, r11) r13 = py_call(r12, r9) r14 = None return r14 @@ -3206,7 +3206,7 @@ L4: r10 = typing :: module r11 = __main__.globals :: static r12 = unicode_2 :: static ('Callable') - r13 = getattr r10, r12 + r13 = CPyObject_GetAttr(r10, r12) r14 = unicode_2 :: static ('Callable') r15 = CPyDict_SetItem(r11, r14, r13) r16 = None @@ -3233,9 +3233,9 @@ def call_any(l): L0: r1 = False r0 = r1 - r2 = iter l :: object + r2 = PyObject_GetIter(l) L1: - r3 = next r2 :: object + r3 = PyIter_Next(r2) if is_error(r3) goto L9 else goto L2 L2: r4 = unbox(int, r3) @@ -3275,9 +3275,9 @@ def call_all(l): L0: r1 = True r0 = r1 - r2 = iter l :: object + r2 = PyObject_GetIter(l) L1: - r3 = next r2 :: object + r3 = PyIter_Next(r2) if is_error(r3) goto L9 else goto L2 L2: r4 = unbox(int, r3) @@ -3317,13 +3317,13 @@ def lol(x: Any): def lol(x): x :: object r0, r1 :: str - r2 :: bool + r2 :: int32 r3, r4 :: None r5 :: object L0: r0 = unicode_5 :: static ('x') r1 = unicode_6 :: static ('5') - r2 = setattr x, r0, r1 + r2 = PyObject_SetAttr(x, r0, r1) r3 = None r4 = None r5 = box(None, r4) @@ -3575,7 +3575,7 @@ def f(x): L0: r0 = builtins :: module r1 = unicode_1 :: static ('reveal_type') - r2 = getattr r0, r1 + r2 = CPyObject_GetAttr(r0, r1) r3 = box(int, x) r4 = py_call(r2, r3) r5 = None diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index 51e53f91a0dc..d92291e29c95 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -334,7 +334,7 @@ def __top_level__(): r43 :: bool r44 :: str r45 :: tuple - r46 :: bool + r46 :: int32 r47 :: dict r48 :: str r49 :: int32 @@ -343,7 +343,7 @@ def __top_level__(): r52, r53 :: object r54 :: str r55 :: tuple - r56 :: bool + r56 :: int32 r57 :: dict r58 :: str r59 :: int32 @@ -360,7 +360,7 @@ def __top_level__(): r73 :: bool r74, r75 :: str r76 :: tuple - r77 :: bool + r77 :: int32 r78 :: dict r79 :: str r80 :: int32 @@ -387,11 +387,11 @@ L4: r10 = typing :: module r11 = __main__.globals :: static r12 = unicode_2 :: static ('TypeVar') - r13 = getattr r10, r12 + r13 = CPyObject_GetAttr(r10, r12) r14 = unicode_2 :: static ('TypeVar') r15 = CPyDict_SetItem(r11, r14, r13) r16 = unicode_3 :: static ('Generic') - r17 = getattr r10, r16 + r17 = CPyObject_GetAttr(r10, r16) r18 = unicode_3 :: static ('Generic') r19 = CPyDict_SetItem(r11, r18, r17) r20 = mypy_extensions :: module @@ -406,7 +406,7 @@ L6: r25 = mypy_extensions :: module r26 = __main__.globals :: static r27 = unicode_5 :: static ('trait') - r28 = getattr r25, r27 + r28 = CPyObject_GetAttr(r25, r27) r29 = unicode_5 :: static ('trait') r30 = CPyDict_SetItem(r26, r29, r28) r31 = unicode_6 :: static ('T') @@ -424,7 +424,7 @@ L6: r43 = C_trait_vtable_setup() r44 = unicode_8 :: static ('__mypyc_attrs__') r45 = () :: tuple - r46 = setattr r42, r44, r45 + r46 = PyObject_SetAttr(r42, r44, r45) __main__.C = r42 :: type r47 = __main__.globals :: static r48 = unicode_9 :: static ('C') @@ -435,7 +435,7 @@ L6: r53 = pytype_from_template(r52, r50, r51) r54 = unicode_8 :: static ('__mypyc_attrs__') r55 = () :: tuple - r56 = setattr r53, r54, r55 + r56 = PyObject_SetAttr(r53, r54, r55) __main__.S = r53 :: type r57 = __main__.globals :: static r58 = unicode_10 :: static ('S') @@ -448,7 +448,7 @@ L6: r65 = __main__.globals :: static r66 = unicode_6 :: static ('T') r67 = CPyDict_GetItem(r65, r66) - r68 = r64[r67] :: object + r68 = PyObject_GetItem(r64, r67) r69 = (r60, r61, r68) :: tuple r70 = unicode_7 :: static ('__main__') r71 = __main__.D_template :: type @@ -457,7 +457,7 @@ L6: r74 = unicode_8 :: static ('__mypyc_attrs__') r75 = unicode_11 :: static ('__dict__') r76 = (r75) :: tuple - r77 = setattr r72, r74, r76 + r77 = PyObject_SetAttr(r72, r74, r76) __main__.D = r72 :: type r78 = __main__.globals :: static r79 = unicode_12 :: static ('D') @@ -767,7 +767,7 @@ def f(): L0: r0 = __main__.A :: type r1 = unicode_6 :: static ('x') - r2 = getattr r0, r1 + r2 = CPyObject_GetAttr(r0, r1) r3 = unbox(int, r2) return r3 diff --git a/mypyc/test-data/irbuild-dict.test b/mypyc/test-data/irbuild-dict.test index 85f23058126d..064406c9f808 100644 --- a/mypyc/test-data/irbuild-dict.test +++ b/mypyc/test-data/irbuild-dict.test @@ -172,7 +172,7 @@ L2: k = r7 r8 = CPyDict_GetItem(d, k) r9 = box(short_int, 2) - r10 = r8 += r9 + r10 = PyNumber_InPlaceAdd(r8, r9) r11 = CPyDict_SetItem(d, k, r10) L3: r12 = CPyDict_CheckSize(d, r1) @@ -288,7 +288,7 @@ L9: r24 = box(int, k) r25 = CPyDict_GetItem(d2, r24) r26 = box(int, v) - r27 = r25 += r26 + r27 = PyNumber_InPlaceAdd(r25, r26) r28 = box(int, k) r29 = CPyDict_SetItem(d2, r28, r27) L10: diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test index b07e5c40b118..76c39fc183eb 100644 --- a/mypyc/test-data/irbuild-lists.test +++ b/mypyc/test-data/irbuild-lists.test @@ -169,7 +169,7 @@ L1: L2: r3 = CPyList_GetItem(l, i) r4 = box(short_int, 2) - r5 = r3 += r4 + r5 = PyNumber_InPlaceAdd(r3, r4) r6 = CPyList_SetItem(l, i, r5) L3: r7 = r1 + 2 diff --git a/mypyc/test-data/irbuild-nested.test b/mypyc/test-data/irbuild-nested.test index 45e7d957feb9..62f8c07812d4 100644 --- a/mypyc/test-data/irbuild-nested.test +++ b/mypyc/test-data/irbuild-nested.test @@ -805,7 +805,7 @@ def __mypyc_lambda__0_f_obj.__call__(__mypyc_self__, a, b): r1 :: object L0: r0 = __mypyc_self__.__mypyc_env__ - r1 = a + b + r1 = PyNumber_Add(a, b) return r1 def __mypyc_lambda__1_f_obj.__get__(__mypyc_self__, instance, owner): __mypyc_self__, instance, owner, r0 :: object diff --git a/mypyc/test-data/irbuild-optional.test b/mypyc/test-data/irbuild-optional.test index 8e70c91ae09f..3f25bf103c80 100644 --- a/mypyc/test-data/irbuild-optional.test +++ b/mypyc/test-data/irbuild-optional.test @@ -368,11 +368,11 @@ L3: def set(o, s): o :: union[__main__.A, __main__.B] s, r0 :: str - r1 :: bool + r1 :: int32 r2 :: None L0: r0 = unicode_5 :: static ('a') - r1 = setattr o, r0, s + r1 = PyObject_SetAttr(o, r0, s) r2 = None return r2 @@ -494,7 +494,7 @@ L1: L2: r5 = o r6 = unicode_7 :: static ('x') - r7 = getattr r5, r6 + r7 = CPyObject_GetAttr(r5, r6) r8 = unbox(int, r7) r0 = r8 L3: @@ -524,7 +524,7 @@ L1: L2: r5 = o r6 = unicode_7 :: static ('x') - r7 = getattr r5, r6 + r7 = CPyObject_GetAttr(r5, r6) r8 = unbox(int, r7) r0 = r8 L3: @@ -554,7 +554,7 @@ def f(o): L0: r1 = o r2 = unicode_6 :: static ('x') - r3 = getattr r1, r2 + r3 = CPyObject_GetAttr(r1, r2) r0 = r3 L1: r4 = None diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index cdac45f0557b..a3768d343cfb 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -509,22 +509,22 @@ def from_any(a): r6 :: bool r7 :: None L0: - r0 = iter a :: object - r1 = next r0 :: object + r0 = PyObject_GetIter(a) + r1 = PyIter_Next(r0) if is_error(r1) goto L1 else goto L2 L1: raise ValueError('not enough values to unpack') unreachable L2: x = r1 - r3 = next r0 :: object + r3 = PyIter_Next(r0) if is_error(r3) goto L3 else goto L4 L3: raise ValueError('not enough values to unpack') unreachable L4: y = r3 - r5 = next r0 :: object + r5 = PyIter_Next(r0) if is_error(r5) goto L6 else goto L5 L5: raise ValueError('too many values to unpack') @@ -573,8 +573,8 @@ def from_any(a): r7 :: bool r8 :: None L0: - r0 = iter a :: object - r1 = next r0 :: object + r0 = PyObject_GetIter(a) + r1 = PyIter_Next(r0) if is_error(r1) goto L1 else goto L2 L1: raise ValueError('not enough values to unpack') @@ -582,14 +582,14 @@ L1: L2: r3 = unbox(int, r1) x = r3 - r4 = next r0 :: object + r4 = PyIter_Next(r0) if is_error(r4) goto L3 else goto L4 L3: raise ValueError('not enough values to unpack') unreachable L4: y = r4 - r6 = next r0 :: object + r6 = PyIter_Next(r0) if is_error(r6) goto L6 else goto L5 L5: raise ValueError('too many values to unpack') @@ -689,7 +689,7 @@ L1: L2: r4 = builtins :: module r5 = unicode_3 :: static ('AssertionError') - r6 = getattr r4, r5 + r6 = CPyObject_GetAttr(r4, r5) r7 = py_call(r6, s) CPy_Raise(r7) unreachable @@ -709,7 +709,7 @@ def delList(): r0, r1 :: object r2, l :: list r3 :: object - r4 :: bool + r4 :: int32 r5 :: None L0: r0 = box(short_int, 2) @@ -717,18 +717,18 @@ L0: r2 = [r0, r1] l = r2 r3 = box(short_int, 2) - r4 = l.__delitem__(r3) :: object + r4 = PyObject_DelItem(l, r3) r5 = None return r5 def delListMultiple(): r0, r1, r2, r3, r4, r5, r6 :: object r7, l :: list r8 :: object - r9 :: bool + r9 :: int32 r10 :: object - r11 :: bool + r11 :: int32 r12 :: object - r13 :: bool + r13 :: int32 r14 :: None L0: r0 = box(short_int, 2) @@ -741,11 +741,11 @@ L0: r7 = [r0, r1, r2, r3, r4, r5, r6] l = r7 r8 = box(short_int, 2) - r9 = l.__delitem__(r8) :: object + r9 = PyObject_DelItem(l, r8) r10 = box(short_int, 4) - r11 = l.__delitem__(r10) :: object + r11 = PyObject_DelItem(l, r10) r12 = box(short_int, 6) - r13 = l.__delitem__(r12) :: object + r13 = PyObject_DelItem(l, r12) r14 = None return r14 @@ -763,7 +763,7 @@ def delDict(): r2, r3 :: object r4, d :: dict r5 :: str - r6 :: bool + r6 :: int32 r7 :: None L0: r0 = unicode_1 :: static ('one') @@ -773,7 +773,7 @@ L0: r4 = CPyDict_Build(2, r0, r2, r1, r3) d = r4 r5 = unicode_1 :: static ('one') - r6 = d.__delitem__(r5) :: object + r6 = PyObject_DelItem(d, r5) r7 = None return r7 def delDictMultiple(): @@ -784,7 +784,7 @@ def delDictMultiple(): r4, r5, r6, r7 :: object r8, d :: dict r9, r10 :: str - r11, r12 :: bool + r11, r12 :: int32 r13 :: None L0: r0 = unicode_1 :: static ('one') @@ -799,8 +799,8 @@ L0: d = r8 r9 = unicode_1 :: static ('one') r10 = unicode_4 :: static ('four') - r11 = d.__delitem__(r9) :: object - r12 = d.__delitem__(r10) :: object + r11 = PyObject_DelItem(d, r9) + r12 = PyObject_DelItem(d, r10) r13 = None return r13 @@ -829,29 +829,29 @@ L0: def delAttribute(): r0, dummy :: __main__.Dummy r1 :: str - r2 :: bool + r2 :: int32 r3 :: None L0: r0 = Dummy(2, 4) dummy = r0 r1 = unicode_3 :: static ('x') - r2 = delattr dummy, r1 + r2 = PyObject_DelAttr(dummy, r1) r3 = None return r3 def delAttributeMultiple(): r0, dummy :: __main__.Dummy r1 :: str - r2 :: bool + r2 :: int32 r3 :: str - r4 :: bool + r4 :: int32 r5 :: None L0: r0 = Dummy(2, 4) dummy = r0 r1 = unicode_3 :: static ('x') - r2 = delattr dummy, r1 + r2 = PyObject_DelAttr(dummy, r1) r3 = unicode_4 :: static ('y') - r4 = delattr dummy, r3 + r4 = PyObject_DelAttr(dummy, r3) r5 = None return r5 @@ -911,9 +911,9 @@ def g(x): L0: r0 = 0 i = 0 - r1 = iter x :: object + r1 = PyObject_GetIter(x) L1: - r2 = next r1 :: object + r2 = PyIter_Next(r1) if is_error(r2) goto L4 else goto L2 L2: r3 = unbox(int, r2) @@ -956,13 +956,13 @@ def f(a, b): r11 :: None L0: r0 = 0 - r1 = iter b :: object + r1 = PyObject_GetIter(b) L1: r2 = len a :: list r3 = r0 < r2 :: signed if r3 goto L2 else goto L7 :: bool L2: - r4 = next r1 :: object + r4 = PyIter_Next(r1) if is_error(r4) goto L7 else goto L3 L3: r5 = a[r0] :: unsafe list @@ -1000,12 +1000,12 @@ def g(a, b): r13 :: bool r14 :: None L0: - r0 = iter a :: object + r0 = PyObject_GetIter(a) r1 = 0 r2 = 0 z = r2 L1: - r3 = next r0 :: object + r3 = PyIter_Next(r0) if is_error(r3) goto L6 else goto L2 L2: r4 = len b :: list diff --git a/mypyc/test-data/irbuild-try.test b/mypyc/test-data/irbuild-try.test index f8b3bfca5c12..6ac7faeaf3ba 100644 --- a/mypyc/test-data/irbuild-try.test +++ b/mypyc/test-data/irbuild-try.test @@ -20,7 +20,7 @@ L0: L1: r0 = builtins :: module r1 = unicode_1 :: static ('object') - r2 = getattr r0, r1 + r2 = CPyObject_GetAttr(r0, r1) r3 = py_call(r2) goto L5 L2: (handler for L1) @@ -28,7 +28,7 @@ L2: (handler for L1) r5 = unicode_2 :: static ('weeee') r6 = builtins :: module r7 = unicode_3 :: static ('print') - r8 = getattr r6, r7 + r8 = CPyObject_GetAttr(r6, r7) r9 = py_call(r8, r5) L3: CPy_RestoreExcInfo(r4) @@ -70,7 +70,7 @@ L1: L2: r0 = builtins :: module r1 = unicode_1 :: static ('object') - r2 = getattr r0, r1 + r2 = CPyObject_GetAttr(r0, r1) r3 = py_call(r2) goto L4 L3: @@ -83,7 +83,7 @@ L5: (handler for L1, L2, L3, L4) r7 = unicode_3 :: static ('weeee') r8 = builtins :: module r9 = unicode_4 :: static ('print') - r10 = getattr r8, r9 + r10 = CPyObject_GetAttr(r8, r9) r11 = py_call(r10, r7) L6: CPy_RestoreExcInfo(r6) @@ -137,19 +137,19 @@ L1: r0 = unicode_1 :: static ('a') r1 = builtins :: module r2 = unicode_2 :: static ('print') - r3 = getattr r1, r2 + r3 = CPyObject_GetAttr(r1, r2) r4 = py_call(r3, r0) L2: r5 = builtins :: module r6 = unicode_3 :: static ('object') - r7 = getattr r5, r6 + r7 = CPyObject_GetAttr(r5, r6) r8 = py_call(r7) goto L8 L3: (handler for L2) r9 = CPy_CatchError() r10 = builtins :: module r11 = unicode_4 :: static ('AttributeError') - r12 = getattr r10, r11 + r12 = CPyObject_GetAttr(r10, r11) r13 = CPy_ExceptionMatches(r12) if r13 goto L4 else goto L5 :: bool L4: @@ -158,7 +158,7 @@ L4: r15 = unicode_5 :: static ('b') r16 = builtins :: module r17 = unicode_2 :: static ('print') - r18 = getattr r16, r17 + r18 = CPyObject_GetAttr(r16, r17) r19 = py_call(r18, r15, e) goto L6 L5: @@ -178,7 +178,7 @@ L9: (handler for L1, L6, L7, L8) r22 = unicode_6 :: static ('weeee') r23 = builtins :: module r24 = unicode_2 :: static ('print') - r25 = getattr r23, r24 + r25 = CPyObject_GetAttr(r23, r24) r26 = py_call(r25, r22) L10: CPy_RestoreExcInfo(r21) @@ -226,27 +226,27 @@ L2: (handler for L1) r0 = CPy_CatchError() r1 = builtins :: module r2 = unicode_1 :: static ('KeyError') - r3 = getattr r1, r2 + r3 = CPyObject_GetAttr(r1, r2) r4 = CPy_ExceptionMatches(r3) if r4 goto L3 else goto L4 :: bool L3: r5 = unicode_2 :: static ('weeee') r6 = builtins :: module r7 = unicode_3 :: static ('print') - r8 = getattr r6, r7 + r8 = CPyObject_GetAttr(r6, r7) r9 = py_call(r8, r5) goto L7 L4: r10 = builtins :: module r11 = unicode_4 :: static ('IndexError') - r12 = getattr r10, r11 + r12 = CPyObject_GetAttr(r10, r11) r13 = CPy_ExceptionMatches(r12) if r13 goto L5 else goto L6 :: bool L5: r14 = unicode_5 :: static ('yo') r15 = builtins :: module r16 = unicode_3 :: static ('print') - r17 = getattr r15, r16 + r17 = CPyObject_GetAttr(r15, r16) r18 = py_call(r17, r14) goto L7 L6: @@ -291,7 +291,7 @@ L2: r0 = unicode_1 :: static ('hi') r1 = builtins :: module r2 = unicode_2 :: static ('Exception') - r3 = getattr r1, r2 + r3 = CPyObject_GetAttr(r1, r2) r4 = py_call(r3, r0) CPy_Raise(r4) unreachable @@ -308,7 +308,7 @@ L7: r8 = unicode_3 :: static ('finally') r9 = builtins :: module r10 = unicode_4 :: static ('print') - r11 = getattr r9, r10 + r11 = CPyObject_GetAttr(r9, r10) r12 = py_call(r11, r8) if is_error(r5) goto L9 else goto L8 L8: @@ -358,9 +358,9 @@ L0: r0 = py_call(x) r1 = PyObject_Type(r0) r2 = unicode_3 :: static ('__exit__') - r3 = getattr r1, r2 + r3 = CPyObject_GetAttr(r1, r2) r4 = unicode_4 :: static ('__enter__') - r5 = getattr r1, r4 + r5 = CPyObject_GetAttr(r1, r4) r6 = py_call(r5, r0) r7 = True r8 = r7 @@ -370,7 +370,7 @@ L2: r9 = unicode_5 :: static ('hello') r10 = builtins :: module r11 = unicode_6 :: static ('print') - r12 = getattr r10, r11 + r12 = CPyObject_GetAttr(r10, r11) r13 = py_call(r12, r9) goto L8 L3: (handler for L2) From 781caffa5c325b197e2c206a7322e671d2fee442 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 5 Aug 2020 07:59:30 -0700 Subject: [PATCH 105/138] When copying a ForStmt or WithStmt, copy is_async flag (#9268) (The test only tests 'async with', but 'async for' had the same bug.) Fixes #9261. --- mypy/treetransform.py | 2 ++ test-data/unit/check-async-await.test | 13 +++++++++++++ test-data/unit/fixtures/typing-async.pyi | 5 +++++ 3 files changed, 20 insertions(+) diff --git a/mypy/treetransform.py b/mypy/treetransform.py index 9bcc8175e046..06339a2a859a 100644 --- a/mypy/treetransform.py +++ b/mypy/treetransform.py @@ -250,6 +250,7 @@ def visit_for_stmt(self, node: ForStmt) -> ForStmt: self.block(node.body), self.optional_block(node.else_body), self.optional_type(node.unanalyzed_index_type)) + new.is_async = node.is_async new.index_type = self.optional_type(node.index_type) return new @@ -293,6 +294,7 @@ def visit_with_stmt(self, node: WithStmt) -> WithStmt: self.optional_expressions(node.target), self.block(node.body), self.optional_type(node.unanalyzed_type)) + new.is_async = node.is_async new.analyzed_types = [self.type(typ) for typ in node.analyzed_types] return new diff --git a/test-data/unit/check-async-await.test b/test-data/unit/check-async-await.test index 7fc5afef6732..033766fd9018 100644 --- a/test-data/unit/check-async-await.test +++ b/test-data/unit/check-async-await.test @@ -745,3 +745,16 @@ def t() -> None: [builtins fixtures/async_await.pyi] [typing fixtures/typing-async.pyi] + +[case testAsyncWithInGenericClass] +from typing import Generic, AsyncContextManager, TypeVar + +T = TypeVar('T', str, int) + +class Foo(Generic[T]): + async def foo(self, manager: AsyncContextManager): + async with manager: + pass + +[builtins fixtures/async_await.pyi] +[typing fixtures/typing-async.pyi] diff --git a/test-data/unit/fixtures/typing-async.pyi b/test-data/unit/fixtures/typing-async.pyi index 76449c2b51ee..b061337845c2 100644 --- a/test-data/unit/fixtures/typing-async.pyi +++ b/test-data/unit/fixtures/typing-async.pyi @@ -118,3 +118,8 @@ class ContextManager(Generic[T]): def __enter__(self) -> T: pass # Use Any because not all the precise types are in the fixtures. def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> Any: pass + +class AsyncContextManager(Generic[T]): + def __aenter__(self) -> Awaitable[T]: pass + # Use Any because not all the precise types are in the fixtures. + def __aexit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> Awaitable[Any]: pass From b10d5405efc32b68b230c77c799c406b8a8d53d9 Mon Sep 17 00:00:00 2001 From: Nikita Sobolev Date: Wed, 5 Aug 2020 21:05:52 +0300 Subject: [PATCH 106/138] Fixes corner case for comparing nested overloads (#9259) This is for a 3rd party plugin: dry-python/returns#410 - Closes #9147 - Closes #8978 --- mypy/subtypes.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index b0fc61a7c874..693af526aa72 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -399,6 +399,10 @@ def visit_overloaded(self, left: Overloaded) -> bool: return True return False elif isinstance(right, Overloaded): + if left == self.right: + # When it is the same overload, then the types are equal. + return True + # Ensure each overload in the right side (the supertype) is accounted for. previous_match_left_index = -1 matched_overloads = set() From 1bbcd536a59ce07fad78f6ec12b53e3859a03aaf Mon Sep 17 00:00:00 2001 From: Alexandre Viau Date: Wed, 5 Aug 2020 16:17:41 -0400 Subject: [PATCH 107/138] Check for deleted vars in 'raise from' (#9272) Thanks to @isra17. Fixes #9270 --- mypy/checker.py | 3 +++ test-data/unit/check-statements.test | 3 +++ 2 files changed, 6 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index 75739fe87a00..c012251dad9f 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -3317,6 +3317,9 @@ def visit_raise_stmt(self, s: RaiseStmt) -> None: def type_check_raise(self, e: Expression, s: RaiseStmt, optional: bool = False) -> None: typ = get_proper_type(self.expr_checker.accept(e)) + if isinstance(typ, DeletedType): + self.msg.deleted_as_rvalue(typ, e) + return exc_type = self.named_type('builtins.BaseException') expected_type = UnionType([exc_type, TypeType(exc_type)]) if optional: diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test index 530588575f97..4502d18bb6f7 100644 --- a/test-data/unit/check-statements.test +++ b/test-data/unit/check-statements.test @@ -446,9 +446,12 @@ raise x # E: Exception must be derived from BaseException e = None # type: BaseException f = None # type: MyError a = None # type: A +x = None # type: BaseException +del x raise e from a # E: Exception must be derived from BaseException raise e from e raise e from f +raise e from x # E: Trying to read deleted variable 'x' class A: pass class MyError(BaseException): pass [builtins fixtures/exception.pyi] From 68763aecddbb4edad90e7ed896db08ef9acbdfd5 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Fri, 7 Aug 2020 18:26:03 +0800 Subject: [PATCH 108/138] [mypyc] Implement builtins.len primitive for list (#9271) * implement list len primitive * use pointer type * rename * add PyObject * use CPyPtr * revert RStruct design and fix tests * list_len helper and updates according to review comments * fix * remove size_t_to_short_int --- mypyc/codegen/emitfunc.py | 3 +- mypyc/ir/ops.py | 9 +- mypyc/ir/rtypes.py | 62 +++-- mypyc/irbuild/builder.py | 7 +- mypyc/irbuild/for_helpers.py | 4 +- mypyc/irbuild/ll_builder.py | 15 +- mypyc/irbuild/specialize.py | 7 +- mypyc/lib-rt/mypyc_util.h | 1 + mypyc/primitives/list_ops.py | 10 +- mypyc/primitives/struct_regsitry.py | 24 -- mypyc/test-data/irbuild-basic.test | 304 +++++++++++++----------- mypyc/test-data/irbuild-lists.test | 50 ++-- mypyc/test-data/irbuild-statements.test | 194 ++++++++------- mypyc/test/test_emitfunc.py | 32 +-- mypyc/test/test_struct.py | 83 ++++--- 15 files changed, 417 insertions(+), 388 deletions(-) delete mode 100644 mypyc/primitives/struct_regsitry.py diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index 0dc0dd96900c..537f47cf6acd 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -477,7 +477,8 @@ def visit_get_element_ptr(self, op: GetElementPtr) -> None: # TODO: support tuple type assert isinstance(op.src_type, RStruct) assert op.field in op.src_type.names, "Invalid field name." - self.emit_line('%s = &%s.%s;' % (dest, src, op.field)) + self.emit_line('%s = (%s)&((%s *)%s)->%s;' % (dest, op.type._ctype, op.src_type.name, + src, op.field)) # Helpers diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index 12d7e32db230..e90ccafd29ac 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -25,8 +25,7 @@ from mypyc.ir.rtypes import ( RType, RInstance, RTuple, RVoid, is_bool_rprimitive, is_int_rprimitive, is_short_int_rprimitive, is_none_rprimitive, object_rprimitive, bool_rprimitive, - short_int_rprimitive, int_rprimitive, void_rtype, is_c_py_ssize_t_rprimitive, - c_pyssize_t_rprimitive + short_int_rprimitive, int_rprimitive, void_rtype, pointer_rprimitive, is_pointer_rprimitive ) from mypyc.common import short_name @@ -1360,7 +1359,7 @@ def __init__(self, type: RType, src: Value, line: int = -1) -> None: self.type = type # TODO: for now we enforce that the src memory address should be Py_ssize_t # later we should also support same width unsigned int - assert is_c_py_ssize_t_rprimitive(src.type) + assert is_pointer_rprimitive(src.type) self.src = src def sources(self) -> List[Value]: @@ -1379,7 +1378,7 @@ class GetElementPtr(RegisterOp): def __init__(self, src: Value, src_type: RType, field: str, line: int = -1) -> None: super().__init__(line) - self.type = c_pyssize_t_rprimitive + self.type = pointer_rprimitive self.src = src self.src_type = src_type self.field = field @@ -1388,7 +1387,7 @@ def sources(self) -> List[Value]: return [self.src] def to_str(self, env: Environment) -> str: - return env.format("%r = get_element_ptr %r %r :: %r", self, self.src, + return env.format("%r = get_element_ptr %r %s :: %r", self, self.src, self.field, self.src_type) def accept(self, visitor: 'OpVisitor[T]') -> T: diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index 46be550cec05..fe5abe4fb1b9 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -182,8 +182,10 @@ def __init__(self, self.size = size # TODO: For low-level integers, they actually don't have undefined values # we need to figure out some way to represent here. - if ctype in ('CPyTagged', 'int32_t', 'int64_t'): + if ctype == 'CPyTagged': self.c_undefined = 'CPY_INT_TAG' + elif ctype in ('int32_t', 'int64_t', 'CPyPtr'): + self.c_undefined = '0' elif ctype == 'PyObject *': # Boxed types use the null pointer as the error value. self.c_undefined = 'NULL' @@ -254,6 +256,10 @@ def __repr__(self) -> str: else: c_pyssize_t_rprimitive = int64_rprimitive +# low level pointer, represented as integer in C backends +pointer_rprimitive = RPrimitive('ptr', is_unboxed=True, is_refcounted=False, + ctype='CPyPtr') # type: Final + # Floats are represent as 'float' PyObject * values. (In the future # we'll likely switch to a more efficient, unboxed representation.) float_rprimitive = RPrimitive('builtins.float', is_unboxed=False, @@ -311,6 +317,10 @@ def is_c_py_ssize_t_rprimitive(rtype: RType) -> bool: return rtype is c_pyssize_t_rprimitive +def is_pointer_rprimitive(rtype: RType) -> bool: + return rtype is pointer_rprimitive + + def is_float_rprimitive(rtype: RType) -> bool: return isinstance(rtype, RPrimitive) and rtype.name == 'builtins.float' @@ -514,12 +524,8 @@ def compute_aligned_offsets_and_size(types: List[RType]) -> Tuple[List[int], int return offsets, final_size -class StructInfo: - """Struct-like type Infomation - - StructInfo should work with registry to ensure constraints like the unique naming - constraint for struct type - """ +class RStruct(RType): + """Represent CPython structs""" def __init__(self, name: str, names: List[str], @@ -532,31 +538,7 @@ def __init__(self, for i in range(len(self.types) - len(self.names)): self.names.append('_item' + str(i)) self.offsets, self.size = compute_aligned_offsets_and_size(types) - - -class RStruct(RType): - """Represent CPython structs""" - def __init__(self, - info: StructInfo) -> None: - self.info = info - self.name = self.info.name - self._ctype = self.info.name - - @property - def names(self) -> List[str]: - return self.info.names - - @property - def types(self) -> List[RType]: - return self.info.types - - @property - def offsets(self) -> List[int]: - return self.info.offsets - - @property - def size(self) -> int: - return self.info.size + self._ctype = name def accept(self, visitor: 'RTypeVisitor[T]') -> T: return visitor.visit_rstruct(self) @@ -571,10 +553,11 @@ def __repr__(self) -> str: in zip(self.names, self.types))) def __eq__(self, other: object) -> bool: - return isinstance(other, RStruct) and self.info == other.info + return (isinstance(other, RStruct) and self.name == other.name + and self.names == other.names and self.types == other.types) def __hash__(self) -> int: - return hash(self.info) + return hash((self.name, tuple(self.names), tuple(self.types))) def serialize(self) -> JsonDict: assert False @@ -687,3 +670,14 @@ def optional_value_type(rtype: RType) -> Optional[RType]: def is_optional_type(rtype: RType) -> bool: """Is rtype an optional type with exactly two union items?""" return optional_value_type(rtype) is not None + + +PyObject = RStruct( + name='PyObject', + names=['ob_refcnt', 'ob_type'], + types=[c_pyssize_t_rprimitive, pointer_rprimitive]) + +PyVarObject = RStruct( + name='PyVarObject', + names=['ob_base', 'ob_size'], + types=[PyObject, c_pyssize_t_rprimitive]) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 1b087c501aa3..53bd50f2a845 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -44,7 +44,7 @@ from mypyc.ir.func_ir import FuncIR, INVALID_FUNC_DEF from mypyc.ir.class_ir import ClassIR, NonExtClassInfo from mypyc.primitives.registry import func_ops, CFunctionDescription, c_function_ops -from mypyc.primitives.list_ops import list_len_op, to_list, list_pop_last +from mypyc.primitives.list_ops import to_list, list_pop_last from mypyc.primitives.dict_ops import dict_get_item_op, dict_set_item_op from mypyc.primitives.generic_ops import py_setattr_op, iter_op, next_op from mypyc.primitives.misc_ops import true_op, false_op, import_op @@ -238,6 +238,9 @@ def binary_int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: int) def compare_tagged(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: return self.builder.compare_tagged(lhs, rhs, op, line) + def list_len(self, val: Value, line: int) -> Value: + return self.builder.list_len(val, line) + @property def environment(self) -> Environment: return self.builder.environment @@ -508,7 +511,7 @@ def process_iterator_tuple_assignment(self, if target.star_idx is not None: post_star_vals = target.items[split_idx + 1:] iter_list = self.call_c(to_list, [iterator], line) - iter_list_len = self.primitive_op(list_len_op, [iter_list], line) + iter_list_len = self.list_len(iter_list, line) post_star_len = self.add(LoadInt(len(post_star_vals))) condition = self.binary_op(post_star_len, iter_list_len, '<=', line) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index 56e8cf8b816f..a1e3583aa1d0 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -29,7 +29,6 @@ from mypyc.primitives.exc_ops import no_err_occurred_op from mypyc.irbuild.builder import IRBuilder - GenFunc = Callable[[], None] @@ -333,6 +332,9 @@ def gen_cleanup(self) -> None: def load_len(self, expr: Union[Value, AssignmentTarget]) -> Value: """A helper to get collection length, used by several subclasses.""" + val = self.builder.read(expr, self.line) + if is_list_rprimitive(val.type): + return self.builder.builder.list_len(self.builder.read(expr, self.line), self.line) return self.builder.builder.builtin_call( [self.builder.read(expr, self.line)], 'builtins.len', diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index f8d5e5d8e04f..216f3f582a96 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -21,12 +21,13 @@ Assign, Branch, Goto, Call, Box, Unbox, Cast, GetAttr, LoadStatic, MethodCall, PrimitiveOp, OpDescription, RegisterOp, CallC, Truncate, RaiseStandardError, Unreachable, LoadErrorValue, LoadGlobal, - NAMESPACE_TYPE, NAMESPACE_MODULE, NAMESPACE_STATIC, BinaryIntOp + NAMESPACE_TYPE, NAMESPACE_MODULE, NAMESPACE_STATIC, BinaryIntOp, GetElementPtr, + LoadMem ) from mypyc.ir.rtypes import ( RType, RUnion, RInstance, optional_value_type, int_rprimitive, float_rprimitive, bool_rprimitive, list_rprimitive, str_rprimitive, is_none_rprimitive, object_rprimitive, - c_pyssize_t_rprimitive, is_short_int_rprimitive, is_tagged + c_pyssize_t_rprimitive, is_short_int_rprimitive, is_tagged, PyVarObject, short_int_rprimitive ) from mypyc.ir.func_ir import FuncDecl, FuncSignature from mypyc.ir.class_ir import ClassIR, all_concrete_classes @@ -40,7 +41,7 @@ c_binary_ops, c_unary_ops ) from mypyc.primitives.list_ops import ( - list_extend_op, list_len_op, new_list_op + list_extend_op, new_list_op ) from mypyc.primitives.tuple_ops import list_tuple_op, new_tuple_op from mypyc.primitives.dict_ops import ( @@ -703,7 +704,7 @@ def add_bool_branch(self, value: Value, true: BasicBlock, false: BasicBlock) -> zero = self.add(LoadInt(0)) value = self.binary_op(value, zero, '!=', value.line) elif is_same_type(value.type, list_rprimitive): - length = self.primitive_op(list_len_op, [value], value.line) + length = self.list_len(value, value.line) zero = self.add(LoadInt(0)) value = self.binary_op(length, zero, '!=', value.line) elif (isinstance(value.type, RInstance) and value.type.class_ir.is_ext_class @@ -810,6 +811,12 @@ def matching_call_c(self, def binary_int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: int) -> Value: return self.add(BinaryIntOp(type, lhs, rhs, op, line)) + def list_len(self, val: Value, line: int) -> Value: + elem_address = self.add(GetElementPtr(val, PyVarObject, 'ob_size')) + size_value = self.add(LoadMem(c_pyssize_t_rprimitive, elem_address)) + offset = self.add(LoadInt(1, -1, rtype=c_pyssize_t_rprimitive)) + return self.binary_int_op(short_int_rprimitive, size_value, offset, + BinaryIntOp.LEFT_SHIFT, -1) # Internal helpers def decompose_union_helper(self, diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index d10387c26f6b..bb6b146f413e 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -18,11 +18,11 @@ from mypy.types import AnyType, TypeOfAny from mypyc.ir.ops import ( - Value, BasicBlock, LoadInt, RaiseStandardError, Unreachable, OpDescription + Value, BasicBlock, LoadInt, RaiseStandardError, Unreachable, OpDescription, ) from mypyc.ir.rtypes import ( RType, RTuple, str_rprimitive, list_rprimitive, dict_rprimitive, set_rprimitive, - bool_rprimitive, is_dict_rprimitive + bool_rprimitive, is_dict_rprimitive, is_list_rprimitive, ) from mypyc.primitives.dict_ops import dict_keys_op, dict_values_op, dict_items_op from mypyc.primitives.misc_ops import true_op, false_op @@ -75,6 +75,9 @@ def translate_len( # though we still need to evaluate it. builder.accept(expr.args[0]) return builder.add(LoadInt(len(expr_rtype.types))) + elif is_list_rprimitive(expr_rtype): + obj = builder.accept(expr.args[0]) + return builder.list_len(obj, -1) return None diff --git a/mypyc/lib-rt/mypyc_util.h b/mypyc/lib-rt/mypyc_util.h index 217533de9d32..ed4e09c14cd1 100644 --- a/mypyc/lib-rt/mypyc_util.h +++ b/mypyc/lib-rt/mypyc_util.h @@ -32,6 +32,7 @@ #define CPy_XDECREF(p) Py_XDECREF(p) typedef size_t CPyTagged; +typedef size_t CPyPtr; #define CPY_INT_BITS (CHAR_BIT * sizeof(CPyTagged)) diff --git a/mypyc/primitives/list_ops.py b/mypyc/primitives/list_ops.py index 24546e9f1914..7768f2bd0af4 100644 --- a/mypyc/primitives/list_ops.py +++ b/mypyc/primitives/list_ops.py @@ -8,7 +8,7 @@ c_int_rprimitive ) from mypyc.primitives.registry import ( - name_ref_op, func_op, custom_op, name_emit, + name_ref_op, custom_op, name_emit, call_emit, c_function_op, c_binary_op, c_method_op ) @@ -146,11 +146,3 @@ def emit_len(emitter: EmitterInterface, args: List[str], dest: str) -> None: emitter.emit_declaration('Py_ssize_t %s;' % temp) emitter.emit_line('%s = PyList_GET_SIZE(%s);' % (temp, args[0])) emitter.emit_line('%s = CPyTagged_ShortFromSsize_t(%s);' % (dest, temp)) - - -# len(list) -list_len_op = func_op(name='builtins.len', - arg_types=[list_rprimitive], - result_type=short_int_rprimitive, - error_kind=ERR_NEVER, - emit=emit_len) diff --git a/mypyc/primitives/struct_regsitry.py b/mypyc/primitives/struct_regsitry.py deleted file mode 100644 index 4233889c8fb8..000000000000 --- a/mypyc/primitives/struct_regsitry.py +++ /dev/null @@ -1,24 +0,0 @@ -"""Struct registries for C backend""" - -from typing import List, NamedTuple -from mypyc.ir.rtypes import RType - -CStructDescription = NamedTuple( - 'CStructDescription', [('name', str), - ('names', List[str]), - ('types', List[RType])]) - - -def c_struct(name: str, - names: List[str], - types: List[RType]) -> CStructDescription: - """Define a known C struct for generating IR to manipulate it - - name: The name of the C struct - types: type of each field - names: name of each field - TODO: the names list can be empty in the future when we merge Tuple as part of Struct - """ - return CStructDescription(name, names, types) - -# TODO: create PyVarObject, to do which we probably need PyObject diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 6a6cd086642c..951cf06a9809 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -1400,12 +1400,16 @@ L6: unreachable def lst(x): x :: list - r0 :: short_int - r1 :: bool + r0 :: ptr + r1 :: native_int + r2 :: short_int + r3 :: bool L0: - r0 = len x :: list - r1 = r0 != 0 - if r1 goto L1 else goto L2 :: bool + r0 = get_element_ptr x ob_size :: PyVarObject + r1 = load_mem r0 :: native_int* + r2 = r1 << 1 + r3 = r2 != 0 + if r3 goto L1 else goto L2 :: bool L1: return 2 L2: @@ -2007,20 +2011,23 @@ def f(): r0 :: list r1, r2, r3 :: object r4 :: list - r5, r6 :: short_int - r7 :: bool - r8 :: object - x, r9 :: int - r10 :: bool - r11 :: native_int - r12, r13, r14, r15 :: bool - r16 :: bool - r17 :: native_int - r18, r19, r20, r21 :: bool - r22 :: int - r23 :: object - r24 :: int32 - r25 :: short_int + r5 :: short_int + r6 :: ptr + r7 :: native_int + r8 :: short_int + r9 :: bool + r10 :: object + x, r11 :: int + r12 :: bool + r13 :: native_int + r14, r15, r16, r17 :: bool + r18 :: bool + r19 :: native_int + r20, r21, r22, r23 :: bool + r24 :: int + r25 :: object + r26 :: int32 + r27 :: short_int L0: r0 = [] r1 = box(short_int, 2) @@ -2029,51 +2036,53 @@ L0: r4 = [r1, r2, r3] r5 = 0 L1: - r6 = len r4 :: list - r7 = r5 < r6 :: signed - if r7 goto L2 else goto L14 :: bool + r6 = get_element_ptr r4 ob_size :: PyVarObject + r7 = load_mem r6 :: native_int* + r8 = r7 << 1 + r9 = r5 < r8 :: signed + if r9 goto L2 else goto L14 :: bool L2: - r8 = r4[r5] :: unsafe list - r9 = unbox(int, r8) - x = r9 - r11 = x & 1 - r12 = r11 == 0 - if r12 goto L3 else goto L4 :: bool + r10 = r4[r5] :: unsafe list + r11 = unbox(int, r10) + x = r11 + r13 = x & 1 + r14 = r13 == 0 + if r14 goto L3 else goto L4 :: bool L3: - r13 = x != 4 - r10 = r13 + r15 = x != 4 + r12 = r15 goto L5 L4: - r14 = CPyTagged_IsEq_(x, 4) - r15 = !r14 - r10 = r15 + r16 = CPyTagged_IsEq_(x, 4) + r17 = !r16 + r12 = r17 L5: - if r10 goto L7 else goto L6 :: bool + if r12 goto L7 else goto L6 :: bool L6: goto L13 L7: - r17 = x & 1 - r18 = r17 == 0 - if r18 goto L8 else goto L9 :: bool + r19 = x & 1 + r20 = r19 == 0 + if r20 goto L8 else goto L9 :: bool L8: - r19 = x != 6 - r16 = r19 + r21 = x != 6 + r18 = r21 goto L10 L9: - r20 = CPyTagged_IsEq_(x, 6) - r21 = !r20 - r16 = r21 + r22 = CPyTagged_IsEq_(x, 6) + r23 = !r22 + r18 = r23 L10: - if r16 goto L12 else goto L11 :: bool + if r18 goto L12 else goto L11 :: bool L11: goto L13 L12: - r22 = CPyTagged_Multiply(x, x) - r23 = box(int, r22) - r24 = PyList_Append(r0, r23) + r24 = CPyTagged_Multiply(x, x) + r25 = box(int, r24) + r26 = PyList_Append(r0, r25) L13: - r25 = r5 + 2 - r5 = r25 + r27 = r5 + 2 + r5 = r27 goto L1 L14: return r0 @@ -2087,20 +2096,23 @@ def f(): r0 :: dict r1, r2, r3 :: object r4 :: list - r5, r6 :: short_int - r7 :: bool - r8 :: object - x, r9 :: int - r10 :: bool - r11 :: native_int - r12, r13, r14, r15 :: bool - r16 :: bool - r17 :: native_int - r18, r19, r20, r21 :: bool - r22 :: int - r23, r24 :: object - r25 :: int32 - r26 :: short_int + r5 :: short_int + r6 :: ptr + r7 :: native_int + r8 :: short_int + r9 :: bool + r10 :: object + x, r11 :: int + r12 :: bool + r13 :: native_int + r14, r15, r16, r17 :: bool + r18 :: bool + r19 :: native_int + r20, r21, r22, r23 :: bool + r24 :: int + r25, r26 :: object + r27 :: int32 + r28 :: short_int L0: r0 = PyDict_New() r1 = box(short_int, 2) @@ -2109,52 +2121,54 @@ L0: r4 = [r1, r2, r3] r5 = 0 L1: - r6 = len r4 :: list - r7 = r5 < r6 :: signed - if r7 goto L2 else goto L14 :: bool + r6 = get_element_ptr r4 ob_size :: PyVarObject + r7 = load_mem r6 :: native_int* + r8 = r7 << 1 + r9 = r5 < r8 :: signed + if r9 goto L2 else goto L14 :: bool L2: - r8 = r4[r5] :: unsafe list - r9 = unbox(int, r8) - x = r9 - r11 = x & 1 - r12 = r11 == 0 - if r12 goto L3 else goto L4 :: bool + r10 = r4[r5] :: unsafe list + r11 = unbox(int, r10) + x = r11 + r13 = x & 1 + r14 = r13 == 0 + if r14 goto L3 else goto L4 :: bool L3: - r13 = x != 4 - r10 = r13 + r15 = x != 4 + r12 = r15 goto L5 L4: - r14 = CPyTagged_IsEq_(x, 4) - r15 = !r14 - r10 = r15 + r16 = CPyTagged_IsEq_(x, 4) + r17 = !r16 + r12 = r17 L5: - if r10 goto L7 else goto L6 :: bool + if r12 goto L7 else goto L6 :: bool L6: goto L13 L7: - r17 = x & 1 - r18 = r17 == 0 - if r18 goto L8 else goto L9 :: bool + r19 = x & 1 + r20 = r19 == 0 + if r20 goto L8 else goto L9 :: bool L8: - r19 = x != 6 - r16 = r19 + r21 = x != 6 + r18 = r21 goto L10 L9: - r20 = CPyTagged_IsEq_(x, 6) - r21 = !r20 - r16 = r21 + r22 = CPyTagged_IsEq_(x, 6) + r23 = !r22 + r18 = r23 L10: - if r16 goto L12 else goto L11 :: bool + if r18 goto L12 else goto L11 :: bool L11: goto L13 L12: - r22 = CPyTagged_Multiply(x, x) - r23 = box(int, x) - r24 = box(int, r22) - r25 = CPyDict_SetItem(r0, r23, r24) + r24 = CPyTagged_Multiply(x, x) + r25 = box(int, x) + r26 = box(int, r24) + r27 = CPyDict_SetItem(r0, r25, r26) L13: - r26 = r5 + 2 - r5 = r26 + r28 = r5 + 2 + r5 = r28 goto L1 L14: return r0 @@ -2168,68 +2182,78 @@ def f(l: List[Tuple[int, int, int]]) -> List[int]: [out] def f(l): l :: list - r0, r1 :: short_int - r2 :: bool - r3 :: object + r0 :: short_int + r1 :: ptr + r2 :: native_int + r3 :: short_int + r4 :: bool + r5 :: object x, y, z :: int - r4 :: tuple[int, int, int] - r5, r6, r7 :: int - r8 :: short_int - r9 :: list - r10, r11 :: short_int - r12 :: bool - r13 :: object + r6 :: tuple[int, int, int] + r7, r8, r9 :: int + r10 :: short_int + r11 :: list + r12 :: short_int + r13 :: ptr + r14 :: native_int + r15 :: short_int + r16 :: bool + r17 :: object x0, y0, z0 :: int - r14 :: tuple[int, int, int] - r15, r16, r17, r18, r19 :: int - r20 :: object - r21 :: int32 - r22 :: short_int + r18 :: tuple[int, int, int] + r19, r20, r21, r22, r23 :: int + r24 :: object + r25 :: int32 + r26 :: short_int L0: r0 = 0 L1: - r1 = len l :: list - r2 = r0 < r1 :: signed - if r2 goto L2 else goto L4 :: bool + r1 = get_element_ptr l ob_size :: PyVarObject + r2 = load_mem r1 :: native_int* + r3 = r2 << 1 + r4 = r0 < r3 :: signed + if r4 goto L2 else goto L4 :: bool L2: - r3 = l[r0] :: unsafe list - r4 = unbox(tuple[int, int, int], r3) - r5 = r4[0] - x = r5 - r6 = r4[1] - y = r6 - r7 = r4[2] - z = r7 + r5 = l[r0] :: unsafe list + r6 = unbox(tuple[int, int, int], r5) + r7 = r6[0] + x = r7 + r8 = r6[1] + y = r8 + r9 = r6[2] + z = r9 L3: - r8 = r0 + 2 - r0 = r8 + r10 = r0 + 2 + r0 = r10 goto L1 L4: - r9 = [] - r10 = 0 + r11 = [] + r12 = 0 L5: - r11 = len l :: list - r12 = r10 < r11 :: signed - if r12 goto L6 else goto L8 :: bool + r13 = get_element_ptr l ob_size :: PyVarObject + r14 = load_mem r13 :: native_int* + r15 = r14 << 1 + r16 = r12 < r15 :: signed + if r16 goto L6 else goto L8 :: bool L6: - r13 = l[r10] :: unsafe list - r14 = unbox(tuple[int, int, int], r13) - r15 = r14[0] - x0 = r15 - r16 = r14[1] - y0 = r16 - r17 = r14[2] - z0 = r17 - r18 = CPyTagged_Add(x0, y0) - r19 = CPyTagged_Add(r18, z0) - r20 = box(int, r19) - r21 = PyList_Append(r9, r20) + r17 = l[r12] :: unsafe list + r18 = unbox(tuple[int, int, int], r17) + r19 = r18[0] + x0 = r19 + r20 = r18[1] + y0 = r20 + r21 = r18[2] + z0 = r21 + r22 = CPyTagged_Add(x0, y0) + r23 = CPyTagged_Add(r22, z0) + r24 = box(int, r23) + r25 = PyList_Append(r11, r24) L7: - r22 = r10 + 2 - r10 = r22 + r26 = r12 + 2 + r12 = r26 goto L5 L8: - return r9 + return r11 [case testProperty] class PropertyHolder: diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test index 76c39fc183eb..cd28ec286791 100644 --- a/mypyc/test-data/irbuild-lists.test +++ b/mypyc/test-data/irbuild-lists.test @@ -120,10 +120,14 @@ def f(a: List[int]) -> int: [out] def f(a): a :: list - r0 :: short_int + r0 :: ptr + r1 :: native_int + r2 :: short_int L0: - r0 = len a :: list - return r0 + r0 = get_element_ptr a ob_size :: PyVarObject + r1 = load_mem r0 :: native_int* + r2 = r1 << 1 + return r2 [case testListAppend] from typing import List @@ -152,29 +156,33 @@ def increment(l: List[int]) -> List[int]: [out] def increment(l): l :: list - r0, r1 :: short_int + r0 :: ptr + r1 :: native_int + r2, r3 :: short_int i :: int - r2 :: bool - r3 :: object - r4, r5 :: object - r6 :: bool - r7 :: short_int + r4 :: bool + r5 :: object + r6, r7 :: object + r8 :: bool + r9 :: short_int L0: - r0 = len l :: list - r1 = 0 - i = r1 + r0 = get_element_ptr l ob_size :: PyVarObject + r1 = load_mem r0 :: native_int* + r2 = r1 << 1 + r3 = 0 + i = r3 L1: - r2 = r1 < r0 :: signed - if r2 goto L2 else goto L4 :: bool + r4 = r3 < r2 :: signed + if r4 goto L2 else goto L4 :: bool L2: - r3 = CPyList_GetItem(l, i) - r4 = box(short_int, 2) - r5 = PyNumber_InPlaceAdd(r3, r4) - r6 = CPyList_SetItem(l, i, r5) + r5 = CPyList_GetItem(l, i) + r6 = box(short_int, 2) + r7 = PyNumber_InPlaceAdd(r5, r6) + r8 = CPyList_SetItem(l, i, r7) L3: - r7 = r1 + 2 - r1 = r7 - i = r7 + r9 = r3 + 2 + r3 = r9 + i = r9 goto L1 L4: return l diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index a3768d343cfb..39b5958cede9 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -326,27 +326,32 @@ def f(ls: List[int]) -> int: def f(ls): ls :: list y :: int - r0, r1 :: short_int - r2 :: bool - r3 :: object - x, r4, r5 :: int - r6 :: short_int + r0 :: short_int + r1 :: ptr + r2 :: native_int + r3 :: short_int + r4 :: bool + r5 :: object + x, r6, r7 :: int + r8 :: short_int L0: y = 0 r0 = 0 L1: - r1 = len ls :: list - r2 = r0 < r1 :: signed - if r2 goto L2 else goto L4 :: bool + r1 = get_element_ptr ls ob_size :: PyVarObject + r2 = load_mem r1 :: native_int* + r3 = r2 << 1 + r4 = r0 < r3 :: signed + if r4 goto L2 else goto L4 :: bool L2: - r3 = ls[r0] :: unsafe list - r4 = unbox(int, r3) - x = r4 - r5 = CPyTagged_Add(y, x) - y = r5 + r5 = ls[r0] :: unsafe list + r6 = unbox(int, r5) + x = r6 + r7 = CPyTagged_Add(y, x) + y = r7 L3: - r6 = r0 + 2 - r0 = r6 + r8 = r0 + 2 + r0 = r8 goto L1 L4: return y @@ -869,36 +874,41 @@ def f(a): a :: list r0 :: short_int i :: int - r1, r2 :: short_int - r3 :: bool - r4 :: object - x, r5, r6 :: int - r7, r8 :: short_int - r9 :: None + r1 :: short_int + r2 :: ptr + r3 :: native_int + r4 :: short_int + r5 :: bool + r6 :: object + x, r7, r8 :: int + r9, r10 :: short_int + r11 :: None L0: r0 = 0 i = 0 r1 = 0 L1: - r2 = len a :: list - r3 = r1 < r2 :: signed - if r3 goto L2 else goto L4 :: bool + r2 = get_element_ptr a ob_size :: PyVarObject + r3 = load_mem r2 :: native_int* + r4 = r3 << 1 + r5 = r1 < r4 :: signed + if r5 goto L2 else goto L4 :: bool L2: - r4 = a[r1] :: unsafe list - r5 = unbox(int, r4) - x = r5 - r6 = CPyTagged_Add(i, x) + r6 = a[r1] :: unsafe list + r7 = unbox(int, r6) + x = r7 + r8 = CPyTagged_Add(i, x) L3: - r7 = r0 + 2 - r0 = r7 - i = r7 - r8 = r1 + 2 - r1 = r8 + r9 = r0 + 2 + r0 = r9 + i = r9 + r10 = r1 + 2 + r1 = r10 goto L1 L4: L5: - r9 = None - return r9 + r11 = None + return r11 def g(x): x :: object r0 :: short_int @@ -946,44 +956,48 @@ def f(a, b): b :: object r0 :: short_int r1 :: object - r2 :: short_int - r3 :: bool - r4, r5 :: object - x, r6 :: int - r7, y, r8 :: bool - r9 :: short_int - r10 :: bool - r11 :: None + r2 :: ptr + r3 :: native_int + r4 :: short_int + r5 :: bool + r6, r7 :: object + x, r8 :: int + r9, y, r10 :: bool + r11 :: short_int + r12 :: bool + r13 :: None L0: r0 = 0 r1 = PyObject_GetIter(b) L1: - r2 = len a :: list - r3 = r0 < r2 :: signed - if r3 goto L2 else goto L7 :: bool + r2 = get_element_ptr a ob_size :: PyVarObject + r3 = load_mem r2 :: native_int* + r4 = r3 << 1 + r5 = r0 < r4 :: signed + if r5 goto L2 else goto L7 :: bool L2: - r4 = PyIter_Next(r1) - if is_error(r4) goto L7 else goto L3 + r6 = PyIter_Next(r1) + if is_error(r6) goto L7 else goto L3 L3: - r5 = a[r0] :: unsafe list - r6 = unbox(int, r5) - x = r6 - r7 = unbox(bool, r4) - y = r7 - r8 = bool b :: object - if r8 goto L4 else goto L5 :: bool + r7 = a[r0] :: unsafe list + r8 = unbox(int, r7) + x = r8 + r9 = unbox(bool, r6) + y = r9 + r10 = bool b :: object + if r10 goto L4 else goto L5 :: bool L4: x = 2 L5: L6: - r9 = r0 + 2 - r0 = r9 + r11 = r0 + 2 + r0 = r11 goto L1 L7: - r10 = CPy_NoErrOccured() + r12 = CPy_NoErrOccured() L8: - r11 = None - return r11 + r13 = None + return r13 def g(a, b): a :: object b :: list @@ -991,14 +1005,16 @@ def g(a, b): r1, r2 :: short_int z :: int r3 :: object - r4 :: short_int - r5, r6, r7, x :: bool - r8 :: object - y, r9 :: int - r10 :: bool - r11, r12 :: short_int - r13 :: bool - r14 :: None + r4 :: ptr + r5 :: native_int + r6 :: short_int + r7, r8, r9, x :: bool + r10 :: object + y, r11 :: int + r12 :: bool + r13, r14 :: short_int + r15 :: bool + r16 :: None L0: r0 = PyObject_GetIter(a) r1 = 0 @@ -1008,30 +1024,32 @@ L1: r3 = PyIter_Next(r0) if is_error(r3) goto L6 else goto L2 L2: - r4 = len b :: list - r5 = r1 < r4 :: signed - if r5 goto L3 else goto L6 :: bool + r4 = get_element_ptr b ob_size :: PyVarObject + r5 = load_mem r4 :: native_int* + r6 = r5 << 1 + r7 = r1 < r6 :: signed + if r7 goto L3 else goto L6 :: bool L3: - r6 = r2 < 10 :: signed - if r6 goto L4 else goto L6 :: bool + r8 = r2 < 10 :: signed + if r8 goto L4 else goto L6 :: bool L4: - r7 = unbox(bool, r3) - x = r7 - r8 = b[r1] :: unsafe list - r9 = unbox(int, r8) - y = r9 - r10 = False - x = r10 + r9 = unbox(bool, r3) + x = r9 + r10 = b[r1] :: unsafe list + r11 = unbox(int, r10) + y = r11 + r12 = False + x = r12 L5: - r11 = r1 + 2 - r1 = r11 - r12 = r2 + 2 - r2 = r12 - z = r12 + r13 = r1 + 2 + r1 = r13 + r14 = r2 + 2 + r2 = r14 + z = r14 goto L1 L6: - r13 = CPy_NoErrOccured() + r15 = CPy_NoErrOccured() L7: - r14 = None - return r14 + r16 = None + return r16 diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index a2e821e9148b..a1954eda7348 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -15,7 +15,7 @@ from mypyc.ir.rtypes import ( RTuple, RInstance, int_rprimitive, bool_rprimitive, list_rprimitive, dict_rprimitive, object_rprimitive, c_int_rprimitive, short_int_rprimitive, int32_rprimitive, - int64_rprimitive, StructInfo, RStruct + int64_rprimitive, RStruct, pointer_rprimitive ) from mypyc.ir.func_ir import FuncIR, FuncDecl, RuntimeArg, FuncSignature from mypyc.ir.class_ir import ClassIR @@ -25,7 +25,7 @@ from mypyc.primitives.registry import binary_ops, c_binary_ops from mypyc.primitives.misc_ops import none_object_op, true_op, false_op from mypyc.primitives.list_ops import ( - list_len_op, list_get_item_op, list_set_item_op, new_list_op, list_append_op + list_get_item_op, list_set_item_op, new_list_op, list_append_op ) from mypyc.primitives.dict_ops import ( dict_new_op, dict_update_op, dict_get_item_op, dict_set_item_op @@ -33,7 +33,6 @@ from mypyc.primitives.int_ops import int_neg_op from mypyc.subtype import is_subtype from mypyc.namegen import NameGenerator -from mypyc.common import IS_32_BIT_PLATFORM class TestFunctionEmitterVisitor(unittest.TestCase): @@ -54,6 +53,7 @@ def setUp(self) -> None: self.i32_1 = self.env.add_local(Var('i32_1'), int32_rprimitive) self.i64 = self.env.add_local(Var('i64'), int64_rprimitive) self.i64_1 = self.env.add_local(Var('i64_1'), int64_rprimitive) + self.ptr = self.env.add_local(Var('ptr'), pointer_rprimitive) self.t = self.env.add_local(Var('t'), RTuple([int_rprimitive, bool_rprimitive])) self.tt = self.env.add_local( Var('tt'), @@ -117,13 +117,6 @@ def test_int_neg(self) -> None: int_neg_op.steals, int_neg_op.error_kind, 55), "cpy_r_r0 = CPyTagged_Negate(cpy_r_m);") - def test_list_len(self) -> None: - self.assert_emit(PrimitiveOp([self.l], list_len_op, 55), - """Py_ssize_t __tmp1; - __tmp1 = PyList_GET_SIZE(cpy_r_l); - cpy_r_r0 = CPyTagged_ShortFromSsize_t(__tmp1); - """) - def test_branch(self) -> None: self.assert_emit(Branch(self.b, BasicBlock(8), BasicBlock(9), Branch.BOOL_EXPR), """if (cpy_r_b) { @@ -275,23 +268,18 @@ def test_binary_int_op(self) -> None: """cpy_r_r04 = (uint64_t)cpy_r_i64 < (uint64_t)cpy_r_i64_1;""") def test_load_mem(self) -> None: - if IS_32_BIT_PLATFORM: - self.assert_emit(LoadMem(bool_rprimitive, self.i32), - """cpy_r_r0 = *(char *)cpy_r_i32;""") - else: - self.assert_emit(LoadMem(bool_rprimitive, self.i64), - """cpy_r_r0 = *(char *)cpy_r_i64;""") + self.assert_emit(LoadMem(bool_rprimitive, self.ptr), + """cpy_r_r0 = *(char *)cpy_r_ptr;""") def test_get_element_ptr(self) -> None: - info = StructInfo("Foo", ["b", "i32", "i64"], [bool_rprimitive, - int32_rprimitive, int64_rprimitive]) - r = RStruct(info) + r = RStruct("Foo", ["b", "i32", "i64"], [bool_rprimitive, + int32_rprimitive, int64_rprimitive]) self.assert_emit(GetElementPtr(self.o, r, "b"), - """cpy_r_r0 = &cpy_r_o.b;""") + """cpy_r_r0 = (CPyPtr)&((Foo *)cpy_r_o)->b;""") self.assert_emit(GetElementPtr(self.o, r, "i32"), - """cpy_r_r00 = &cpy_r_o.i32;""") + """cpy_r_r00 = (CPyPtr)&((Foo *)cpy_r_o)->i32;""") self.assert_emit(GetElementPtr(self.o, r, "i64"), - """cpy_r_r01 = &cpy_r_o.i64;""") + """cpy_r_r01 = (CPyPtr)&((Foo *)cpy_r_o)->i64;""") def assert_emit(self, op: Op, expected: str) -> None: self.emitter.fragments = [] diff --git a/mypyc/test/test_struct.py b/mypyc/test/test_struct.py index 0478b891063b..0617f83bbb38 100644 --- a/mypyc/test/test_struct.py +++ b/mypyc/test/test_struct.py @@ -1,7 +1,7 @@ import unittest from mypyc.ir.rtypes import ( - RStruct, bool_rprimitive, int64_rprimitive, int32_rprimitive, object_rprimitive, StructInfo, + RStruct, bool_rprimitive, int64_rprimitive, int32_rprimitive, object_rprimitive, int_rprimitive ) from mypyc.rt_subtype import is_runtime_subtype @@ -10,93 +10,106 @@ class TestStruct(unittest.TestCase): def test_struct_offsets(self) -> None: # test per-member alignment - info = StructInfo("", [], [bool_rprimitive, int32_rprimitive, int64_rprimitive]) - r = RStruct(info) + r = RStruct("", [], [bool_rprimitive, int32_rprimitive, int64_rprimitive]) assert r.size == 16 assert r.offsets == [0, 4, 8] # test final alignment - info1 = StructInfo("", [], [bool_rprimitive, bool_rprimitive]) - r1 = RStruct(info1) + r1 = RStruct("", [], [bool_rprimitive, bool_rprimitive]) assert r1.size == 2 assert r1.offsets == [0, 1] - info2 = StructInfo("", [], [int32_rprimitive, bool_rprimitive]) - r2 = RStruct(info2) - info3 = StructInfo("", [], [int64_rprimitive, bool_rprimitive]) - r3 = RStruct(info3) + r2 = RStruct("", [], [int32_rprimitive, bool_rprimitive]) + r3 = RStruct("", [], [int64_rprimitive, bool_rprimitive]) assert r2.offsets == [0, 4] assert r3.offsets == [0, 8] assert r2.size == 8 assert r3.size == 16 - info4 = StructInfo("", [], [bool_rprimitive, bool_rprimitive, + r4 = RStruct("", [], [bool_rprimitive, bool_rprimitive, bool_rprimitive, int32_rprimitive]) - r4 = RStruct(info4) assert r4.size == 8 assert r4.offsets == [0, 1, 2, 4] # test nested struct - info5 = StructInfo("", [], [bool_rprimitive, r]) - r5 = RStruct(info5) + r5 = RStruct("", [], [bool_rprimitive, r]) assert r5.offsets == [0, 8] assert r5.size == 24 - info6 = StructInfo("", [], [int32_rprimitive, r5]) - r6 = RStruct(info6) + r6 = RStruct("", [], [int32_rprimitive, r5]) assert r6.offsets == [0, 8] assert r6.size == 32 # test nested struct with alignment less than 8 - info7 = StructInfo("", [], [bool_rprimitive, r4]) - r7 = RStruct(info7) + r7 = RStruct("", [], [bool_rprimitive, r4]) assert r7.offsets == [0, 4] assert r7.size == 12 def test_struct_str(self) -> None: - info = StructInfo("Foo", ["a", "b"], + r = RStruct("Foo", ["a", "b"], [bool_rprimitive, object_rprimitive]) - r = RStruct(info) assert str(r) == "Foo{a:bool, b:object}" assert repr(r) == ", " \ "b:}>" - info1 = StructInfo("Bar", ["c"], [int32_rprimitive]) - r1 = RStruct(info1) + r1 = RStruct("Bar", ["c"], [int32_rprimitive]) assert str(r1) == "Bar{c:int32}" assert repr(r1) == "}>" - info2 = StructInfo("Baz", [], []) - r2 = RStruct(info2) + r2 = RStruct("Baz", [], []) assert str(r2) == "Baz{}" assert repr(r2) == "" def test_runtime_subtype(self) -> None: # right type to check with - info = StructInfo("Foo", ["a", "b"], + r = RStruct("Foo", ["a", "b"], [bool_rprimitive, int_rprimitive]) - r = RStruct(info) - # using the same StructInfo - r1 = RStruct(info) + # using the exact same fields + r1 = RStruct("Foo", ["a", "b"], + [bool_rprimitive, int_rprimitive]) # names different - info2 = StructInfo("Bar", ["c", "b"], + r2 = RStruct("Bar", ["c", "b"], [bool_rprimitive, int_rprimitive]) - r2 = RStruct(info2) # name different - info3 = StructInfo("Baz", ["a", "b"], + r3 = RStruct("Baz", ["a", "b"], [bool_rprimitive, int_rprimitive]) - r3 = RStruct(info3) # type different - info4 = StructInfo("FooBar", ["a", "b"], + r4 = RStruct("FooBar", ["a", "b"], [bool_rprimitive, int32_rprimitive]) - r4 = RStruct(info4) # number of types different - info5 = StructInfo("FooBarBaz", ["a", "b", "c"], + r5 = RStruct("FooBarBaz", ["a", "b", "c"], [bool_rprimitive, int_rprimitive, bool_rprimitive]) - r5 = RStruct(info5) assert is_runtime_subtype(r1, r) is True assert is_runtime_subtype(r2, r) is False assert is_runtime_subtype(r3, r) is False assert is_runtime_subtype(r4, r) is False assert is_runtime_subtype(r5, r) is False + + def test_eq_and_hash(self) -> None: + r = RStruct("Foo", ["a", "b"], + [bool_rprimitive, int_rprimitive]) + + # using the exact same fields + r1 = RStruct("Foo", ["a", "b"], + [bool_rprimitive, int_rprimitive]) + assert hash(r) == hash(r1) + assert r == r1 + + # different name + r2 = RStruct("Foq", ["a", "b"], + [bool_rprimitive, int_rprimitive]) + assert hash(r) != hash(r2) + assert r != r2 + + # different names + r3 = RStruct("Foo", ["a", "c"], + [bool_rprimitive, int_rprimitive]) + assert hash(r) != hash(r3) + assert r != r3 + + # different type + r4 = RStruct("Foo", ["a", "b"], + [bool_rprimitive, int_rprimitive, bool_rprimitive]) + assert hash(r) != hash(r4) + assert r != r4 From e2a7d08675a8e30fc25a039cbbd01d664980a9a5 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Fri, 7 Aug 2020 20:58:10 +0800 Subject: [PATCH 109/138] [mypyc] Clean up unused int op definitions (#9276) This PR cleans up unused int op definitions including unsafe_short_add and int_compare_op. unsafe_short_add's usages have been replaced with low-level integer op in #9108 and int_compare_op has no usages since all comparison operators are now built in irbuild. --- mypyc/primitives/int_ops.py | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index b2e6960c378a..e0416a0d256b 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -9,11 +9,11 @@ from typing import Dict, NamedTuple from mypyc.ir.ops import ERR_NEVER, ERR_MAGIC, BinaryIntOp from mypyc.ir.rtypes import ( - int_rprimitive, bool_rprimitive, float_rprimitive, object_rprimitive, short_int_rprimitive, + int_rprimitive, bool_rprimitive, float_rprimitive, object_rprimitive, str_rprimitive, RType ) from mypyc.primitives.registry import ( - name_ref_op, binary_op, custom_op, simple_emit, name_emit, + name_ref_op, name_emit, c_unary_op, CFunctionDescription, c_function_op, c_binary_op, c_custom_op ) @@ -81,20 +81,6 @@ def int_binary_op(name: str, c_function_name: str, error_kind=error_kind) -def int_compare_op(name: str, c_function_name: str) -> None: - int_binary_op(name, c_function_name, bool_rprimitive) - # Generate a straight compare if we know both sides are short - op = name - binary_op(op=op, - arg_types=[short_int_rprimitive, short_int_rprimitive], - result_type=bool_rprimitive, - error_kind=ERR_NEVER, - format_str='{dest} = {args[0]} %s {args[1]} :: short_int' % op, - emit=simple_emit( - '{dest} = (Py_ssize_t){args[0]} %s (Py_ssize_t){args[1]};' % op), - priority=2) - - # Binary, unary and augmented assignment operations that operate on CPyTagged ints. int_binary_op('+', 'CPyTagged_Add') @@ -114,15 +100,6 @@ def int_compare_op(name: str, c_function_name: str) -> None: int_binary_op('//=', 'CPyTagged_FloorDivide', error_kind=ERR_MAGIC) int_binary_op('%=', 'CPyTagged_Remainder', error_kind=ERR_MAGIC) -# Add short integers and assume that it doesn't overflow or underflow. -# Assume that the operands are not big integers. -unsafe_short_add = custom_op( - arg_types=[int_rprimitive, int_rprimitive], - result_type=short_int_rprimitive, - error_kind=ERR_NEVER, - format_str='{dest} = {args[0]} + {args[1]} :: short_int', - emit=simple_emit('{dest} = {args[0]} + {args[1]};')) - def int_unary_op(name: str, c_function_name: str) -> CFunctionDescription: return c_unary_op(name=name, From 5e9682143720263466f0aaed1924ccb218160ee0 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 7 Aug 2020 18:51:09 +0100 Subject: [PATCH 110/138] Sync typeshed (#9277) --- mypy/typeshed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/typeshed b/mypy/typeshed index 8bf7efe94e1f..199b262f6315 160000 --- a/mypy/typeshed +++ b/mypy/typeshed @@ -1 +1 @@ -Subproject commit 8bf7efe94e1fc0f43aaf352b3c2260bd24073f5c +Subproject commit 199b262f6315f7c83b4fb0e7f7676d7ad506cb7d From 88eb84ec182c3e638aa15fe0ce7c9673059def7b Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Mon, 10 Aug 2020 18:50:24 +0800 Subject: [PATCH 111/138] [mypyc] Implement new-style builtins.len for all supported types (#9284) This PR completes the support of the new style builtins.len for dict, set, tuple and generic cases. --- mypyc/ir/rtypes.py | 18 +++ mypyc/irbuild/builder.py | 6 +- mypyc/irbuild/for_helpers.py | 9 +- mypyc/irbuild/ll_builder.py | 38 +++-- mypyc/irbuild/specialize.py | 6 +- mypyc/primitives/dict_ops.py | 27 +--- mypyc/primitives/generic_ops.py | 13 +- mypyc/primitives/set_ops.py | 22 +-- mypyc/primitives/tuple_ops.py | 24 +-- mypyc/test-data/irbuild-dict.test | 192 ++++++++++++------------ mypyc/test-data/irbuild-set.test | 10 +- mypyc/test-data/irbuild-statements.test | 150 +++++++++--------- mypyc/test-data/irbuild-tuple.test | 63 ++++---- mypyc/test-data/refcount.test | 69 ++++----- 14 files changed, 316 insertions(+), 331 deletions(-) diff --git a/mypyc/ir/rtypes.py b/mypyc/ir/rtypes.py index fe5abe4fb1b9..3438ea76c2d1 100644 --- a/mypyc/ir/rtypes.py +++ b/mypyc/ir/rtypes.py @@ -681,3 +681,21 @@ def is_optional_type(rtype: RType) -> bool: name='PyVarObject', names=['ob_base', 'ob_size'], types=[PyObject, c_pyssize_t_rprimitive]) + +setentry = RStruct( + name='setentry', + names=['key', 'hash'], + types=[pointer_rprimitive, c_pyssize_t_rprimitive]) + +smalltable = RStruct( + name='smalltable', + names=[], + types=[setentry] * 8) + +PySetObject = RStruct( + name='PySetObject', + names=['ob_base', 'fill', 'used', 'mask', 'table', 'hash', 'finger', + 'smalltable', 'weakreflist'], + types=[PyObject, c_pyssize_t_rprimitive, c_pyssize_t_rprimitive, c_pyssize_t_rprimitive, + pointer_rprimitive, c_pyssize_t_rprimitive, c_pyssize_t_rprimitive, smalltable, + pointer_rprimitive]) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 53bd50f2a845..e770a31ba85b 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -238,8 +238,8 @@ def binary_int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: int) def compare_tagged(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: return self.builder.compare_tagged(lhs, rhs, op, line) - def list_len(self, val: Value, line: int) -> Value: - return self.builder.list_len(val, line) + def builtin_len(self, val: Value, line: int) -> Value: + return self.builder.builtin_len(val, line) @property def environment(self) -> Environment: @@ -511,7 +511,7 @@ def process_iterator_tuple_assignment(self, if target.star_idx is not None: post_star_vals = target.items[split_idx + 1:] iter_list = self.call_c(to_list, [iterator], line) - iter_list_len = self.list_len(iter_list, line) + iter_list_len = self.builtin_len(iter_list, line) post_star_len = self.add(LoadInt(len(post_star_vals))) condition = self.binary_op(post_star_len, iter_list_len, '<=', line) diff --git a/mypyc/irbuild/for_helpers.py b/mypyc/irbuild/for_helpers.py index a1e3583aa1d0..7b3aa0dc952a 100644 --- a/mypyc/irbuild/for_helpers.py +++ b/mypyc/irbuild/for_helpers.py @@ -332,14 +332,7 @@ def gen_cleanup(self) -> None: def load_len(self, expr: Union[Value, AssignmentTarget]) -> Value: """A helper to get collection length, used by several subclasses.""" - val = self.builder.read(expr, self.line) - if is_list_rprimitive(val.type): - return self.builder.builder.list_len(self.builder.read(expr, self.line), self.line) - return self.builder.builder.builtin_call( - [self.builder.read(expr, self.line)], - 'builtins.len', - self.line, - ) + return self.builder.builder.builtin_len(self.builder.read(expr, self.line), self.line) class ForIterable(ForGenerator): diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 216f3f582a96..43766f988f5f 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -27,7 +27,8 @@ from mypyc.ir.rtypes import ( RType, RUnion, RInstance, optional_value_type, int_rprimitive, float_rprimitive, bool_rprimitive, list_rprimitive, str_rprimitive, is_none_rprimitive, object_rprimitive, - c_pyssize_t_rprimitive, is_short_int_rprimitive, is_tagged, PyVarObject, short_int_rprimitive + c_pyssize_t_rprimitive, is_short_int_rprimitive, is_tagged, PyVarObject, short_int_rprimitive, + is_list_rprimitive, is_tuple_rprimitive, is_dict_rprimitive, is_set_rprimitive, PySetObject ) from mypyc.ir.func_ir import FuncDecl, FuncSignature from mypyc.ir.class_ir import ClassIR, all_concrete_classes @@ -45,10 +46,10 @@ ) from mypyc.primitives.tuple_ops import list_tuple_op, new_tuple_op from mypyc.primitives.dict_ops import ( - dict_update_in_display_op, dict_new_op, dict_build_op + dict_update_in_display_op, dict_new_op, dict_build_op, dict_size_op ) from mypyc.primitives.generic_ops import ( - py_getattr_op, py_call_op, py_call_with_kwargs_op, py_method_call_op + py_getattr_op, py_call_op, py_call_with_kwargs_op, py_method_call_op, generic_len_op ) from mypyc.primitives.misc_ops import ( none_op, none_object_op, false_op, fast_isinstance_op, bool_op, type_is_op @@ -704,7 +705,7 @@ def add_bool_branch(self, value: Value, true: BasicBlock, false: BasicBlock) -> zero = self.add(LoadInt(0)) value = self.binary_op(value, zero, '!=', value.line) elif is_same_type(value.type, list_rprimitive): - length = self.list_len(value, value.line) + length = self.builtin_len(value, value.line) zero = self.add(LoadInt(0)) value = self.binary_op(length, zero, '!=', value.line) elif (isinstance(value.type, RInstance) and value.type.class_ir.is_ext_class @@ -811,12 +812,29 @@ def matching_call_c(self, def binary_int_op(self, type: RType, lhs: Value, rhs: Value, op: int, line: int) -> Value: return self.add(BinaryIntOp(type, lhs, rhs, op, line)) - def list_len(self, val: Value, line: int) -> Value: - elem_address = self.add(GetElementPtr(val, PyVarObject, 'ob_size')) - size_value = self.add(LoadMem(c_pyssize_t_rprimitive, elem_address)) - offset = self.add(LoadInt(1, -1, rtype=c_pyssize_t_rprimitive)) - return self.binary_int_op(short_int_rprimitive, size_value, offset, - BinaryIntOp.LEFT_SHIFT, -1) + def builtin_len(self, val: Value, line: int) -> Value: + typ = val.type + if is_list_rprimitive(typ) or is_tuple_rprimitive(typ): + elem_address = self.add(GetElementPtr(val, PyVarObject, 'ob_size')) + size_value = self.add(LoadMem(c_pyssize_t_rprimitive, elem_address)) + offset = self.add(LoadInt(1, line, rtype=c_pyssize_t_rprimitive)) + return self.binary_int_op(short_int_rprimitive, size_value, offset, + BinaryIntOp.LEFT_SHIFT, line) + elif is_dict_rprimitive(typ): + size_value = self.call_c(dict_size_op, [val], line) + offset = self.add(LoadInt(1, line, rtype=c_pyssize_t_rprimitive)) + return self.binary_int_op(short_int_rprimitive, size_value, offset, + BinaryIntOp.LEFT_SHIFT, line) + elif is_set_rprimitive(typ): + elem_address = self.add(GetElementPtr(val, PySetObject, 'used')) + size_value = self.add(LoadMem(c_pyssize_t_rprimitive, elem_address)) + offset = self.add(LoadInt(1, line, rtype=c_pyssize_t_rprimitive)) + return self.binary_int_op(short_int_rprimitive, size_value, offset, + BinaryIntOp.LEFT_SHIFT, line) + # generic case + else: + return self.call_c(generic_len_op, [val], line) + # Internal helpers def decompose_union_helper(self, diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index bb6b146f413e..f99798db1aea 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -22,7 +22,7 @@ ) from mypyc.ir.rtypes import ( RType, RTuple, str_rprimitive, list_rprimitive, dict_rprimitive, set_rprimitive, - bool_rprimitive, is_dict_rprimitive, is_list_rprimitive, + bool_rprimitive, is_dict_rprimitive ) from mypyc.primitives.dict_ops import dict_keys_op, dict_values_op, dict_items_op from mypyc.primitives.misc_ops import true_op, false_op @@ -75,9 +75,9 @@ def translate_len( # though we still need to evaluate it. builder.accept(expr.args[0]) return builder.add(LoadInt(len(expr_rtype.types))) - elif is_list_rprimitive(expr_rtype): + else: obj = builder.accept(expr.args[0]) - return builder.list_len(obj, -1) + return builder.builtin_len(obj, -1) return None diff --git a/mypyc/primitives/dict_ops.py b/mypyc/primitives/dict_ops.py index 47a2712709c1..04cea8a7e0f0 100644 --- a/mypyc/primitives/dict_ops.py +++ b/mypyc/primitives/dict_ops.py @@ -1,8 +1,6 @@ """Primitive dict ops.""" -from typing import List - -from mypyc.ir.ops import EmitterInterface, ERR_FALSE, ERR_MAGIC, ERR_NEVER, ERR_NEG_INT +from mypyc.ir.ops import ERR_FALSE, ERR_MAGIC, ERR_NEVER, ERR_NEG_INT from mypyc.ir.rtypes import ( dict_rprimitive, object_rprimitive, bool_rprimitive, int_rprimitive, list_rprimitive, dict_next_rtuple_single, dict_next_rtuple_pair, c_pyssize_t_rprimitive, @@ -10,7 +8,7 @@ ) from mypyc.primitives.registry import ( - name_ref_op, method_op, func_op, + name_ref_op, method_op, simple_emit, name_emit, c_custom_op, c_method_op, c_function_op, c_binary_op ) @@ -168,21 +166,6 @@ c_function_name='CPyDict_Items', error_kind=ERR_MAGIC) - -def emit_len(emitter: EmitterInterface, args: List[str], dest: str) -> None: - temp = emitter.temp_name() - emitter.emit_declaration('Py_ssize_t %s;' % temp) - emitter.emit_line('%s = PyDict_Size(%s);' % (temp, args[0])) - emitter.emit_line('%s = CPyTagged_ShortFromSsize_t(%s);' % (dest, temp)) - - -# len(dict) -func_op(name='builtins.len', - arg_types=[dict_rprimitive], - result_type=int_rprimitive, - error_kind=ERR_NEVER, - emit=emit_len) - # PyDict_Next() fast iteration dict_key_iter_op = c_custom_op( arg_types=[dict_rprimitive], @@ -226,3 +209,9 @@ def emit_len(emitter: EmitterInterface, args: List[str], dest: str) -> None: return_type=bool_rprimitive, c_function_name='CPyDict_CheckSize', error_kind=ERR_FALSE) + +dict_size_op = c_custom_op( + arg_types=[dict_rprimitive], + return_type=c_pyssize_t_rprimitive, + c_function_name='PyDict_Size', + error_kind=ERR_NEVER) diff --git a/mypyc/primitives/generic_ops.py b/mypyc/primitives/generic_ops.py index 39d8f6cd279f..21ca44c12f5d 100644 --- a/mypyc/primitives/generic_ops.py +++ b/mypyc/primitives/generic_ops.py @@ -12,7 +12,7 @@ from mypyc.ir.ops import ERR_NEVER, ERR_MAGIC, ERR_NEG_INT from mypyc.ir.rtypes import object_rprimitive, int_rprimitive, bool_rprimitive, c_int_rprimitive from mypyc.primitives.registry import ( - binary_op, unary_op, func_op, custom_op, call_emit, simple_emit, + binary_op, unary_op, custom_op, call_emit, simple_emit, call_negative_magic_emit, negative_int_emit, c_binary_op, c_unary_op, c_method_op, c_function_op, c_custom_op ) @@ -226,12 +226,11 @@ emit=simple_emit('{dest} = CPyObject_CallMethodObjArgs({comma_args}, NULL);')) # len(obj) -func_op(name='builtins.len', - arg_types=[object_rprimitive], - result_type=int_rprimitive, - error_kind=ERR_NEVER, - emit=call_emit('CPyObject_Size'), - priority=0) +generic_len_op = c_custom_op( + arg_types=[object_rprimitive], + return_type=int_rprimitive, + c_function_name='CPyObject_Size', + error_kind=ERR_NEVER) # iter(obj) iter_op = c_function_op(name='builtins.iter', diff --git a/mypyc/primitives/set_ops.py b/mypyc/primitives/set_ops.py index fd929829b2ed..51c100ce9bd4 100644 --- a/mypyc/primitives/set_ops.py +++ b/mypyc/primitives/set_ops.py @@ -3,11 +3,10 @@ from mypyc.primitives.registry import ( func_op, simple_emit, c_function_op, c_method_op, c_binary_op ) -from mypyc.ir.ops import ERR_MAGIC, ERR_FALSE, ERR_NEVER, ERR_NEG_INT, EmitterInterface +from mypyc.ir.ops import ERR_MAGIC, ERR_FALSE, ERR_NEG_INT from mypyc.ir.rtypes import ( - object_rprimitive, bool_rprimitive, set_rprimitive, int_rprimitive, c_int_rprimitive + object_rprimitive, bool_rprimitive, set_rprimitive, c_int_rprimitive ) -from typing import List # Construct an empty set. @@ -35,23 +34,6 @@ c_function_name='PyFrozenSet_New', error_kind=ERR_MAGIC) - -def emit_len(emitter: EmitterInterface, args: List[str], dest: str) -> None: - temp = emitter.temp_name() - emitter.emit_declaration('Py_ssize_t %s;' % temp) - emitter.emit_line('%s = PySet_GET_SIZE(%s);' % (temp, args[0])) - emitter.emit_line('%s = CPyTagged_ShortFromSsize_t(%s);' % (dest, temp)) - - -# len(set) -func_op( - name='builtins.len', - arg_types=[set_rprimitive], - result_type=int_rprimitive, - error_kind=ERR_NEVER, - emit=emit_len, -) - # item in set c_binary_op( name='in', diff --git a/mypyc/primitives/tuple_ops.py b/mypyc/primitives/tuple_ops.py index 4373fd5da18e..02746e7dd462 100644 --- a/mypyc/primitives/tuple_ops.py +++ b/mypyc/primitives/tuple_ops.py @@ -4,14 +4,10 @@ objects, i.e. tuple_rprimitive (RPrimitive), not RTuple. """ -from typing import List - -from mypyc.ir.ops import ( - EmitterInterface, ERR_NEVER, ERR_MAGIC -) +from mypyc.ir.ops import ERR_MAGIC from mypyc.ir.rtypes import tuple_rprimitive, int_rprimitive, list_rprimitive, object_rprimitive from mypyc.primitives.registry import ( - func_op, c_method_op, custom_op, simple_emit, c_function_op + c_method_op, custom_op, simple_emit, c_function_op ) @@ -33,22 +29,6 @@ format_str='{dest} = ({comma_args}) :: tuple', emit=simple_emit('{dest} = PyTuple_Pack({num_args}{comma_if_args}{comma_args});')) - -def emit_len(emitter: EmitterInterface, args: List[str], dest: str) -> None: - temp = emitter.temp_name() - emitter.emit_declaration('Py_ssize_t %s;' % temp) - emitter.emit_line('%s = PyTuple_GET_SIZE(%s);' % (temp, args[0])) - emitter.emit_line('%s = CPyTagged_ShortFromSsize_t(%s);' % (dest, temp)) - - -# len(tuple) -tuple_len_op = func_op( - name='builtins.len', - arg_types=[tuple_rprimitive], - result_type=int_rprimitive, - error_kind=ERR_NEVER, - emit=emit_len) - # Construct tuple from a list. list_tuple_op = c_function_op( name='builtins.tuple', diff --git a/mypyc/test-data/irbuild-dict.test b/mypyc/test-data/irbuild-dict.test index 064406c9f808..8296219a83d3 100644 --- a/mypyc/test-data/irbuild-dict.test +++ b/mypyc/test-data/irbuild-dict.test @@ -145,40 +145,42 @@ def increment(d: Dict[str, int]) -> Dict[str, int]: def increment(d): d :: dict r0 :: short_int - r1 :: int - r2 :: object - r3 :: tuple[bool, int, object] - r4 :: int - r5 :: bool - r6 :: object - k, r7 :: str - r8 :: object - r9, r10 :: object - r11 :: int32 - r12, r13 :: bool + r1 :: native_int + r2 :: short_int + r3 :: object + r4 :: tuple[bool, int, object] + r5 :: int + r6 :: bool + r7 :: object + k, r8 :: str + r9 :: object + r10, r11 :: object + r12 :: int32 + r13, r14 :: bool L0: r0 = 0 - r1 = len d :: dict - r2 = CPyDict_GetKeysIter(d) + r1 = PyDict_Size(d) + r2 = r1 << 1 + r3 = CPyDict_GetKeysIter(d) L1: - r3 = CPyDict_NextKey(r2, r0) - r4 = r3[1] - r0 = r4 - r5 = r3[0] - if r5 goto L2 else goto L4 :: bool + r4 = CPyDict_NextKey(r3, r0) + r5 = r4[1] + r0 = r5 + r6 = r4[0] + if r6 goto L2 else goto L4 :: bool L2: - r6 = r3[2] - r7 = cast(str, r6) - k = r7 - r8 = CPyDict_GetItem(d, k) - r9 = box(short_int, 2) - r10 = PyNumber_InPlaceAdd(r8, r9) - r11 = CPyDict_SetItem(d, k, r10) + r7 = r4[2] + r8 = cast(str, r7) + k = r8 + r9 = CPyDict_GetItem(d, k) + r10 = box(short_int, 2) + r11 = PyNumber_InPlaceAdd(r9, r10) + r12 = CPyDict_SetItem(d, k, r11) L3: - r12 = CPyDict_CheckSize(d, r1) + r13 = CPyDict_CheckSize(d, r2) goto L1 L4: - r13 = CPy_NoErrOccured() + r14 = CPy_NoErrOccured() L5: return d @@ -217,86 +219,90 @@ def print_dict_methods(d1: Dict[int, int], d2: Dict[int, int]) -> None: def print_dict_methods(d1, d2): d1, d2 :: dict r0 :: short_int - r1 :: int - r2 :: object - r3 :: tuple[bool, int, object] - r4 :: int - r5 :: bool - r6 :: object - v, r7 :: int - r8 :: object - r9 :: int32 - r10 :: bool - r11 :: None - r12, r13 :: bool - r14 :: short_int - r15 :: int - r16 :: object - r17 :: tuple[bool, int, object, object] - r18 :: int - r19 :: bool - r20, r21 :: object - r22, r23, k :: int - r24, r25, r26, r27, r28 :: object - r29 :: int32 - r30, r31 :: bool - r32 :: None + r1 :: native_int + r2 :: short_int + r3 :: object + r4 :: tuple[bool, int, object] + r5 :: int + r6 :: bool + r7 :: object + v, r8 :: int + r9 :: object + r10 :: int32 + r11 :: bool + r12 :: None + r13, r14 :: bool + r15 :: short_int + r16 :: native_int + r17 :: short_int + r18 :: object + r19 :: tuple[bool, int, object, object] + r20 :: int + r21 :: bool + r22, r23 :: object + r24, r25, k :: int + r26, r27, r28, r29, r30 :: object + r31 :: int32 + r32, r33 :: bool + r34 :: None L0: r0 = 0 - r1 = len d1 :: dict - r2 = CPyDict_GetValuesIter(d1) + r1 = PyDict_Size(d1) + r2 = r1 << 1 + r3 = CPyDict_GetValuesIter(d1) L1: - r3 = CPyDict_NextValue(r2, r0) - r4 = r3[1] - r0 = r4 - r5 = r3[0] - if r5 goto L2 else goto L6 :: bool + r4 = CPyDict_NextValue(r3, r0) + r5 = r4[1] + r0 = r5 + r6 = r4[0] + if r6 goto L2 else goto L6 :: bool L2: - r6 = r3[2] - r7 = unbox(int, r6) - v = r7 - r8 = box(int, v) - r9 = PyDict_Contains(d2, r8) - r10 = truncate r9: int32 to builtins.bool - if r10 goto L3 else goto L4 :: bool + r7 = r4[2] + r8 = unbox(int, r7) + v = r8 + r9 = box(int, v) + r10 = PyDict_Contains(d2, r9) + r11 = truncate r10: int32 to builtins.bool + if r11 goto L3 else goto L4 :: bool L3: - r11 = None - return r11 + r12 = None + return r12 L4: L5: - r12 = CPyDict_CheckSize(d1, r1) + r13 = CPyDict_CheckSize(d1, r2) goto L1 L6: - r13 = CPy_NoErrOccured() + r14 = CPy_NoErrOccured() L7: - r14 = 0 - r15 = len d2 :: dict - r16 = CPyDict_GetItemsIter(d2) + r15 = 0 + r16 = PyDict_Size(d2) + r17 = r16 << 1 + r18 = CPyDict_GetItemsIter(d2) L8: - r17 = CPyDict_NextItem(r16, r14) - r18 = r17[1] - r14 = r18 - r19 = r17[0] - if r19 goto L9 else goto L11 :: bool + r19 = CPyDict_NextItem(r18, r15) + r20 = r19[1] + r15 = r20 + r21 = r19[0] + if r21 goto L9 else goto L11 :: bool L9: - r20 = r17[2] - r21 = r17[3] - r22 = unbox(int, r20) - r23 = unbox(int, r21) - k = r22 - v = r23 - r24 = box(int, k) - r25 = CPyDict_GetItem(d2, r24) - r26 = box(int, v) - r27 = PyNumber_InPlaceAdd(r25, r26) - r28 = box(int, k) - r29 = CPyDict_SetItem(d2, r28, r27) + r22 = r19[2] + r23 = r19[3] + r24 = unbox(int, r22) + r25 = unbox(int, r23) + k = r24 + v = r25 + r26 = box(int, k) + r27 = CPyDict_GetItem(d2, r26) + r28 = box(int, v) + r29 = PyNumber_InPlaceAdd(r27, r28) + r30 = box(int, k) + r31 = CPyDict_SetItem(d2, r30, r29) L10: - r30 = CPyDict_CheckSize(d2, r15) + r32 = CPyDict_CheckSize(d2, r17) goto L8 L11: - r31 = CPy_NoErrOccured() + r33 = CPy_NoErrOccured() L12: - r32 = None - return r32 + r34 = None + return r34 diff --git a/mypyc/test-data/irbuild-set.test b/mypyc/test-data/irbuild-set.test index 02ec9f3b9a33..7a8e8edf2fc1 100644 --- a/mypyc/test-data/irbuild-set.test +++ b/mypyc/test-data/irbuild-set.test @@ -57,7 +57,9 @@ def f(): r4 :: int32 r5 :: object r6 :: int32 - r7 :: int + r7 :: ptr + r8 :: native_int + r9 :: short_int L0: r0 = set r1 = box(short_int, 2) @@ -66,8 +68,10 @@ L0: r4 = PySet_Add(r0, r3) r5 = box(short_int, 6) r6 = PySet_Add(r0, r5) - r7 = len r0 :: set - return r7 + r7 = get_element_ptr r0 used :: PySetObject + r8 = load_mem r7 :: native_int* + r9 = r8 << 1 + return r9 [case testSetContains] from typing import Set diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index 39b5958cede9..4140d42e9998 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -366,42 +366,44 @@ def f(d: Dict[int, int]) -> None: def f(d): d :: dict r0 :: short_int - r1 :: int - r2 :: object - r3 :: tuple[bool, int, object] - r4 :: int - r5 :: bool - r6 :: object - key, r7 :: int - r8, r9 :: object - r10 :: int - r11, r12 :: bool - r13 :: None + r1 :: native_int + r2 :: short_int + r3 :: object + r4 :: tuple[bool, int, object] + r5 :: int + r6 :: bool + r7 :: object + key, r8 :: int + r9, r10 :: object + r11 :: int + r12, r13 :: bool + r14 :: None L0: r0 = 0 - r1 = len d :: dict - r2 = CPyDict_GetKeysIter(d) + r1 = PyDict_Size(d) + r2 = r1 << 1 + r3 = CPyDict_GetKeysIter(d) L1: - r3 = CPyDict_NextKey(r2, r0) - r4 = r3[1] - r0 = r4 - r5 = r3[0] - if r5 goto L2 else goto L4 :: bool + r4 = CPyDict_NextKey(r3, r0) + r5 = r4[1] + r0 = r5 + r6 = r4[0] + if r6 goto L2 else goto L4 :: bool L2: - r6 = r3[2] - r7 = unbox(int, r6) - key = r7 - r8 = box(int, key) - r9 = CPyDict_GetItem(d, r8) - r10 = unbox(int, r9) + r7 = r4[2] + r8 = unbox(int, r7) + key = r8 + r9 = box(int, key) + r10 = CPyDict_GetItem(d, r9) + r11 = unbox(int, r10) L3: - r11 = CPyDict_CheckSize(d, r1) + r12 = CPyDict_CheckSize(d, r2) goto L1 L4: - r12 = CPy_NoErrOccured() + r13 = CPy_NoErrOccured() L5: - r13 = None - return r13 + r14 = None + return r14 [case testForDictContinue] from typing import Dict @@ -418,67 +420,69 @@ def sum_over_even_values(d): d :: dict s :: int r0 :: short_int - r1 :: int - r2 :: object - r3 :: tuple[bool, int, object] - r4 :: int - r5 :: bool - r6 :: object - key, r7 :: int - r8, r9 :: object - r10 :: int + r1 :: native_int + r2 :: short_int + r3 :: object + r4 :: tuple[bool, int, object] + r5 :: int + r6 :: bool + r7 :: object + key, r8 :: int + r9, r10 :: object r11 :: int - r12 :: bool - r13 :: native_int - r14, r15, r16, r17 :: bool - r18, r19 :: object - r20, r21 :: int - r22, r23 :: bool + r12 :: int + r13 :: bool + r14 :: native_int + r15, r16, r17, r18 :: bool + r19, r20 :: object + r21, r22 :: int + r23, r24 :: bool L0: s = 0 r0 = 0 - r1 = len d :: dict - r2 = CPyDict_GetKeysIter(d) + r1 = PyDict_Size(d) + r2 = r1 << 1 + r3 = CPyDict_GetKeysIter(d) L1: - r3 = CPyDict_NextKey(r2, r0) - r4 = r3[1] - r0 = r4 - r5 = r3[0] - if r5 goto L2 else goto L9 :: bool + r4 = CPyDict_NextKey(r3, r0) + r5 = r4[1] + r0 = r5 + r6 = r4[0] + if r6 goto L2 else goto L9 :: bool L2: - r6 = r3[2] - r7 = unbox(int, r6) - key = r7 - r8 = box(int, key) - r9 = CPyDict_GetItem(d, r8) - r10 = unbox(int, r9) - r11 = CPyTagged_Remainder(r10, 4) - r13 = r11 & 1 - r14 = r13 == 0 - if r14 goto L3 else goto L4 :: bool + r7 = r4[2] + r8 = unbox(int, r7) + key = r8 + r9 = box(int, key) + r10 = CPyDict_GetItem(d, r9) + r11 = unbox(int, r10) + r12 = CPyTagged_Remainder(r11, 4) + r14 = r12 & 1 + r15 = r14 == 0 + if r15 goto L3 else goto L4 :: bool L3: - r15 = r11 != 0 - r12 = r15 + r16 = r12 != 0 + r13 = r16 goto L5 L4: - r16 = CPyTagged_IsEq_(r11, 0) - r17 = !r16 - r12 = r17 + r17 = CPyTagged_IsEq_(r12, 0) + r18 = !r17 + r13 = r18 L5: - if r12 goto L6 else goto L7 :: bool + if r13 goto L6 else goto L7 :: bool L6: goto L8 L7: - r18 = box(int, key) - r19 = CPyDict_GetItem(d, r18) - r20 = unbox(int, r19) - r21 = CPyTagged_Add(s, r20) - s = r21 + r19 = box(int, key) + r20 = CPyDict_GetItem(d, r19) + r21 = unbox(int, r20) + r22 = CPyTagged_Add(s, r21) + s = r22 L8: - r22 = CPyDict_CheckSize(d, r1) + r23 = CPyDict_CheckSize(d, r2) goto L1 L9: - r23 = CPy_NoErrOccured() + r24 = CPy_NoErrOccured() L10: return s diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index 1c626955e33d..41c6bedf9d88 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -64,10 +64,14 @@ def f(x: Tuple[int, ...]) -> int: [out] def f(x): x :: tuple - r0 :: int + r0 :: ptr + r1 :: native_int + r2 :: short_int L0: - r0 = len x :: tuple - return r0 + r0 = get_element_ptr x ob_size :: PyVarObject + r1 = load_mem r0 :: native_int* + r2 = r1 << 1 + return r2 [case testSequenceTupleForced] from typing import Tuple @@ -121,46 +125,33 @@ def f(xs: Tuple[str, ...]) -> None: def f(xs): xs :: tuple r0 :: short_int - r1 :: int - r2 :: bool - r3 :: native_int + r1 :: ptr + r2 :: native_int + r3 :: short_int r4 :: bool - r5 :: native_int - r6, r7, r8, r9 :: bool - r10 :: object - x, r11 :: str - r12 :: short_int - r13 :: None + r5 :: object + x, r6 :: str + r7 :: short_int + r8 :: None L0: r0 = 0 L1: - r1 = len xs :: tuple - r3 = r0 & 1 - r4 = r3 == 0 - r5 = r1 & 1 - r6 = r5 == 0 - r7 = r4 & r6 - if r7 goto L2 else goto L3 :: bool + r1 = get_element_ptr xs ob_size :: PyVarObject + r2 = load_mem r1 :: native_int* + r3 = r2 << 1 + r4 = r0 < r3 :: signed + if r4 goto L2 else goto L4 :: bool L2: - r8 = r0 < r1 :: signed - r2 = r8 - goto L4 + r5 = CPySequenceTuple_GetItem(xs, r0) + r6 = cast(str, r5) + x = r6 L3: - r9 = CPyTagged_IsLt_(r0, r1) - r2 = r9 -L4: - if r2 goto L5 else goto L7 :: bool -L5: - r10 = CPySequenceTuple_GetItem(xs, r0) - r11 = cast(str, r10) - x = r11 -L6: - r12 = r0 + 2 - r0 = r12 + r7 = r0 + 2 + r0 = r7 goto L1 -L7: - r13 = None - return r13 +L4: + r8 = None + return r8 [case testNamedTupleAttribute] from typing import NamedTuple diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index d3f79322c8b2..39db7d05f017 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -777,51 +777,52 @@ def f(d: Dict[int, int]) -> None: def f(d): d :: dict r0 :: short_int - r1 :: int - r2 :: object - r3 :: tuple[bool, int, object] - r4 :: int - r5 :: bool - r6 :: object - key, r7 :: int - r8, r9 :: object - r10 :: int - r11, r12 :: bool - r13 :: None + r1 :: native_int + r2 :: short_int + r3 :: object + r4 :: tuple[bool, int, object] + r5 :: int + r6 :: bool + r7 :: object + key, r8 :: int + r9, r10 :: object + r11 :: int + r12, r13 :: bool + r14 :: None L0: r0 = 0 - r1 = len d :: dict - r2 = CPyDict_GetKeysIter(d) + r1 = PyDict_Size(d) + r2 = r1 << 1 + r3 = CPyDict_GetKeysIter(d) L1: - r3 = CPyDict_NextKey(r2, r0) - r4 = r3[1] - r0 = r4 - r5 = r3[0] - if r5 goto L2 else goto L6 :: bool + r4 = CPyDict_NextKey(r3, r0) + r5 = r4[1] + r0 = r5 + r6 = r4[0] + if r6 goto L2 else goto L6 :: bool L2: - r6 = r3[2] - dec_ref r3 - r7 = unbox(int, r6) - dec_ref r6 - key = r7 - r8 = box(int, key) - r9 = CPyDict_GetItem(d, r8) - dec_ref r8 - r10 = unbox(int, r9) + r7 = r4[2] + dec_ref r4 + r8 = unbox(int, r7) + dec_ref r7 + key = r8 + r9 = box(int, key) + r10 = CPyDict_GetItem(d, r9) dec_ref r9 - dec_ref r10 :: int + r11 = unbox(int, r10) + dec_ref r10 + dec_ref r11 :: int L3: - r11 = CPyDict_CheckSize(d, r1) + r12 = CPyDict_CheckSize(d, r2) goto L1 L4: - r12 = CPy_NoErrOccured() + r13 = CPy_NoErrOccured() L5: - r13 = None - return r13 + r14 = None + return r14 L6: - dec_ref r1 :: int - dec_ref r2 dec_ref r3 + dec_ref r4 goto L4 [case testBorrowRefs] From d0711bd33db2828529b062aabe41c1ea004546d1 Mon Sep 17 00:00:00 2001 From: Callum Wilkinson Date: Mon, 10 Aug 2020 16:12:01 +0100 Subject: [PATCH 112/138] Add support for --enable-error-code and --disable-error-code (#9172) Add ability to enable error codes globally from the command line Add ability to disable error codes globally from the command line Enabling error codes will always override disabling error codes Update documentation to include new flags Fixes #8975. --- docs/source/command_line.rst | 30 ++++++++++++++++++++ mypy/build.py | 4 ++- mypy/errorcodes.py | 11 ++++++-- mypy/errors.py | 24 +++++++++++++--- mypy/main.py | 26 +++++++++++++++++ mypy/options.py | 13 ++++++++- test-data/unit/check-flags.test | 45 +++++++++++++++++++++++++++++ test-data/unit/cmdline.test | 50 +++++++++++++++++++++++++++++++++ 8 files changed, 195 insertions(+), 8 deletions(-) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index 10760f418026..ed40803510d4 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -553,6 +553,36 @@ of the above sections. Note: the exact list of flags enabled by running :option:`--strict` may change over time. +.. option:: --disable-error-code + + This flag allows disabling one or multiple error codes globally. + + .. code-block:: python + + # no flag + x = 'a string' + x.trim() # error: "str" has no attribute "trim" [attr-defined] + + # --disable-error-code attr-defined + x = 'a string' + x.trim() + +.. option:: --enable-error-code + + This flag allows enabling one or multiple error codes globally. + + Note: This flag will override disabled error codes from the --disable-error-code + flag + + .. code-block:: python + + # --disable-error-code attr-defined + x = 'a string' + x.trim() + + # --disable-error-code attr-defined --enable-error-code attr-defined + x = 'a string' + x.trim() # error: "str" has no attribute "trim" [attr-defined] .. _configuring-error-messages: diff --git a/mypy/build.py b/mypy/build.py index 8d84196af642..c94ca94a3d70 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -223,7 +223,9 @@ def _build(sources: List[BuildSource], options.show_error_codes, options.pretty, lambda path: read_py_file(path, cached_read, options.python_version), - options.show_absolute_path) + options.show_absolute_path, + options.enabled_error_codes, + options.disabled_error_codes) plugin, snapshot = load_plugins(options, errors, stdout, extra_plugins) # Add catch-all .gitignore to cache dir if we created it diff --git a/mypy/errorcodes.py b/mypy/errorcodes.py index 47206c53e9de..0df1989d17dd 100644 --- a/mypy/errorcodes.py +++ b/mypy/errorcodes.py @@ -3,19 +3,26 @@ These can be used for filtering specific errors. """ -from typing import List +from typing import Dict, List from typing_extensions import Final # All created error codes are implicitly stored in this list. all_error_codes = [] # type: List[ErrorCode] +error_codes = {} # type: Dict[str, ErrorCode] + class ErrorCode: - def __init__(self, code: str, description: str, category: str) -> None: + def __init__(self, code: str, + description: str, + category: str, + default_enabled: bool = True) -> None: self.code = code self.description = description self.category = category + self.default_enabled = default_enabled + error_codes[code] = self def __str__(self) -> str: return ''.format(self.code) diff --git a/mypy/errors.py b/mypy/errors.py index b3747658b6f3..465bc5f0cabd 100644 --- a/mypy/errors.py +++ b/mypy/errors.py @@ -164,7 +164,9 @@ def __init__(self, show_error_codes: bool = False, pretty: bool = False, read_source: Optional[Callable[[str], Optional[List[str]]]] = None, - show_absolute_path: bool = False) -> None: + show_absolute_path: bool = False, + enabled_error_codes: Optional[Set[ErrorCode]] = None, + disabled_error_codes: Optional[Set[ErrorCode]] = None) -> None: self.show_error_context = show_error_context self.show_column_numbers = show_column_numbers self.show_error_codes = show_error_codes @@ -172,6 +174,8 @@ def __init__(self, self.pretty = pretty # We use fscache to read source code when showing snippets. self.read_source = read_source + self.enabled_error_codes = enabled_error_codes or set() + self.disabled_error_codes = disabled_error_codes or set() self.initialize() def initialize(self) -> None: @@ -195,7 +199,9 @@ def copy(self) -> 'Errors': self.show_error_codes, self.pretty, self.read_source, - self.show_absolute_path) + self.show_absolute_path, + self.enabled_error_codes, + self.disabled_error_codes) new.file = self.file new.import_ctx = self.import_ctx[:] new.function_or_member = self.function_or_member[:] @@ -351,15 +357,25 @@ def add_error_info(self, info: ErrorInfo) -> None: self._add_error_info(file, info) def is_ignored_error(self, line: int, info: ErrorInfo, ignores: Dict[int, List[str]]) -> bool: - if line not in ignores: + if info.code and self.is_error_code_enabled(info.code) is False: + return True + elif line not in ignores: return False elif not ignores[line]: # Empty list means that we ignore all errors return True - elif info.code: + elif info.code and self.is_error_code_enabled(info.code) is True: return info.code.code in ignores[line] return False + def is_error_code_enabled(self, error_code: ErrorCode) -> bool: + if error_code in self.disabled_error_codes: + return False + elif error_code in self.enabled_error_codes: + return True + else: + return error_code.default_enabled + def clear_errors_in_targets(self, path: str, targets: Set[str]) -> None: """Remove errors in specific fine-grained targets within a file.""" if path in self.error_info_map: diff --git a/mypy/main.py b/mypy/main.py index 8e80364234b4..7038ce40a5c1 100644 --- a/mypy/main.py +++ b/mypy/main.py @@ -18,6 +18,7 @@ from mypy.find_sources import create_source_list, InvalidSourceList from mypy.fscache import FileSystemCache from mypy.errors import CompileError +from mypy.errorcodes import error_codes from mypy.options import Options, BuildType from mypy.config_parser import parse_version, parse_config_file from mypy.split_namespace import SplitNamespace @@ -612,6 +613,14 @@ def add_invertible_flag(flag: str, '--strict', action='store_true', dest='special-opts:strict', help=strict_help) + strictness_group.add_argument( + '--disable-error-code', metavar='NAME', action='append', default=[], + help="Disable a specific error code") + strictness_group.add_argument( + '--enable-error-code', metavar='NAME', action='append', default=[], + help="Enable a specific error code" + ) + error_group = parser.add_argument_group( title='Configuring error messages', description="Adjust the amount of detail shown in error messages.") @@ -860,6 +869,23 @@ def set_strict_flags() -> None: parser.error("You can't make a variable always true and always false (%s)" % ', '.join(sorted(overlap))) + # Process `--enable-error-code` and `--disable-error-code` flags + disabled_codes = set(options.disable_error_code) + enabled_codes = set(options.enable_error_code) + + valid_error_codes = set(error_codes.keys()) + + invalid_codes = (enabled_codes | disabled_codes) - valid_error_codes + if invalid_codes: + parser.error("Invalid error code(s): %s" % + ', '.join(sorted(invalid_codes))) + + options.disabled_error_codes |= {error_codes[code] for code in disabled_codes} + options.enabled_error_codes |= {error_codes[code] for code in enabled_codes} + + # Enabling an error code always overrides disabling + options.disabled_error_codes -= options.enabled_error_codes + # Set build flags. if options.strict_optional_whitelist is not None: # TODO: Deprecate, then kill this flag diff --git a/mypy/options.py b/mypy/options.py index 54e106d2efe7..975c3d8b40f6 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -3,12 +3,15 @@ import pprint import sys -from typing_extensions import Final +from typing_extensions import Final, TYPE_CHECKING from typing import Dict, List, Mapping, Optional, Pattern, Set, Tuple, Callable, Any from mypy import defaults from mypy.util import get_class_descriptors, replace_object_state +if TYPE_CHECKING: + from mypy.errors import ErrorCode + class BuildType: STANDARD = 0 # type: Final[int] @@ -177,6 +180,14 @@ def __init__(self) -> None: # Variable names considered False self.always_false = [] # type: List[str] + # Error codes to disable + self.disable_error_code = [] # type: List[str] + self.disabled_error_codes = set() # type: Set[ErrorCode] + + # Error codes to enable + self.enable_error_code = [] # type: List[str] + self.enabled_error_codes = set() # type: Set[ErrorCode] + # Use script name instead of __main__ self.scripts_are_modules = False diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 0e23476c4d0e..865995376f5d 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -1530,3 +1530,48 @@ def f(a = None): no_implicit_optional = True \[mypy-m] no_implicit_optional = False + +[case testDisableErrorCode] +# flags: --disable-error-code attr-defined +x = 'should be fine' +x.trim() + +[case testDisableDifferentErrorCode] +# flags: --disable-error-code name-defined --show-error-code +x = 'should not be fine' +x.trim() # E: "str" has no attribute "trim" [attr-defined] + +[case testDisableMultipleErrorCode] +# flags: --disable-error-code attr-defined --disable-error-code return-value --show-error-code +x = 'should be fine' +x.trim() + +def bad_return_type() -> str: + return None + +bad_return_type('no args taken!') # E: Too many arguments for "bad_return_type" [call-arg] + +[case testEnableErrorCode] +# flags: --disable-error-code attr-defined --enable-error-code attr-defined --show-error-code +x = 'should be fine' +x.trim() # E: "str" has no attribute "trim" [attr-defined] + +[case testEnableDifferentErrorCode] +# flags: --disable-error-code attr-defined --enable-error-code name-defined --show-error-code +x = 'should not be fine' +x.trim() # E: "str" has no attribute "trim" [attr-defined] + +[case testEnableMultipleErrorCode] +# flags: \ + --disable-error-code attr-defined \ + --disable-error-code return-value \ + --disable-error-code call-arg \ + --enable-error-code attr-defined \ + --enable-error-code return-value --show-error-code +x = 'should be fine' +x.trim() # E: "str" has no attribute "trim" [attr-defined] + +def bad_return_type() -> str: + return None # E: Incompatible return value type (got "None", expected "str") [return-value] + +bad_return_type('no args taken!') diff --git a/test-data/unit/cmdline.test b/test-data/unit/cmdline.test index c388725eec84..70960b011a25 100644 --- a/test-data/unit/cmdline.test +++ b/test-data/unit/cmdline.test @@ -1123,3 +1123,53 @@ import foo.bar [out] src/foo/bar.py: error: Source file found twice under different module names: 'src.foo.bar' and 'foo.bar' == Return code: 2 + +[case testEnableInvalidErrorCode] +# cmd: mypy --enable-error-code YOLO test.py +[file test.py] +x = 1 +[out] +usage: mypy [-h] [-v] [-V] [more options; see below] + [-m MODULE] [-p PACKAGE] [-c PROGRAM_TEXT] [files ...] +mypy: error: Invalid error code(s): YOLO +== Return code: 2 + +[case testDisableInvalidErrorCode] +# cmd: mypy --disable-error-code YOLO test.py +[file test.py] +x = 1 +[out] +usage: mypy [-h] [-v] [-V] [more options; see below] + [-m MODULE] [-p PACKAGE] [-c PROGRAM_TEXT] [files ...] +mypy: error: Invalid error code(s): YOLO +== Return code: 2 + +[case testEnableAndDisableInvalidErrorCode] +# cmd: mypy --disable-error-code YOLO --enable-error-code YOLO2 test.py +[file test.py] +x = 1 +[out] +usage: mypy [-h] [-v] [-V] [more options; see below] + [-m MODULE] [-p PACKAGE] [-c PROGRAM_TEXT] [files ...] +mypy: error: Invalid error code(s): YOLO, YOLO2 +== Return code: 2 + +[case testEnableValidAndInvalidErrorCode] +# cmd: mypy --enable-error-code attr-defined --enable-error-code YOLO test.py +[file test.py] +x = 1 +[out] +usage: mypy [-h] [-v] [-V] [more options; see below] + [-m MODULE] [-p PACKAGE] [-c PROGRAM_TEXT] [files ...] +mypy: error: Invalid error code(s): YOLO +== Return code: 2 + +[case testDisableValidAndInvalidErrorCode] +# cmd: mypy --disable-error-code attr-defined --disable-error-code YOLO test.py +[file test.py] +x = 1 +[out] +usage: mypy [-h] [-v] [-V] [more options; see below] + [-m MODULE] [-p PACKAGE] [-c PROGRAM_TEXT] [files ...] +mypy: error: Invalid error code(s): YOLO +== Return code: 2 From 6e41508d3953d8ecb97218a263a1cc625400f477 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 10 Aug 2020 16:24:17 +0100 Subject: [PATCH 113/138] Sync typeshed (#9285) --- mypy/typeshed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/typeshed b/mypy/typeshed index 199b262f6315..866b0c3bf0cb 160000 --- a/mypy/typeshed +++ b/mypy/typeshed @@ -1 +1 @@ -Subproject commit 199b262f6315f7c83b4fb0e7f7676d7ad506cb7d +Subproject commit 866b0c3bf0cbd791d4c2072526568abd74463e84 From ab5b0ea1fcc099fce3241d8393f0dab24fa67ecf Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 11 Aug 2020 14:04:34 +0100 Subject: [PATCH 114/138] Sync typeshed (#9289) --- mypy/typeshed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/typeshed b/mypy/typeshed index 866b0c3bf0cb..276d0428b90c 160000 --- a/mypy/typeshed +++ b/mypy/typeshed @@ -1 +1 @@ -Subproject commit 866b0c3bf0cbd791d4c2072526568abd74463e84 +Subproject commit 276d0428b90c7454d008dd6d794dad727a609492 From d94853d74077ff5ce50da311d792da546f4e6c38 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Tue, 11 Aug 2020 21:06:44 +0800 Subject: [PATCH 115/138] [mypyc] Implement LoadAddress (#9287) This PR introduces LoadAddress, an op for taking the address of a given name. Currently, in mypyc we only need to take the address of a name when we are using the name_ref_ops, so the op takes a string (CPython name) as its argument. We can revisit the design later if we want to take the address of arbitrary name later. --- mypyc/analysis/dataflow.py | 5 ++++- mypyc/codegen/emitfunc.py | 7 ++++++- mypyc/ir/ops.py | 23 +++++++++++++++++++++++ mypyc/irbuild/expression.py | 7 +++++-- mypyc/primitives/dict_ops.py | 13 +++++-------- mypyc/primitives/registry.py | 10 ++++++++++ mypyc/test-data/irbuild-dict.test | 12 ++++++++++++ mypyc/test/test_emitfunc.py | 6 +++++- 8 files changed, 70 insertions(+), 13 deletions(-) diff --git a/mypyc/analysis/dataflow.py b/mypyc/analysis/dataflow.py index 90a2e4d20a31..182b34e636c8 100644 --- a/mypyc/analysis/dataflow.py +++ b/mypyc/analysis/dataflow.py @@ -9,7 +9,7 @@ BasicBlock, OpVisitor, Assign, LoadInt, LoadErrorValue, RegisterOp, Goto, Branch, Return, Call, Environment, Box, Unbox, Cast, Op, Unreachable, TupleGet, TupleSet, GetAttr, SetAttr, LoadStatic, InitStatic, PrimitiveOp, MethodCall, RaiseStandardError, CallC, LoadGlobal, - Truncate, BinaryIntOp, LoadMem, GetElementPtr + Truncate, BinaryIntOp, LoadMem, GetElementPtr, LoadAddress ) @@ -214,6 +214,9 @@ def visit_load_mem(self, op: LoadMem) -> GenAndKill: def visit_get_element_ptr(self, op: GetElementPtr) -> GenAndKill: return self.visit_register_op(op) + def visit_load_address(self, op: LoadAddress) -> GenAndKill: + return self.visit_register_op(op) + class DefinedVisitor(BaseAnalysisVisitor): """Visitor for finding defined registers. diff --git a/mypyc/codegen/emitfunc.py b/mypyc/codegen/emitfunc.py index 537f47cf6acd..d5b6e2e13619 100644 --- a/mypyc/codegen/emitfunc.py +++ b/mypyc/codegen/emitfunc.py @@ -12,7 +12,7 @@ LoadStatic, InitStatic, TupleGet, TupleSet, Call, IncRef, DecRef, Box, Cast, Unbox, BasicBlock, Value, MethodCall, PrimitiveOp, EmitterInterface, Unreachable, NAMESPACE_STATIC, NAMESPACE_TYPE, NAMESPACE_MODULE, RaiseStandardError, CallC, LoadGlobal, Truncate, - BinaryIntOp, LoadMem, GetElementPtr + BinaryIntOp, LoadMem, GetElementPtr, LoadAddress ) from mypyc.ir.rtypes import ( RType, RTuple, is_tagged, is_int32_rprimitive, is_int64_rprimitive, RStruct @@ -480,6 +480,11 @@ def visit_get_element_ptr(self, op: GetElementPtr) -> None: self.emit_line('%s = (%s)&((%s *)%s)->%s;' % (dest, op.type._ctype, op.src_type.name, src, op.field)) + def visit_load_address(self, op: LoadAddress) -> None: + typ = op.type + dest = self.reg(op) + self.emit_line('%s = (%s)&%s;' % (dest, typ._ctype, op.src)) + # Helpers def label(self, label: BasicBlock) -> str: diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index e90ccafd29ac..aecf224f8f9c 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -1394,6 +1394,25 @@ def accept(self, visitor: 'OpVisitor[T]') -> T: return visitor.visit_get_element_ptr(self) +class LoadAddress(RegisterOp): + error_kind = ERR_NEVER + is_borrowed = True + + def __init__(self, type: RType, src: str, line: int = -1) -> None: + super().__init__(line) + self.type = type + self.src = src + + def sources(self) -> List[Value]: + return [] + + def to_str(self, env: Environment) -> str: + return env.format("%r = load_address %s", self, self.src) + + def accept(self, visitor: 'OpVisitor[T]') -> T: + return visitor.visit_load_address(self) + + @trait class OpVisitor(Generic[T]): """Generic visitor over ops (uses the visitor design pattern).""" @@ -1508,6 +1527,10 @@ def visit_load_mem(self, op: LoadMem) -> T: def visit_get_element_ptr(self, op: GetElementPtr) -> T: raise NotImplementedError + @abstractmethod + def visit_load_address(self, op: LoadAddress) -> T: + raise NotImplementedError + # TODO: Should this live somewhere else? LiteralsMap = Dict[Tuple[Type[object], Union[int, float, str, bytes, complex]], str] diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index f759ffeb329d..f709be3f32b2 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -16,11 +16,11 @@ from mypy.types import TupleType, get_proper_type from mypyc.ir.ops import ( - Value, TupleGet, TupleSet, PrimitiveOp, BasicBlock, OpDescription, Assign + Value, TupleGet, TupleSet, PrimitiveOp, BasicBlock, OpDescription, Assign, LoadAddress ) from mypyc.ir.rtypes import RTuple, object_rprimitive, is_none_rprimitive, is_int_rprimitive from mypyc.ir.func_ir import FUNC_CLASSMETHOD, FUNC_STATICMETHOD -from mypyc.primitives.registry import name_ref_ops, CFunctionDescription +from mypyc.primitives.registry import name_ref_ops, CFunctionDescription, builtin_names from mypyc.primitives.generic_ops import iter_op from mypyc.primitives.misc_ops import new_slice_op, ellipsis_op, type_op from mypyc.primitives.list_ops import new_list_op, list_append_op, list_extend_op @@ -39,6 +39,9 @@ def transform_name_expr(builder: IRBuilder, expr: NameExpr) -> Value: assert expr.node, "RefExpr not resolved" fullname = expr.node.fullname + if fullname in builtin_names: + typ, src = builtin_names[fullname] + return builder.add(LoadAddress(typ, src, expr.line)) if fullname in name_ref_ops: # Use special access op for this particular name. desc = name_ref_ops[fullname] diff --git a/mypyc/primitives/dict_ops.py b/mypyc/primitives/dict_ops.py index 04cea8a7e0f0..031aee813e41 100644 --- a/mypyc/primitives/dict_ops.py +++ b/mypyc/primitives/dict_ops.py @@ -8,17 +8,14 @@ ) from mypyc.primitives.registry import ( - name_ref_op, method_op, - simple_emit, name_emit, c_custom_op, c_method_op, c_function_op, c_binary_op + method_op, simple_emit, c_custom_op, c_method_op, c_function_op, c_binary_op, load_address_op ) - # Get the 'dict' type object. -name_ref_op('builtins.dict', - result_type=object_rprimitive, - error_kind=ERR_NEVER, - emit=name_emit('&PyDict_Type', target_type="PyObject *"), - is_borrowed=True) +load_address_op( + name='builtins.dict', + type=object_rprimitive, + src='PyDict_Type') # dict[key] dict_get_item_op = c_method_op( diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index 16890889532a..57f8c038d278 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -93,6 +93,8 @@ # LoadGlobal/LoadAddress op for reading global names c_name_ref_ops = {} # type: Dict[str, CLoadDescription] +builtin_names = {} # type: Dict[str, Tuple[RType, str]] + def simple_emit(template: str) -> EmitCallback: """Construct a simple PrimitiveOp emit callback function. @@ -498,6 +500,14 @@ def c_name_ref_op(name: str, c_name_ref_ops[name] = desc return desc + +def load_address_op(name: str, + type: RType, + src: str) -> None: + assert name not in builtin_names, 'already defined: %s' % name + builtin_names[name] = (type, src) + + # Import various modules that set up global state. import mypyc.primitives.int_ops # noqa import mypyc.primitives.str_ops # noqa diff --git a/mypyc/test-data/irbuild-dict.test b/mypyc/test-data/irbuild-dict.test index 8296219a83d3..dc32cb1f011e 100644 --- a/mypyc/test-data/irbuild-dict.test +++ b/mypyc/test-data/irbuild-dict.test @@ -306,3 +306,15 @@ L12: r34 = None return r34 +[case testDictLoadAddress] +def f() -> None: + x = dict +[out] +def f(): + r0, x :: object + r1 :: None +L0: + r0 = load_address PyDict_Type + x = r0 + r1 = None + return r1 diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index a1954eda7348..aa538939146f 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -10,7 +10,7 @@ from mypyc.ir.ops import ( Environment, BasicBlock, Goto, Return, LoadInt, Assign, IncRef, DecRef, Branch, Call, Unbox, Box, TupleGet, GetAttr, PrimitiveOp, RegisterOp, - SetAttr, Op, Value, CallC, BinaryIntOp, LoadMem, GetElementPtr + SetAttr, Op, Value, CallC, BinaryIntOp, LoadMem, GetElementPtr, LoadAddress ) from mypyc.ir.rtypes import ( RTuple, RInstance, int_rprimitive, bool_rprimitive, list_rprimitive, @@ -281,6 +281,10 @@ def test_get_element_ptr(self) -> None: self.assert_emit(GetElementPtr(self.o, r, "i64"), """cpy_r_r01 = (CPyPtr)&((Foo *)cpy_r_o)->i64;""") + def test_load_address(self) -> None: + self.assert_emit(LoadAddress(object_rprimitive, "PyDict_Type"), + """cpy_r_r0 = (PyObject *)&PyDict_Type;""") + def assert_emit(self, op: Op, expected: str) -> None: self.emitter.fragments = [] self.declarations.fragments = [] From b34f4c6b9c2aca899c81c125b8a38b9f2c94c121 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Wed, 12 Aug 2020 18:03:45 +0800 Subject: [PATCH 116/138] [mypyc] Merge most name_ref_op with LoadAddress (#9293) This PR replaces name_ref_op: int, str, list, type, NotImplemented with LoadAddress. The only three remaining name_ref_ops are true, false and none, which should be built directly in irbuild --- mypyc/irbuild/classdef.py | 12 +++++++----- mypyc/primitives/int_ops.py | 14 ++++++-------- mypyc/primitives/list_ops.py | 12 +++++------- mypyc/primitives/misc_ops.py | 21 +++++++++----------- mypyc/primitives/registry.py | 28 ++++++--------------------- mypyc/primitives/str_ops.py | 15 +++++++------- mypyc/test-data/irbuild-basic.test | 16 +++++++-------- mypyc/test-data/irbuild-classes.test | 4 ++-- mypyc/test-data/irbuild-optional.test | 2 +- mypyc/test-data/refcount.test | 2 +- 10 files changed, 52 insertions(+), 74 deletions(-) diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index 68d5ff3ee1bd..19033b19bf14 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -8,7 +8,7 @@ ) from mypyc.ir.ops import ( Value, Call, LoadErrorValue, LoadStatic, InitStatic, TupleSet, SetAttr, Return, - BasicBlock, Branch, MethodCall, NAMESPACE_TYPE + BasicBlock, Branch, MethodCall, NAMESPACE_TYPE, LoadAddress ) from mypyc.ir.rtypes import ( RInstance, object_rprimitive, bool_rprimitive, dict_rprimitive, is_optional_type, @@ -227,7 +227,8 @@ def find_non_ext_metaclass(builder: IRBuilder, cdef: ClassDef, bases: Value) -> if cdef.metaclass: declared_metaclass = builder.accept(cdef.metaclass) else: - declared_metaclass = builder.primitive_op(type_object_op, [], cdef.line) + declared_metaclass = builder.add(LoadAddress(type_object_op.type, + type_object_op.src, cdef.line)) return builder.primitive_op(py_calc_meta_op, [declared_metaclass, bases], cdef.line) @@ -279,7 +280,7 @@ def add_non_ext_class_attr(builder: IRBuilder, # which attributes to compute on. # TODO: Maybe generate more precise types for annotations key = builder.load_static_unicode(lvalue.name) - typ = builder.primitive_op(type_object_op, [], stmt.line) + typ = builder.add(LoadAddress(type_object_op.type, type_object_op.src, stmt.line)) builder.call_c(dict_set_item_op, [non_ext.anns, key, typ], stmt.line) # Only add the attribute to the __dict__ if the assignment is of the form: @@ -393,7 +394,8 @@ def gen_glue_ne_method(builder: IRBuilder, cls: ClassIR, line: int) -> FuncIR: # If __eq__ returns NotImplemented, then __ne__ should also not_implemented_block, regular_block = BasicBlock(), BasicBlock() eqval = builder.add(MethodCall(args[0], '__eq__', [args[1]], line)) - not_implemented = builder.primitive_op(not_implemented_op, [], line) + not_implemented = builder.add(LoadAddress(not_implemented_op.type, + not_implemented_op.src, line)) builder.add(Branch( builder.binary_op(eqval, not_implemented, 'is', line), not_implemented_block, @@ -521,7 +523,7 @@ def dataclass_non_ext_info(builder: IRBuilder, cdef: ClassDef) -> Optional[NonEx builder.call_c(dict_new_op, [], cdef.line), builder.add(TupleSet([], cdef.line)), builder.call_c(dict_new_op, [], cdef.line), - builder.primitive_op(type_object_op, [], cdef.line), + builder.add(LoadAddress(type_object_op.type, type_object_op.src, cdef.line)) ) else: return None diff --git a/mypyc/primitives/int_ops.py b/mypyc/primitives/int_ops.py index e0416a0d256b..ee9a76cb0c67 100644 --- a/mypyc/primitives/int_ops.py +++ b/mypyc/primitives/int_ops.py @@ -13,20 +13,18 @@ str_rprimitive, RType ) from mypyc.primitives.registry import ( - name_ref_op, name_emit, - c_unary_op, CFunctionDescription, c_function_op, c_binary_op, c_custom_op + load_address_op, c_unary_op, CFunctionDescription, c_function_op, c_binary_op, c_custom_op ) # These int constructors produce object_rprimitives that then need to be unboxed # I guess unboxing ourselves would save a check and branch though? # Get the type object for 'builtins.int'. -# For ordinary calls to int() we use a name_ref to the type -name_ref_op('builtins.int', - result_type=object_rprimitive, - error_kind=ERR_NEVER, - emit=name_emit('&PyLong_Type', target_type='PyObject *'), - is_borrowed=True) +# For ordinary calls to int() we use a load_address to the type +load_address_op( + name='builtins.int', + type=object_rprimitive, + src='PyLong_Type') # Convert from a float to int. We could do a bit better directly. c_function_op( diff --git a/mypyc/primitives/list_ops.py b/mypyc/primitives/list_ops.py index 7768f2bd0af4..ca4110be2bf7 100644 --- a/mypyc/primitives/list_ops.py +++ b/mypyc/primitives/list_ops.py @@ -8,17 +8,15 @@ c_int_rprimitive ) from mypyc.primitives.registry import ( - name_ref_op, custom_op, name_emit, - call_emit, c_function_op, c_binary_op, c_method_op + custom_op, load_address_op, call_emit, c_function_op, c_binary_op, c_method_op ) # Get the 'builtins.list' type object. -name_ref_op('builtins.list', - result_type=object_rprimitive, - error_kind=ERR_NEVER, - emit=name_emit('&PyList_Type', target_type='PyObject *'), - is_borrowed=True) +load_address_op( + name='builtins.list', + type=object_rprimitive, + src='PyList_Type') # list(obj) to_list = c_function_op( diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py index bab25ef58a07..9ff548acee9e 100644 --- a/mypyc/primitives/misc_ops.py +++ b/mypyc/primitives/misc_ops.py @@ -7,7 +7,7 @@ ) from mypyc.primitives.registry import ( name_ref_op, simple_emit, unary_op, func_op, custom_op, call_emit, name_emit, - call_negative_magic_emit, c_function_op, c_custom_op + call_negative_magic_emit, c_function_op, c_custom_op, load_address_op ) @@ -46,11 +46,10 @@ is_borrowed=True) # Get the boxed NotImplemented object -not_implemented_op = name_ref_op(name='builtins.NotImplemented', - result_type=object_rprimitive, - error_kind=ERR_NEVER, - emit=name_emit('Py_NotImplemented'), - is_borrowed=True) +not_implemented_op = load_address_op( + name='builtins.NotImplemented', + type=object_rprimitive, + src='_Py_NotImplementedStruct') # id(obj) c_function_op( @@ -201,12 +200,10 @@ error_kind=ERR_NEVER) # Get 'builtins.type' (base class of all classes) -type_object_op = name_ref_op( - 'builtins.type', - result_type=object_rprimitive, - error_kind=ERR_NEVER, - emit=name_emit('&PyType_Type', target_type='PyObject *'), - is_borrowed=True) +type_object_op = load_address_op( + name='builtins.type', + type=object_rprimitive, + src='PyType_Type') # Create a heap type based on a template non-heap type. # See CPyType_FromTemplate for more docs. diff --git a/mypyc/primitives/registry.py b/mypyc/primitives/registry.py index 57f8c038d278..1053d2721494 100644 --- a/mypyc/primitives/registry.py +++ b/mypyc/primitives/registry.py @@ -56,12 +56,10 @@ ('priority', int)]) # A description for C load operations including LoadGlobal and LoadAddress -CLoadDescription = NamedTuple( - 'CLoadDescription', [('name', str), - ('return_type', RType), - ('identifier', str), # name of the target to load - ('cast_str', str), # string represents optional type cast - ('load_address', bool)]) # True for LoadAddress otherwise LoadGlobal +LoadAddressDescription = NamedTuple( + 'LoadAddressDescription', [('name', str), + ('type', RType), + ('src', str)]) # name of the target to load # Primitive binary ops (key is operator such as '+') binary_ops = {} # type: Dict[str, List[OpDescription]] @@ -90,9 +88,6 @@ # CallC op for unary ops c_unary_ops = {} # type: Dict[str, List[CFunctionDescription]] -# LoadGlobal/LoadAddress op for reading global names -c_name_ref_ops = {} # type: Dict[str, CLoadDescription] - builtin_names = {} # type: Dict[str, Tuple[RType, str]] @@ -489,23 +484,12 @@ def c_unary_op(name: str, return desc -def c_name_ref_op(name: str, - return_type: RType, - identifier: str, - cast_str: Optional[str] = None, - load_address: bool = False) -> CLoadDescription: - assert name not in c_name_ref_ops, 'already defined: %s' % name - cast_str = cast_str if cast_str else "" - desc = CLoadDescription(name, return_type, identifier, cast_str, load_address) - c_name_ref_ops[name] = desc - return desc - - def load_address_op(name: str, type: RType, - src: str) -> None: + src: str) -> LoadAddressDescription: assert name not in builtin_names, 'already defined: %s' % name builtin_names[name] = (type, src) + return LoadAddressDescription(name, type, src) # Import various modules that set up global state. diff --git a/mypyc/primitives/str_ops.py b/mypyc/primitives/str_ops.py index 69a972bf0715..778b2f3bb56f 100644 --- a/mypyc/primitives/str_ops.py +++ b/mypyc/primitives/str_ops.py @@ -2,22 +2,21 @@ from typing import List, Callable -from mypyc.ir.ops import ERR_MAGIC, ERR_NEVER, EmitterInterface, EmitCallback +from mypyc.ir.ops import ERR_MAGIC, EmitterInterface, EmitCallback from mypyc.ir.rtypes import ( RType, object_rprimitive, str_rprimitive, bool_rprimitive, int_rprimitive, list_rprimitive ) from mypyc.primitives.registry import ( - binary_op, simple_emit, name_ref_op, method_op, call_emit, name_emit, - c_method_op, c_binary_op, c_function_op + binary_op, simple_emit, method_op, call_emit, c_method_op, c_binary_op, c_function_op, + load_address_op ) # Get the 'str' type object. -name_ref_op('builtins.str', - result_type=object_rprimitive, - error_kind=ERR_NEVER, - emit=name_emit('&PyUnicode_Type', target_type='PyObject *'), - is_borrowed=True) +load_address_op( + name='builtins.str', + type=object_rprimitive, + src='PyUnicode_Type') # str(obj) c_function_op( diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 951cf06a9809..410e667d2b2c 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -1243,7 +1243,7 @@ def call_python_function(x): r0, r1, r2 :: object r3 :: int L0: - r0 = int + r0 = load_address PyLong_Type r1 = box(int, x) r2 = py_call(r0, r1) r3 = unbox(int, r2) @@ -1294,7 +1294,7 @@ def call_python_function_with_keyword_arg(x): r5 :: object r6 :: int L0: - r0 = int + r0 = load_address PyLong_Type r1 = unicode_3 :: static ('base') r2 = (x) :: tuple r3 = box(short_int, 4) @@ -1696,7 +1696,7 @@ def foo(x): r3 :: __main__.B r4 :: __main__.A L0: - r0 = int + r0 = load_address PyLong_Type r1 = PyObject_IsInstance(x, r0) r2 = truncate r1: int32 to builtins.bool if r2 goto L1 else goto L2 :: bool @@ -2688,11 +2688,11 @@ L4: r23 = CPyDict_SetItem(r11, r22, r21) r24 = unicode_5 :: static ('Lol') r25 = unicode_6 :: static ('a') - r26 = int + r26 = load_address PyLong_Type r27 = (r25, r26) r28 = box(tuple[str, object], r27) r29 = unicode_7 :: static ('b') - r30 = str + r30 = load_address PyUnicode_Type r31 = (r29, r30) r32 = box(tuple[str, object], r31) r33 = (r28, r32) @@ -2717,7 +2717,7 @@ L4: r52 = __main__.globals :: static r53 = unicode_2 :: static ('List') r54 = CPyDict_GetItem(r52, r53) - r55 = int + r55 = load_address PyLong_Type r56 = PyObject_GetItem(r54, r55) r57 = __main__.globals :: static r58 = unicode_10 :: static ('Foo') @@ -2820,7 +2820,7 @@ def A.__eq__(self, x): self :: __main__.A x, r0 :: object L0: - r0 = NotImplemented + r0 = load_address _Py_NotImplementedStruct return r0 def A.__ne__(self, rhs): self :: __main__.A @@ -2829,7 +2829,7 @@ def A.__ne__(self, rhs): r4 :: object L0: r0 = self.__eq__(rhs) - r1 = NotImplemented + r1 = load_address _Py_NotImplementedStruct r2 = r0 is r1 if r2 goto L2 else goto L1 :: bool L1: diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index d92291e29c95..3ad447c458d6 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -832,7 +832,7 @@ def Base.__ne__(self, rhs): r4 :: object L0: r0 = self.__eq__(rhs) - r1 = NotImplemented + r1 = load_address _Py_NotImplementedStruct r2 = r0 is r1 if r2 goto L2 else goto L1 :: bool L1: @@ -953,7 +953,7 @@ def Derived.__ne__(self, rhs): r4 :: object L0: r0 = self.__eq__(rhs) - r1 = NotImplemented + r1 = load_address _Py_NotImplementedStruct r2 = r0 is r1 if r2 goto L2 else goto L1 :: bool L1: diff --git a/mypyc/test-data/irbuild-optional.test b/mypyc/test-data/irbuild-optional.test index 3f25bf103c80..320186fde8d8 100644 --- a/mypyc/test-data/irbuild-optional.test +++ b/mypyc/test-data/irbuild-optional.test @@ -295,7 +295,7 @@ def f(x): r5 :: __main__.A r6 :: int L0: - r0 = int + r0 = load_address PyLong_Type r1 = PyObject_IsInstance(x, r0) r2 = truncate r1: int32 to builtins.bool if r2 goto L1 else goto L2 :: bool diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index 39db7d05f017..20025102e4c8 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -734,7 +734,7 @@ def g(x): r5 :: object r6 :: int L0: - r0 = int + r0 = load_address PyLong_Type r1 = unicode_1 :: static ('base') r2 = (x) :: tuple r3 = box(short_int, 4) From 0dfac58ede61f7c4a92f55ffe5f527fa6781fab2 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Wed, 12 Aug 2020 21:48:12 +0800 Subject: [PATCH 117/138] [mypyc] Build True, False and None op in irbuild (#9294) This PR builds three specialized op directly in irbuild, instead of using name_ref_ops. --- mypyc/irbuild/builder.py | 15 +- mypyc/irbuild/classdef.py | 4 +- mypyc/irbuild/expression.py | 7 + mypyc/irbuild/ll_builder.py | 19 +- mypyc/irbuild/specialize.py | 17 +- mypyc/irbuild/statement.py | 6 +- mypyc/primitives/misc_ops.py | 21 +- mypyc/test-data/analysis.test | 24 +- mypyc/test-data/exceptions.test | 88 +++---- mypyc/test-data/irbuild-any.test | 21 +- mypyc/test-data/irbuild-basic.test | 328 +++++++++--------------- mypyc/test-data/irbuild-classes.test | 133 ++++------ mypyc/test-data/irbuild-dict.test | 131 ++++------ mypyc/test-data/irbuild-generics.test | 21 +- mypyc/test-data/irbuild-lists.test | 21 +- mypyc/test-data/irbuild-nested.test | 8 +- mypyc/test-data/irbuild-optional.test | 195 ++++++-------- mypyc/test-data/irbuild-set.test | 13 +- mypyc/test-data/irbuild-statements.test | 124 +++------ mypyc/test-data/irbuild-try.test | 107 ++++---- mypyc/test-data/irbuild-tuple.test | 18 +- mypyc/test-data/refcount.test | 61 ++--- mypyc/test/test_emitfunc.py | 8 +- 23 files changed, 516 insertions(+), 874 deletions(-) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index e770a31ba85b..2b9c994ae47c 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -47,7 +47,7 @@ from mypyc.primitives.list_ops import to_list, list_pop_last from mypyc.primitives.dict_ops import dict_get_item_op, dict_set_item_op from mypyc.primitives.generic_ops import py_setattr_op, iter_op, next_op -from mypyc.primitives.misc_ops import true_op, false_op, import_op +from mypyc.primitives.misc_ops import import_op from mypyc.crash import catch_errors from mypyc.options import CompilerOptions from mypyc.errors import Errors @@ -200,6 +200,15 @@ def coerce(self, src: Value, target_type: RType, line: int, force: bool = False) def none_object(self) -> Value: return self.builder.none_object() + def none(self) -> Value: + return self.builder.none() + + def true(self) -> Value: + return self.builder.true() + + def false(self) -> Value: + return self.builder.false() + def py_call(self, function: Value, arg_values: List[Value], @@ -339,9 +348,9 @@ def load_final_literal_value(self, val: Union[int, str, bytes, float, bool], """Load value of a final name or class-level attribute.""" if isinstance(val, bool): if val: - return self.primitive_op(true_op, [], line) + return self.true() else: - return self.primitive_op(false_op, [], line) + return self.false() elif isinstance(val, int): # TODO: take care of negative integer initializers # (probably easier to fix this in mypy itself). diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index 19033b19bf14..cf88d9896db1 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -19,7 +19,7 @@ from mypyc.primitives.generic_ops import py_setattr_op, py_hasattr_op from mypyc.primitives.misc_ops import ( dataclass_sleight_of_hand, pytype_from_template_op, py_calc_meta_op, type_object_op, - not_implemented_op, true_op + not_implemented_op ) from mypyc.primitives.dict_ops import dict_set_item_op, dict_new_op from mypyc.primitives.tuple_ops import new_tuple_op @@ -350,7 +350,7 @@ def generate_attr_defaults(builder: IRBuilder, cdef: ClassDef) -> None: val = builder.coerce(builder.accept(stmt.rvalue), attr_type, stmt.line) builder.add(SetAttr(self_var, lvalue.name, val, -1)) - builder.add(Return(builder.primitive_op(true_op, [], -1))) + builder.add(Return(builder.true())) blocks, env, ret_type, _ = builder.leave() ir = FuncIR( diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index f709be3f32b2..7fa379db52f2 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -42,6 +42,13 @@ def transform_name_expr(builder: IRBuilder, expr: NameExpr) -> Value: if fullname in builtin_names: typ, src = builtin_names[fullname] return builder.add(LoadAddress(typ, src, expr.line)) + # special cases + if fullname == 'builtins.None': + return builder.none() + if fullname == 'builtins.True': + return builder.true() + if fullname == 'builtins.False': + return builder.false() if fullname in name_ref_ops: # Use special access op for this particular name. desc = name_ref_ops[fullname] diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 43766f988f5f..0e07092fdb63 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -28,7 +28,8 @@ RType, RUnion, RInstance, optional_value_type, int_rprimitive, float_rprimitive, bool_rprimitive, list_rprimitive, str_rprimitive, is_none_rprimitive, object_rprimitive, c_pyssize_t_rprimitive, is_short_int_rprimitive, is_tagged, PyVarObject, short_int_rprimitive, - is_list_rprimitive, is_tuple_rprimitive, is_dict_rprimitive, is_set_rprimitive, PySetObject + is_list_rprimitive, is_tuple_rprimitive, is_dict_rprimitive, is_set_rprimitive, PySetObject, + none_rprimitive ) from mypyc.ir.func_ir import FuncDecl, FuncSignature from mypyc.ir.class_ir import ClassIR, all_concrete_classes @@ -52,7 +53,7 @@ py_getattr_op, py_call_op, py_call_with_kwargs_op, py_method_call_op, generic_len_op ) from mypyc.primitives.misc_ops import ( - none_op, none_object_op, false_op, fast_isinstance_op, bool_op, type_is_op + none_object_op, fast_isinstance_op, bool_op, type_is_op ) from mypyc.primitives.int_ops import int_logical_op_mapping from mypyc.rt_subtype import is_runtime_subtype @@ -195,7 +196,7 @@ def py_get_attr(self, obj: Value, attr: str, line: int) -> Value: def isinstance_helper(self, obj: Value, class_irs: List[ClassIR], line: int) -> Value: """Fast path for isinstance() that checks against a list of native classes.""" if not class_irs: - return self.primitive_op(false_op, [], line) + return self.false() ret = self.isinstance_native(obj, class_irs[0], line) for class_ir in class_irs[1:]: def other() -> Value: @@ -217,7 +218,7 @@ def isinstance_native(self, obj: Value, class_ir: ClassIR, line: int) -> Value: line) if not concrete: # There can't be any concrete instance that matches this. - return self.primitive_op(false_op, [], line) + return self.false() type_obj = self.get_native_type(concrete[0]) ret = self.primitive_op(type_is_op, [obj, type_obj], line) for c in concrete[1:]: @@ -424,7 +425,15 @@ def call_union_item(value: Value) -> Value: def none(self) -> Value: """Load unboxed None value (type: none_rprimitive).""" - return self.add(PrimitiveOp([], none_op, line=-1)) + return self.add(LoadInt(1, -1, none_rprimitive)) + + def true(self) -> Value: + """Load unboxed True value (type: bool_rprimitive).""" + return self.add(LoadInt(1, -1, bool_rprimitive)) + + def false(self) -> Value: + """Load unboxed False value (type: bool_rprimitive).""" + return self.add(LoadInt(0, -1, bool_rprimitive)) def none_object(self) -> Value: """Load Python None value (type: object_rprimitive).""" diff --git a/mypyc/irbuild/specialize.py b/mypyc/irbuild/specialize.py index f99798db1aea..f1fd0782748e 100644 --- a/mypyc/irbuild/specialize.py +++ b/mypyc/irbuild/specialize.py @@ -18,14 +18,13 @@ from mypy.types import AnyType, TypeOfAny from mypyc.ir.ops import ( - Value, BasicBlock, LoadInt, RaiseStandardError, Unreachable, OpDescription, + Value, BasicBlock, LoadInt, RaiseStandardError, Unreachable ) from mypyc.ir.rtypes import ( RType, RTuple, str_rprimitive, list_rprimitive, dict_rprimitive, set_rprimitive, bool_rprimitive, is_dict_rprimitive ) from mypyc.primitives.dict_ops import dict_keys_op, dict_values_op, dict_items_op -from mypyc.primitives.misc_ops import true_op, false_op from mypyc.irbuild.builder import IRBuilder from mypyc.irbuild.for_helpers import translate_list_comprehension, comprehension_helper @@ -147,7 +146,7 @@ def translate_any_call(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> O if (len(expr.args) == 1 and expr.arg_kinds == [ARG_POS] and isinstance(expr.args[0], GeneratorExpr)): - return any_all_helper(builder, expr.args[0], false_op, lambda x: x, true_op) + return any_all_helper(builder, expr.args[0], builder.false, lambda x: x, builder.true) return None @@ -158,20 +157,20 @@ def translate_all_call(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> O and isinstance(expr.args[0], GeneratorExpr)): return any_all_helper( builder, expr.args[0], - true_op, + builder.true, lambda x: builder.unary_op(x, 'not', expr.line), - false_op + builder.false ) return None def any_all_helper(builder: IRBuilder, gen: GeneratorExpr, - initial_value_op: OpDescription, + initial_value: Callable[[], Value], modify: Callable[[Value], Value], - new_value_op: OpDescription) -> Value: + new_value: Callable[[], Value]) -> Value: retval = builder.alloc_temp(bool_rprimitive) - builder.assign(retval, builder.primitive_op(initial_value_op, [], -1), -1) + builder.assign(retval, initial_value(), -1) loop_params = list(zip(gen.indices, gen.sequences, gen.condlists)) true_block, false_block, exit_block = BasicBlock(), BasicBlock(), BasicBlock() @@ -179,7 +178,7 @@ def gen_inner_stmts() -> None: comparison = modify(builder.accept(gen.left_expr)) builder.add_bool_branch(comparison, true_block, false_block) builder.activate_block(true_block) - builder.assign(retval, builder.primitive_op(new_value_op, [], -1), -1) + builder.assign(retval, new_value(), -1) builder.goto(exit_block) builder.activate_block(false_block) diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py index 0898a0b19d4d..260c5af1cadf 100644 --- a/mypyc/irbuild/statement.py +++ b/mypyc/irbuild/statement.py @@ -22,7 +22,7 @@ ) from mypyc.ir.rtypes import exc_rtuple from mypyc.primitives.generic_ops import py_delattr_op -from mypyc.primitives.misc_ops import true_op, false_op, type_op, get_module_dict_op +from mypyc.primitives.misc_ops import type_op, get_module_dict_op from mypyc.primitives.dict_ops import dict_get_item_op from mypyc.primitives.exc_ops import ( raise_exception_op, reraise_exception_op, error_catch_op, exc_matches_op, restore_exc_info_op, @@ -540,7 +540,7 @@ def transform_with(builder: IRBuilder, builder.py_get_attr(typ, '__enter__', line), [mgr_v], line ) mgr = builder.maybe_spill(mgr_v) - exc = builder.maybe_spill_assignable(builder.primitive_op(true_op, [], -1)) + exc = builder.maybe_spill_assignable(builder.true()) def try_body() -> None: if target: @@ -548,7 +548,7 @@ def try_body() -> None: body() def except_body() -> None: - builder.assign(exc, builder.primitive_op(false_op, [], -1), line) + builder.assign(exc, builder.false(), line) out_block, reraise_block = BasicBlock(), BasicBlock() builder.add_bool_branch( builder.py_call(builder.read(exit_), diff --git a/mypyc/primitives/misc_ops.py b/mypyc/primitives/misc_ops.py index 9ff548acee9e..390917a86eb1 100644 --- a/mypyc/primitives/misc_ops.py +++ b/mypyc/primitives/misc_ops.py @@ -2,11 +2,11 @@ from mypyc.ir.ops import ERR_NEVER, ERR_MAGIC, ERR_FALSE, ERR_NEG_INT from mypyc.ir.rtypes import ( - RTuple, none_rprimitive, bool_rprimitive, object_rprimitive, str_rprimitive, + RTuple, bool_rprimitive, object_rprimitive, str_rprimitive, int_rprimitive, dict_rprimitive, c_int_rprimitive ) from mypyc.primitives.registry import ( - name_ref_op, simple_emit, unary_op, func_op, custom_op, call_emit, name_emit, + simple_emit, unary_op, func_op, custom_op, call_emit, name_emit, call_negative_magic_emit, c_function_op, c_custom_op, load_address_op ) @@ -19,23 +19,6 @@ emit=name_emit('Py_None'), is_borrowed=True) -# Get an unboxed None value -none_op = name_ref_op('builtins.None', - result_type=none_rprimitive, - error_kind=ERR_NEVER, - emit=simple_emit('{dest} = 1; /* None */')) - -# Get an unboxed True value -true_op = name_ref_op('builtins.True', - result_type=bool_rprimitive, - error_kind=ERR_NEVER, - emit=simple_emit('{dest} = 1;')) - -# Get an unboxed False value -false_op = name_ref_op('builtins.False', - result_type=bool_rprimitive, - error_kind=ERR_NEVER, - emit=simple_emit('{dest} = 0;')) # Get the boxed object '...' ellipsis_op = custom_op(name='...', diff --git a/mypyc/test-data/analysis.test b/mypyc/test-data/analysis.test index c79c61bbcdeb..5b36c03f596f 100644 --- a/mypyc/test-data/analysis.test +++ b/mypyc/test-data/analysis.test @@ -16,7 +16,6 @@ def f(a): r2, r3, r4 :: bool y :: int z :: int - r5 :: None L0: x = 2 r1 = x & 1 @@ -37,8 +36,7 @@ L4: L5: z = 2 L6: - r5 = None - return r5 + return 1 (0, 0) {a} {a} (0, 1) {a} {a, x} (0, 2) {a, x} {a, x} @@ -175,7 +173,6 @@ def f(a): r2, r3, r4 :: bool y :: int x :: int - r5 :: None L0: r1 = a & 1 r2 = r1 == 0 @@ -196,8 +193,7 @@ L4: L5: x = 4 L6: - r5 = None - return r5 + return 1 (0, 0) {a} {a} (0, 1) {a} {a} (0, 2) {a} {a} @@ -246,7 +242,6 @@ def f(n): r3 :: native_int r4, r5, r6, r7 :: bool r8, m :: int - r9 :: None L0: L1: r1 = n & 1 @@ -270,8 +265,7 @@ L5: m = n goto L1 L6: - r9 = None - return r9 + return 1 (0, 0) {n} {n} (1, 0) {n} {n} (1, 1) {n} {n} @@ -323,7 +317,6 @@ def f(n): r10 :: bool r11 :: native_int r12, r13, r14, r15 :: bool - r16 :: None L0: x = 2 y = 2 @@ -368,8 +361,7 @@ L10: L11: goto L1 L12: - r16 = None - return r16 + return 1 (0, 0) {n} {i0, n} (0, 1) {i0, n} {n, x} (0, 2) {n, x} {i1, n, x} @@ -418,8 +410,8 @@ L12: (10, 2) {x, y} {n, x, y} (10, 3) {n, x, y} {n, x, y} (11, 0) {n, x, y} {n, x, y} -(12, 0) {} {r16} -(12, 1) {r16} {} +(12, 0) {} {i13} +(12, 1) {i13} {} [case testCall_Liveness] def f(x: int) -> int: @@ -472,7 +464,6 @@ def f(a): r11 :: native_int r12, r13, r14, r15 :: bool y, x :: int - r16 :: None L0: L1: r1 = a & 1 @@ -514,8 +505,7 @@ L11: x = a goto L1 L12: - r16 = None - return r16 + return 1 (0, 0) {a} {a} (1, 0) {a, r0, r8, x, y} {a, r0, r8, x, y} (1, 1) {a, r0, r8, x, y} {a, r0, r8, x, y} diff --git a/mypyc/test-data/exceptions.test b/mypyc/test-data/exceptions.test index 02db4aa9e789..125b57fc7a6c 100644 --- a/mypyc/test-data/exceptions.test +++ b/mypyc/test-data/exceptions.test @@ -35,10 +35,9 @@ def f(x, y, z): y, z :: int r0 :: object r1 :: int32 - r2 :: None - r3 :: object - r4 :: bool - r5, r6 :: None + r2 :: object + r3 :: bool + r4 :: None L0: inc_ref y :: int r0 = box(int, y) @@ -46,17 +45,15 @@ L0: dec_ref r0 if r1 < 0 goto L3 (error at f:3) else goto L1 L1: - r2 = None inc_ref z :: int - r3 = box(int, z) - r4 = CPyList_SetItem(x, y, r3) - if not r4 goto L3 (error at f:4) else goto L2 :: bool + r2 = box(int, z) + r3 = CPyList_SetItem(x, y, r2) + if not r3 goto L3 (error at f:4) else goto L2 :: bool L2: - r5 = None - return r5 + return 1 L3: - r6 = :: None - return r6 + r4 = :: None + return r4 [case testOptionalHandling] from typing import Optional @@ -72,39 +69,35 @@ def f(x: Optional[A]) -> int: [out] def f(x): x :: union[__main__.A, None] - r0 :: None - r1 :: object - r2 :: bool - r3 :: __main__.A - r4 :: None - r5 :: object - r6, r7 :: bool - r8 :: int + r0 :: object + r1 :: bool + r2 :: __main__.A + r3 :: object + r4, r5 :: bool + r6 :: int L0: - r0 = None - r1 = box(None, r0) - r2 = x is r1 - if r2 goto L1 else goto L2 :: bool + r0 = box(None, 1) + r1 = x is r0 + if r1 goto L1 else goto L2 :: bool L1: return 2 L2: inc_ref x - r3 = cast(__main__.A, x) - if is_error(r3) goto L6 (error at f:8) else goto L3 + r2 = cast(__main__.A, x) + if is_error(r2) goto L6 (error at f:8) else goto L3 L3: - r4 = None - r5 = box(None, r4) - r6 = r3 is r5 - dec_ref r3 - r7 = !r6 - if r7 goto L4 else goto L5 :: bool + r3 = box(None, 1) + r4 = r2 is r3 + dec_ref r2 + r5 = !r4 + if r5 goto L4 else goto L5 :: bool L4: return 4 L5: return 6 L6: - r8 = :: int - return r8 + r6 = :: int + return r6 [case testListSum] from typing import List @@ -194,7 +187,7 @@ def g(): r7 :: str r8, r9 :: object r10 :: bool - r11, r12 :: None + r11 :: None L0: L1: r0 = builtins :: module @@ -228,11 +221,10 @@ L6: L7: unreachable L8: - r11 = None - return r11 + return 1 L9: - r12 = :: None - return r12 + r11 = :: None + return r11 L10: dec_ref r3 goto L8 @@ -345,10 +337,8 @@ def lol() -> None: pass [out] def lol(): - r0 :: None L0: - r0 = None - return r0 + return 1 [case testExceptUndefined1] from typing import Any @@ -479,9 +469,8 @@ def f(b): r4 :: object r5 :: str r6, r7 :: object - r8 :: None - r9 :: bool - r10 :: None + r8 :: bool + r9 :: None L0: r0 = unicode_1 :: static ('a') inc_ref r0 @@ -504,7 +493,7 @@ L4: if is_error(v) goto L13 else goto L7 L5: raise UnboundLocalError("local variable 'v' referenced before assignment") - if not r9 goto L9 (error at f:7) else goto L6 :: bool + if not r8 goto L9 (error at f:7) else goto L6 :: bool L6: unreachable L7: @@ -513,11 +502,10 @@ L7: xdec_ref v if is_error(r7) goto L9 (error at f:7) else goto L14 L8: - r8 = None - return r8 + return 1 L9: - r10 = :: None - return r10 + r9 = :: None + return r9 L10: xdec_ref v goto L2 diff --git a/mypyc/test-data/irbuild-any.test b/mypyc/test-data/irbuild-any.test index 71ae2bfa457d..35f98511c530 100644 --- a/mypyc/test-data/irbuild-any.test +++ b/mypyc/test-data/irbuild-any.test @@ -52,7 +52,6 @@ def f(a, n, c): r6 :: str r7 :: object r8 :: int32 - r9 :: None L0: r0 = box(int, n) c.a = r0; r1 = is_error @@ -65,8 +64,7 @@ L0: r6 = unicode_6 :: static ('a') r7 = box(int, n) r8 = PyObject_SetAttr(a, r6, r7) - r9 = None - return r9 + return 1 [case testCoerceAnyInOps] from typing import Any, List @@ -88,14 +86,12 @@ def f1(a, n): a :: object n :: int r0, r1, r2, r3 :: object - r4 :: None L0: r0 = box(int, n) r1 = PyNumber_Add(a, r0) r2 = box(int, n) r3 = PyNumber_Add(r2, a) - r4 = None - return r4 + return 1 def f2(a, n, l): a :: object n :: int @@ -107,7 +103,6 @@ def f2(a, n, l): r8 :: bool r9 :: object r10 :: list - r11 :: None L0: r0 = box(int, n) r1 = PyObject_GetItem(a, r0) @@ -120,14 +115,12 @@ L0: r8 = CPyList_SetItem(l, n, a) r9 = box(int, n) r10 = [a, r9] - r11 = None - return r11 + return 1 def f3(a, n): a :: object n :: int r0, r1, r2, r3 :: object r4 :: int - r5 :: None L0: r0 = box(int, n) r1 = PyNumber_InPlaceAdd(a, r0) @@ -136,8 +129,7 @@ L0: r3 = PyNumber_InPlaceAdd(r2, a) r4 = unbox(int, r3) n = r4 - r5 = None - return r5 + return 1 [case testCoerceAnyInConditionalExpr] from typing import Any @@ -151,7 +143,6 @@ def f4(a, n, b): b :: bool r0, r1, r2, r3 :: object r4 :: int - r5 :: None L0: if b goto L1 else goto L2 :: bool L1: @@ -172,5 +163,5 @@ L5: L6: r4 = unbox(int, r2) n = r4 - r5 = None - return r5 + return 1 + diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 410e667d2b2c..a6691ac610e6 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -20,20 +20,16 @@ def f() -> None: return [out] def f(): - r0 :: None L0: - r0 = None - return r0 + return 1 [case testExplicitNoneReturn2] def f() -> None: return None [out] def f(): - r0 :: None L0: - r0 = None - return r0 + return 1 [case testAssignment] def f() -> int: @@ -57,12 +53,10 @@ def f(x: int) -> None: def f(x): x :: int y :: int - r0 :: None L0: y = 2 y = x - r0 = None - return r0 + return 1 [case testIntArithmetic] def f(x: int, y: int) -> int: @@ -482,10 +476,8 @@ def f() -> None: pass [out] def f(): - r0 :: None L0: - r0 = None - return r0 + return 1 [case testImplicitNoneReturn2] def f() -> None: @@ -493,11 +485,9 @@ def f() -> None: [out] def f(): x :: int - r0 :: None L0: x = 2 - r0 = None - return r0 + return 1 [case testImplicitNoneReturnAndIf] def f(x: int, y: int) -> None: @@ -513,7 +503,6 @@ def f(x, y): r2 :: bool r3 :: native_int r4, r5, r6, r7 :: bool - r8 :: None L0: r1 = x & 1 r2 = r1 == 0 @@ -536,8 +525,7 @@ L4: L5: y = 4 L6: - r8 = None - return r8 + return 1 [case testRecursion] def f(n: int) -> int: @@ -720,21 +708,16 @@ def f() -> bool: return True [out] def f(): - r0 :: bool L0: - r0 = True - return r0 + return 1 [case testFalse] def f() -> bool: return False [out] def f(): - r0 :: bool L0: - r0 = False - return r0 - + return 0 [case testBoolCond] def f(x: bool) -> bool: if x: @@ -743,15 +726,13 @@ def f(x: bool) -> bool: return True [out] def f(x): - x, r0, r1 :: bool + x :: bool L0: if x goto L1 else goto L2 :: bool L1: - r0 = False - return r0 + return 0 L2: - r1 = True - return r1 + return 1 L3: unreachable @@ -817,15 +798,13 @@ def f(x): r1 :: str r2 :: object r3, r4 :: object - r5 :: None L0: r0 = builtins :: module r1 = unicode_1 :: static ('print') r2 = CPyObject_GetAttr(r0, r1) r3 = box(short_int, 10) r4 = py_call(r2, r3) - r5 = None - return r5 + return 1 [case testPrint] import builtins @@ -837,15 +816,13 @@ def f(x): r0 :: object r1 :: str r2, r3, r4 :: object - r5 :: None L0: r0 = builtins :: module r1 = unicode_1 :: static ('print') r2 = CPyObject_GetAttr(r0, r1) r3 = box(short_int, 10) r4 = py_call(r2, r3) - r5 = None - return r5 + return 1 [case testUnicodeLiteral] def f() -> str: @@ -908,19 +885,17 @@ def g(y): r0 :: None r1 :: object r2 :: list - r3, r4 :: None - r5 :: object - r6, r7 :: None + r3 :: None + r4 :: object + r5 :: None L0: r0 = g(y) r1 = box(short_int, 2) r2 = [r1] r3 = g(r2) - r4 = None - r5 = box(None, r4) - r6 = g(r5) - r7 = None - return r7 + r4 = box(None, 1) + r5 = g(r4) + return 1 [case testCoerceToObject1] def g(y: object) -> object: @@ -936,9 +911,9 @@ def g(y): r2, a :: list r3 :: tuple[int, int] r4 :: object - r5, r6 :: bool + r5 :: bool + r6 :: object r7 :: object - r8 :: object L0: r0 = box(short_int, 2) r1 = g(r0) @@ -947,11 +922,10 @@ L0: r3 = (2, 4) r4 = box(tuple[int, int], r3) r5 = CPyList_SetItem(a, 0, r4) - r6 = True - r7 = box(bool, r6) - y = r7 - r8 = box(short_int, 6) - return r8 + r6 = box(bool, 1) + y = r6 + r7 = box(short_int, 6) + return r7 [case testCoerceToObject2] class A: @@ -968,15 +942,13 @@ def f(a, o): r1 :: bool r2 :: int r3 :: object - r4 :: None L0: r0 = box(short_int, 2) a.x = r0; r1 = is_error r2 = a.n r3 = box(int, r2) o = r3 - r4 = None - return r4 + return 1 [case testDownCast] from typing import cast, List, Tuple @@ -995,7 +967,6 @@ def f(x): r2, a :: __main__.A r3, l :: list r4, t :: tuple[int, __main__.A] - r5 :: None L0: r0 = unbox(int, x) n = r0 @@ -1007,8 +978,7 @@ L0: l = r3 r4 = unbox(tuple[int, __main__.A], x) t = r4 - r5 = None - return r5 + return 1 [case testDownCastSpecialCases] from typing import cast, Optional, Tuple @@ -1028,7 +998,6 @@ def f(o, n, t): r2, m :: bool tt :: tuple[int, int] r3 :: object - r4 :: None L0: r0 = cast(__main__.A, o) a = r0 @@ -1037,8 +1006,7 @@ L0: m = r2 r3 = box(tuple[int, int], tt) t = r3 - r4 = None - return r4 + return 1 [case testSuccessfulCast] from typing import cast, Optional, Tuple, List, Dict @@ -1076,7 +1044,6 @@ def f(o, p, n, b, t, s, a, l, d): r0 :: object l2 :: list d2 :: dict - r1 :: None L0: o = o p = p @@ -1089,8 +1056,7 @@ L0: a = a l2 = l d2 = d - r1 = None - return r1 + return 1 [case testGenericSetItem] from typing import Any @@ -1100,11 +1066,9 @@ def f(x: Any, y: Any, z: Any) -> None: def f(x, y, z): x, y, z :: object r0 :: int32 - r1 :: None L0: r0 = PyObject_SetItem(x, y, z) - r1 = None - return r1 + return 1 [case testLoadFloatSum] def assign_and_return_float_sum() -> float: @@ -1160,7 +1124,6 @@ def big_int() -> None: def big_int(): r0, a_62_bit, r1, max_62_bit, r2, b_63_bit, r3, c_63_bit, r4, max_63_bit, r5, d_64_bit, r6, max_32_bit :: int max_31_bit :: int - r7 :: None L0: r0 = int_1 :: static (4611686018427387902) a_62_bit = r0 @@ -1177,8 +1140,7 @@ L0: r6 = int_7 :: static (2147483647) max_32_bit = r6 max_31_bit = 2147483646 - r7 = None - return r7 + return 1 [case testCallableTypes] from typing import Callable @@ -1552,7 +1514,6 @@ def f(): r4 :: object r5 :: str r6, r7, r8 :: object - r9 :: None L0: r0 = __main__.globals :: static r1 = unicode_1 :: static ('x') @@ -1563,8 +1524,7 @@ L0: r6 = CPyObject_GetAttr(r4, r5) r7 = box(int, r3) r8 = py_call(r6, r7) - r9 = None - return r9 + return 1 def __top_level__(): r0, r1 :: object r2 :: bool @@ -1581,7 +1541,6 @@ def __top_level__(): r13 :: object r14 :: str r15, r16, r17 :: object - r18 :: None L0: r0 = builtins :: module r1 = builtins.None :: object @@ -1605,8 +1564,7 @@ L2: r15 = CPyObject_GetAttr(r13, r14) r16 = box(int, r12) r17 = py_call(r15, r16) - r18 = None - return r18 + return 1 [case testCallOverloaded] import m @@ -1657,14 +1615,12 @@ def main(): r0 :: object r1 :: union[int, str] r2, x :: int - r3 :: None L0: r0 = box(short_int, 0) r1 = foo(r0) r2 = unbox(int, r1) x = r2 - r3 = None - return r3 + return 1 [case testCallOverloadedNativeSubclass] from typing import overload, Union @@ -1710,14 +1666,12 @@ def main(): r0 :: object r1 :: __main__.A r2, x :: __main__.B - r3 :: None L0: r0 = box(short_int, 0) r1 = foo(r0) r2 = cast(__main__.B, r1) x = r2 - r3 = None - return r3 + return 1 [case testFunctionCallWithKeywordArgs] def f(x: int, y: str) -> None: pass @@ -1729,22 +1683,19 @@ def g() -> None: def f(x, y): x :: int y :: str - r0 :: None L0: - r0 = None - return r0 + return 1 def g(): r0 :: str r1 :: None r2 :: str - r3, r4 :: None + r3 :: None L0: r0 = unicode_1 :: static ('a') r1 = f(0, r0) r2 = unicode_2 :: static ('b') r3 = f(2, r2) - r4 = None - return r4 + return 1 [case testMethodCallWithKeywordArgs] class A: @@ -1758,23 +1709,20 @@ def A.f(self, x, y): self :: __main__.A x :: int y :: str - r0 :: None L0: - r0 = None - return r0 + return 1 def g(a): a :: __main__.A r0 :: str r1 :: None r2 :: str - r3, r4 :: None + r3 :: None L0: r0 = unicode_4 :: static ('a') r1 = a.f(0, r0) r2 = unicode_5 :: static ('b') r3 = a.f(2, r2) - r4 = None - return r4 + return 1 [case testStarArgs] from typing import Tuple @@ -1927,7 +1875,6 @@ def f(x, y, z): x, y :: int z :: str r0 :: str - r1 :: None L0: if is_error(y) goto L1 else goto L2 L1: @@ -1938,22 +1885,20 @@ L3: r0 = unicode_1 :: static ('test') z = r0 L4: - r1 = None - return r1 + return 1 def g(): r0 :: int r1 :: str r2 :: None r3 :: str - r4, r5 :: None + r4 :: None L0: r0 = :: int r1 = :: str r2 = f(4, r0, r1) r3 = :: str r4 = f(12, 6, r3) - r5 = None - return r5 + return 1 [case testMethodCallWithDefaultArgs] class A: @@ -1970,7 +1915,6 @@ def A.f(self, x, y, z): x, y :: int z :: str r0 :: str - r1 :: None L0: if is_error(y) goto L1 else goto L2 L1: @@ -1981,15 +1925,14 @@ L3: r0 = unicode_4 :: static ('test') z = r0 L4: - r1 = None - return r1 + return 1 def g(): r0, a :: __main__.A r1 :: int r2 :: str r3 :: None r4 :: str - r5, r6 :: None + r5 :: None L0: r0 = A() a = r0 @@ -1998,8 +1941,7 @@ L0: r3 = a.f(4, r1, r2) r4 = :: str r5 = a.f(12, 6, r4) - r6 = None - return r6 + return 1 [case testListComprehension] from typing import List @@ -2291,13 +2233,11 @@ def PropertyHolder.__init__(self, left, right, is_add): self :: __main__.PropertyHolder left, right :: int is_add, r0, r1, r2 :: bool - r3 :: None L0: self.left = left; r0 = is_error self.right = right; r1 = is_error self.is_add = is_add; r2 = is_error - r3 = None - return r3 + return 1 def PropertyHolder.twice_value(self): self :: __main__.PropertyHolder r0, r1 :: int @@ -2381,11 +2321,9 @@ def BaseProperty.__init__(self, value): self :: __main__.BaseProperty value :: int r0 :: bool - r1 :: None L0: self._incrementer = value; r0 = is_error - r1 = None - return r1 + return 1 def DerivedProperty.value(self): self :: __main__.DerivedProperty r0 :: int @@ -2435,12 +2373,10 @@ def DerivedProperty.__init__(self, incr_func, value): value :: int r0 :: None r1 :: bool - r2 :: None L0: r0 = BaseProperty.__init__(self, value) self._incr_func = incr_func; r1 = is_error - r2 = None - return r2 + return 1 def AgainProperty.next(self): self :: __main__.AgainProperty r0 :: object @@ -2652,7 +2588,6 @@ def __top_level__(): r79 :: dict r80 :: str r81 :: int32 - r82 :: None L0: r0 = builtins :: module r1 = builtins.None :: object @@ -2744,8 +2679,7 @@ L4: r79 = __main__.globals :: static r80 = unicode_12 :: static ('y') r81 = CPyDict_SetItem(r79, r80, r78) - r82 = None - return r82 + return 1 [case testChainedConditional] def g(x: int) -> int: @@ -2892,7 +2826,6 @@ def g_a_obj.__call__(__mypyc_self__): r10 :: object r11 :: str r12, r13 :: object - r14 :: None L0: r0 = __mypyc_self__.__mypyc_env__ r1 = r0.g @@ -2909,8 +2842,7 @@ L0: r11 = unicode_4 :: static ('print') r12 = CPyObject_GetAttr(r10, r11) r13 = py_call(r12, r9) - r14 = None - return r14 + return 1 def a(f): f :: object r0 :: __main__.a_env @@ -2951,7 +2883,6 @@ def g_b_obj.__call__(__mypyc_self__): r10 :: object r11 :: str r12, r13 :: object - r14 :: None L0: r0 = __mypyc_self__.__mypyc_env__ r1 = r0.g @@ -2968,8 +2899,7 @@ L0: r11 = unicode_4 :: static ('print') r12 = CPyObject_GetAttr(r10, r11) r13 = py_call(r12, r9) - r14 = None - return r14 + return 1 def b(f): f :: object r0 :: __main__.b_env @@ -3006,7 +2936,6 @@ def __mypyc_d_decorator_helper_____mypyc_c_decorator_helper___obj.__call__(__myp r3 :: object r4 :: str r5, r6 :: object - r7 :: None L0: r0 = __mypyc_self__.__mypyc_env__ r1 = r0.d @@ -3016,8 +2945,7 @@ L0: r4 = unicode_4 :: static ('print') r5 = CPyObject_GetAttr(r3, r4) r6 = py_call(r5, r2) - r7 = None - return r7 + return 1 def __mypyc_c_decorator_helper__(): r0 :: __main__.__mypyc_c_decorator_helper___env r1 :: __main__.__mypyc_d_decorator_helper_____mypyc_c_decorator_helper___obj @@ -3033,7 +2961,6 @@ def __mypyc_c_decorator_helper__(): r13 :: object r14 :: str r15, r16, r17, r18 :: object - r19 :: None L0: r0 = __mypyc_c_decorator_helper___env() r1 = __mypyc_d_decorator_helper_____mypyc_c_decorator_helper___obj() @@ -3054,8 +2981,7 @@ L0: r16 = py_call(r15, r12) r17 = r0.d r18 = py_call(r17) - r19 = None - return r19 + return 1 def __top_level__(): r0, r1 :: object r2 :: bool @@ -3081,7 +3007,6 @@ def __top_level__(): r27 :: dict r28 :: str r29 :: int32 - r30 :: None L0: r0 = builtins :: module r1 = builtins.None :: object @@ -3121,8 +3046,7 @@ L4: r27 = __main__.globals :: static r28 = unicode_10 :: static ('c') r29 = CPyDict_SetItem(r27, r28, r26) - r30 = None - return r30 + return 1 [case testDecoratorsSimple_toplevel] from typing import Callable @@ -3160,7 +3084,6 @@ def g_a_obj.__call__(__mypyc_self__): r10 :: object r11 :: str r12, r13 :: object - r14 :: None L0: r0 = __mypyc_self__.__mypyc_env__ r1 = r0.g @@ -3177,8 +3100,7 @@ L0: r11 = unicode_4 :: static ('print') r12 = CPyObject_GetAttr(r10, r11) r13 = py_call(r12, r9) - r14 = None - return r14 + return 1 def a(f): f :: object r0 :: __main__.a_env @@ -3207,7 +3129,6 @@ def __top_level__(): r13 :: object r14 :: str r15 :: int32 - r16 :: None L0: r0 = builtins :: module r1 = builtins.None :: object @@ -3233,8 +3154,7 @@ L4: r13 = CPyObject_GetAttr(r10, r12) r14 = unicode_2 :: static ('Callable') r15 = CPyDict_SetItem(r11, r14, r13) - r16 = None - return r16 + return 1 [case testAnyAllG] from typing import Iterable @@ -3248,86 +3168,82 @@ def call_all(l: Iterable[int]) -> bool: [out] def call_any(l): l :: object - r0, r1 :: bool - r2, r3 :: object - r4, i :: int - r5 :: bool - r6 :: native_int - r7, r8, r9, r10, r11 :: bool + r0 :: bool + r1, r2 :: object + r3, i :: int + r4 :: bool + r5 :: native_int + r6, r7, r8, r9 :: bool L0: - r1 = False - r0 = r1 - r2 = PyObject_GetIter(l) + r0 = 0 + r1 = PyObject_GetIter(l) L1: - r3 = PyIter_Next(r2) - if is_error(r3) goto L9 else goto L2 + r2 = PyIter_Next(r1) + if is_error(r2) goto L9 else goto L2 L2: - r4 = unbox(int, r3) - i = r4 - r6 = i & 1 - r7 = r6 == 0 - if r7 goto L3 else goto L4 :: bool + r3 = unbox(int, r2) + i = r3 + r5 = i & 1 + r6 = r5 == 0 + if r6 goto L3 else goto L4 :: bool L3: - r8 = i == 0 - r5 = r8 + r7 = i == 0 + r4 = r7 goto L5 L4: - r9 = CPyTagged_IsEq_(i, 0) - r5 = r9 + r8 = CPyTagged_IsEq_(i, 0) + r4 = r8 L5: - if r5 goto L6 else goto L7 :: bool + if r4 goto L6 else goto L7 :: bool L6: - r10 = True - r0 = r10 + r0 = 1 goto L11 L7: L8: goto L1 L9: - r11 = CPy_NoErrOccured() + r9 = CPy_NoErrOccured() L10: L11: return r0 def call_all(l): l :: object - r0, r1 :: bool - r2, r3 :: object - r4, i :: int - r5 :: bool - r6 :: native_int - r7, r8, r9, r10, r11, r12 :: bool + r0 :: bool + r1, r2 :: object + r3, i :: int + r4 :: bool + r5 :: native_int + r6, r7, r8, r9, r10 :: bool L0: - r1 = True - r0 = r1 - r2 = PyObject_GetIter(l) + r0 = 1 + r1 = PyObject_GetIter(l) L1: - r3 = PyIter_Next(r2) - if is_error(r3) goto L9 else goto L2 + r2 = PyIter_Next(r1) + if is_error(r2) goto L9 else goto L2 L2: - r4 = unbox(int, r3) - i = r4 - r6 = i & 1 - r7 = r6 == 0 - if r7 goto L3 else goto L4 :: bool + r3 = unbox(int, r2) + i = r3 + r5 = i & 1 + r6 = r5 == 0 + if r6 goto L3 else goto L4 :: bool L3: - r8 = i == 0 - r5 = r8 + r7 = i == 0 + r4 = r7 goto L5 L4: - r9 = CPyTagged_IsEq_(i, 0) - r5 = r9 + r8 = CPyTagged_IsEq_(i, 0) + r4 = r8 L5: - r10 = !r5 - if r10 goto L6 else goto L7 :: bool + r9 = !r4 + if r9 goto L6 else goto L7 :: bool L6: - r11 = False - r0 = r11 + r0 = 0 goto L11 L7: L8: goto L1 L9: - r12 = CPy_NoErrOccured() + r10 = CPy_NoErrOccured() L10: L11: return r0 @@ -3342,16 +3258,13 @@ def lol(x): x :: object r0, r1 :: str r2 :: int32 - r3, r4 :: None - r5 :: object + r3 :: object L0: r0 = unicode_5 :: static ('x') r1 = unicode_6 :: static ('5') r2 = PyObject_SetAttr(x, r0, r1) - r3 = None - r4 = None - r5 = box(None, r4) - return r5 + r3 = box(None, 1) + return r3 [case testFinalModuleInt] from typing import Final @@ -3415,15 +3328,13 @@ def f(a: bool) -> bool: return y [out] def f(a): - a, r0, r1 :: bool + a :: bool L0: if a goto L1 else goto L2 :: bool L1: - r0 = True - return r0 + return 1 L2: - r1 = False - return r1 + return 0 L3: unreachable @@ -3443,12 +3354,11 @@ def f(a: bool) -> int: def C.__mypyc_defaults_setup(__mypyc_self__): __mypyc_self__ :: __main__.C r0 :: bool - r1, r2 :: bool + r1 :: bool L0: __mypyc_self__.x = 2; r0 = is_error __mypyc_self__.y = 4; r1 = is_error - r2 = True - return r2 + return 1 def f(a): a :: bool L0: @@ -3541,10 +3451,8 @@ def foo(z: Targ) -> None: [out] def foo(z): z :: object - r0 :: None L0: - r0 = None - return r0 + return 1 [case testDirectlyCall__bool__] class A: @@ -3563,16 +3471,12 @@ def lol(x: A) -> int: [out] def A.__bool__(self): self :: __main__.A - r0 :: bool L0: - r0 = True - return r0 + return 1 def B.__bool__(self): self :: __main__.B - r0 :: bool L0: - r0 = False - return r0 + return 0 def lol(x): x :: __main__.A r0 :: bool @@ -3595,15 +3499,13 @@ def f(x): r0 :: object r1 :: str r2, r3, r4 :: object - r5 :: None L0: r0 = builtins :: module r1 = unicode_1 :: static ('reveal_type') r2 = CPyObject_GetAttr(r0, r1) r3 = box(int, x) r4 = py_call(r2, r3) - r5 = None - return r5 + return 1 [case testCallCWithStrJoinMethod] from typing import List diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index 3ad447c458d6..d2cf53e90c78 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -22,11 +22,9 @@ def f(a: A) -> None: def f(a): a :: __main__.A r0 :: bool - r1 :: None L0: a.x = 2; r0 = is_error - r1 = None - return r1 + return 1 [case testUserClassInList] class C: @@ -80,12 +78,10 @@ def g(a): a :: __main__.A r0 :: str r1 :: int - r2 :: None L0: r0 = unicode_4 :: static ('hi') r1 = a.f(2, r0) - r2 = None - return r2 + return 1 [case testForwardUse] def g(a: A) -> int: @@ -114,25 +110,23 @@ class Node: def Node.length(self): self :: __main__.Node r0 :: union[__main__.Node, None] - r1 :: None - r2 :: object - r3, r4 :: bool - r5 :: union[__main__.Node, None] - r6 :: __main__.Node - r7, r8 :: int + r1 :: object + r2, r3 :: bool + r4 :: union[__main__.Node, None] + r5 :: __main__.Node + r6, r7 :: int L0: r0 = self.next - r1 = None - r2 = box(None, r1) - r3 = r0 is r2 - r4 = !r3 - if r4 goto L1 else goto L2 :: bool + r1 = box(None, 1) + r2 = r0 is r1 + r3 = !r2 + if r3 goto L1 else goto L2 :: bool L1: - r5 = self.next - r6 = cast(__main__.Node, r5) - r7 = r6.length() - r8 = CPyTagged_Add(2, r7) - return r8 + r4 = self.next + r5 = cast(__main__.Node, r4) + r6 = r5.length() + r7 = CPyTagged_Add(2, r6) + return r7 L2: return 2 @@ -148,21 +142,17 @@ class B(A): def A.__init__(self): self :: __main__.A r0 :: bool - r1 :: None L0: self.x = 20; r0 = is_error - r1 = None - return r1 + return 1 def B.__init__(self): self :: __main__.B r0 :: bool r1 :: bool - r2 :: None L0: self.x = 40; r0 = is_error self.y = 60; r1 = is_error - r2 = None - return r2 + return 1 [case testAttrLvalue] class O(object): @@ -176,11 +166,9 @@ def increment(o: O) -> O: def O.__init__(self): self :: __main__.O r0 :: bool - r1 :: None L0: self.x = 2; r0 = is_error - r1 = None - return r1 + return 1 def increment(o): o :: __main__.O r0 :: int @@ -364,7 +352,6 @@ def __top_level__(): r78 :: dict r79 :: str r80 :: int32 - r81 :: None L0: r0 = builtins :: module r1 = builtins.None :: object @@ -462,8 +449,7 @@ L6: r78 = __main__.globals :: static r79 = unicode_12 :: static ('D') r80 = CPyDict_SetItem(r78, r79, r72) - r81 = None - return r81 + return 1 [case testIsInstance] class A: pass @@ -646,22 +632,18 @@ def A.__init__(self, x): self :: __main__.A x :: int r0 :: bool - r1 :: None L0: self.x = x; r0 = is_error - r1 = None - return r1 + return 1 def B.__init__(self, x, y): self :: __main__.B x, y :: int r0 :: None r1 :: bool - r2 :: None L0: r0 = A.__init__(self, x) self.y = y; r1 = is_error - r2 = None - return r2 + return 1 [case testClassMethod] class C: @@ -710,22 +692,18 @@ def A.__init__(self, x): self :: __main__.A x :: int r0 :: bool - r1 :: None L0: self.x = x; r0 = is_error - r1 = None - return r1 + return 1 def B.__init__(self, x, y): self :: __main__.B x, y :: int r0 :: None r1 :: bool - r2 :: None L0: r0 = A.__init__(self, x) self.y = y; r1 = is_error - r2 = None - return r2 + return 1 [case testSuper2] from mypy_extensions import trait @@ -739,17 +717,14 @@ class X(T): [out] def T.foo(self): self :: __main__.T - r0 :: None L0: - r0 = None - return r0 + return 1 def X.foo(self): self :: __main__.X - r0, r1 :: None + r0 :: None L0: r0 = T.foo(self) - r1 = None - return r1 + return 1 [case testClassVariable] from typing import ClassVar @@ -819,12 +794,10 @@ def fOpt2(a: Derived, b: Derived) -> bool: def Base.__eq__(self, other): self :: __main__.Base other :: object - r0 :: bool - r1 :: object + r0 :: object L0: - r0 = False - r1 = box(bool, r0) - return r1 + r0 = box(bool, 0) + return r0 def Base.__ne__(self, rhs): self :: __main__.Base rhs, r0, r1 :: object @@ -844,12 +817,10 @@ L2: def Derived.__eq__(self, other): self :: __main__.Derived other :: object - r0 :: bool - r1 :: object + r0 :: object L0: - r0 = True - r1 = box(bool, r0) - return r1 + r0 = box(bool, 1) + return r0 def f(a, b): a, b :: __main__.Base r0 :: object @@ -940,12 +911,10 @@ L0: def Derived.__eq__(self, other): self :: __main__.Derived other :: object - r0 :: bool - r1 :: object + r0 :: object L0: - r0 = True - r1 = box(bool, r0) - return r1 + r0 = box(bool, 1) + return r0 def Derived.__ne__(self, rhs): self :: __main__.Derived rhs, r0, r1 :: object @@ -980,18 +949,15 @@ class B(A): def A.lol(self): self :: __main__.A r0 :: bool - r1 :: None L0: self.x = 200; r0 = is_error - r1 = None - return r1 + return 1 def A.__mypyc_defaults_setup(__mypyc_self__): __mypyc_self__ :: __main__.A - r0, r1 :: bool + r0 :: bool L0: __mypyc_self__.x = 20; r0 = is_error - r1 = True - return r1 + return 1 def B.__mypyc_defaults_setup(__mypyc_self__): __mypyc_self__ :: __main__.B r0 :: bool @@ -1000,9 +966,8 @@ def B.__mypyc_defaults_setup(__mypyc_self__): r3 :: object r4 :: str r5 :: bool - r6 :: None - r7 :: object - r8, r9, r10, r11 :: bool + r6 :: object + r7, r8 :: bool L0: __mypyc_self__.x = 20; r0 = is_error r1 = __main__.globals :: static @@ -1010,13 +975,10 @@ L0: r3 = CPyDict_GetItem(r1, r2) r4 = cast(str, r3) __mypyc_self__.y = r4; r5 = is_error - r6 = None - r7 = box(None, r6) - __mypyc_self__.z = r7; r8 = is_error - r9 = True - __mypyc_self__.b = r9; r10 = is_error - r11 = True - return r11 + r6 = box(None, 1) + __mypyc_self__.z = r6; r7 = is_error + __mypyc_self__.b = 1; r8 = is_error + return 1 [case testSubclassDictSpecalized] from typing import Dict @@ -1029,12 +991,9 @@ def foo(x: WelpDict) -> None: def foo(x): x :: dict r0 :: int32 - r1, r2 :: None L0: r0 = CPyDict_Update(x, x) - r1 = None - r2 = None - return r2 + return 1 [case testNoSpuriousLinearity] # Make sure that the non-trait MRO linearity check isn't affected by processing order diff --git a/mypyc/test-data/irbuild-dict.test b/mypyc/test-data/irbuild-dict.test index dc32cb1f011e..2ba45c4143ee 100644 --- a/mypyc/test-data/irbuild-dict.test +++ b/mypyc/test-data/irbuild-dict.test @@ -20,17 +20,13 @@ def f(d: Dict[int, bool]) -> None: [out] def f(d): d :: dict - r0 :: bool - r1, r2 :: object - r3 :: int32 - r4 :: None + r0, r1 :: object + r2 :: int32 L0: - r0 = False - r1 = box(short_int, 0) - r2 = box(bool, r0) - r3 = CPyDict_SetItem(d, r1, r2) - r4 = None - return r4 + r0 = box(short_int, 0) + r1 = box(bool, 0) + r2 = CPyDict_SetItem(d, r0, r1) + return 1 [case testNewEmptyDict] from typing import Dict @@ -39,12 +35,10 @@ def f() -> None: [out] def f(): r0, d :: dict - r1 :: None L0: r0 = PyDict_New() d = r0 - r1 = None - return r1 + return 1 [case testNewDictWithValues] def f(x: object) -> None: @@ -55,15 +49,13 @@ def f(x): r0 :: str r1, r2 :: object r3, d :: dict - r4 :: None L0: r0 = unicode_1 :: static r1 = box(short_int, 2) r2 = box(short_int, 4) r3 = CPyDict_Build(2, r1, r2, r0, x) d = r3 - r4 = None - return r4 + return 1 [case testInDict] from typing import Dict @@ -77,18 +69,16 @@ def f(d): d :: dict r0 :: object r1 :: int32 - r2, r3, r4 :: bool + r2 :: bool L0: r0 = box(short_int, 8) r1 = PyDict_Contains(d, r0) r2 = truncate r1: int32 to builtins.bool if r2 goto L1 else goto L2 :: bool L1: - r3 = True - return r3 + return 1 L2: - r4 = False - return r4 + return 0 L3: unreachable @@ -104,7 +94,7 @@ def f(d): d :: dict r0 :: object r1 :: int32 - r2, r3, r4, r5 :: bool + r2, r3 :: bool L0: r0 = box(short_int, 8) r1 = PyDict_Contains(d, r0) @@ -112,11 +102,9 @@ L0: r3 = !r2 if r3 goto L1 else goto L2 :: bool L1: - r4 = True - return r4 + return 1 L2: - r5 = False - return r5 + return 0 L3: unreachable @@ -128,12 +116,9 @@ def f(a: Dict[int, int], b: Dict[int, int]) -> None: def f(a, b): a, b :: dict r0 :: int32 - r1, r2 :: None L0: r0 = CPyDict_Update(a, b) - r1 = None - r2 = None - return r2 + return 1 [case testDictKeyLvalue] from typing import Dict @@ -230,21 +215,19 @@ def print_dict_methods(d1, d2): r9 :: object r10 :: int32 r11 :: bool - r12 :: None - r13, r14 :: bool - r15 :: short_int - r16 :: native_int - r17 :: short_int - r18 :: object - r19 :: tuple[bool, int, object, object] - r20 :: int - r21 :: bool - r22, r23 :: object - r24, r25, k :: int - r26, r27, r28, r29, r30 :: object - r31 :: int32 - r32, r33 :: bool - r34 :: None + r12, r13 :: bool + r14 :: short_int + r15 :: native_int + r16 :: short_int + r17 :: object + r18 :: tuple[bool, int, object, object] + r19 :: int + r20 :: bool + r21, r22 :: object + r23, r24, k :: int + r25, r26, r27, r28, r29 :: object + r30 :: int32 + r31, r32 :: bool L0: r0 = 0 r1 = PyDict_Size(d1) @@ -265,46 +248,44 @@ L2: r11 = truncate r10: int32 to builtins.bool if r11 goto L3 else goto L4 :: bool L3: - r12 = None - return r12 + return 1 L4: L5: - r13 = CPyDict_CheckSize(d1, r2) + r12 = CPyDict_CheckSize(d1, r2) goto L1 L6: - r14 = CPy_NoErrOccured() + r13 = CPy_NoErrOccured() L7: - r15 = 0 - r16 = PyDict_Size(d2) - r17 = r16 << 1 - r18 = CPyDict_GetItemsIter(d2) + r14 = 0 + r15 = PyDict_Size(d2) + r16 = r15 << 1 + r17 = CPyDict_GetItemsIter(d2) L8: - r19 = CPyDict_NextItem(r18, r15) - r20 = r19[1] - r15 = r20 - r21 = r19[0] - if r21 goto L9 else goto L11 :: bool + r18 = CPyDict_NextItem(r17, r14) + r19 = r18[1] + r14 = r19 + r20 = r18[0] + if r20 goto L9 else goto L11 :: bool L9: - r22 = r19[2] - r23 = r19[3] + r21 = r18[2] + r22 = r18[3] + r23 = unbox(int, r21) r24 = unbox(int, r22) - r25 = unbox(int, r23) - k = r24 - v = r25 - r26 = box(int, k) - r27 = CPyDict_GetItem(d2, r26) - r28 = box(int, v) - r29 = PyNumber_InPlaceAdd(r27, r28) - r30 = box(int, k) - r31 = CPyDict_SetItem(d2, r30, r29) + k = r23 + v = r24 + r25 = box(int, k) + r26 = CPyDict_GetItem(d2, r25) + r27 = box(int, v) + r28 = PyNumber_InPlaceAdd(r26, r27) + r29 = box(int, k) + r30 = CPyDict_SetItem(d2, r29, r28) L10: - r32 = CPyDict_CheckSize(d2, r17) + r31 = CPyDict_CheckSize(d2, r16) goto L8 L11: - r33 = CPy_NoErrOccured() + r32 = CPy_NoErrOccured() L12: - r34 = None - return r34 + return 1 [case testDictLoadAddress] def f() -> None: @@ -312,9 +293,7 @@ def f() -> None: [out] def f(): r0, x :: object - r1 :: None L0: r0 = load_address PyDict_Type x = r0 - r1 = None - return r1 + return 1 diff --git a/mypyc/test-data/irbuild-generics.test b/mypyc/test-data/irbuild-generics.test index 069c33f303e9..855c031e55d2 100644 --- a/mypyc/test-data/irbuild-generics.test +++ b/mypyc/test-data/irbuild-generics.test @@ -27,7 +27,6 @@ def h(x, y): r0, r1 :: object r2 :: int r3 :: list - r4 :: None L0: r0 = box(int, x) r1 = f(r0) @@ -35,8 +34,7 @@ L0: x = r2 r3 = g(y) y = r3 - r4 = None - return r4 + return 1 [case testGenericAttrAndTypeApplication] from typing import TypeVar, Generic @@ -54,7 +52,6 @@ def f(): r2 :: bool r3 :: object r4, r5 :: int - r6 :: None L0: r0 = C() c = r0 @@ -63,8 +60,7 @@ L0: r3 = c.x r4 = unbox(int, r3) r5 = CPyTagged_Add(4, r4) - r6 = None - return r6 + return 1 [case testGenericMethod] from typing import TypeVar, Generic @@ -86,11 +82,9 @@ def C.__init__(self, x): self :: __main__.C x :: object r0 :: bool - r1 :: None L0: self.x = x; r0 = is_error - r1 = None - return r1 + return 1 def C.get(self): self :: __main__.C r0 :: object @@ -101,11 +95,9 @@ def C.set(self, y): self :: __main__.C y :: object r0 :: bool - r1 :: None L0: self.x = y; r0 = is_error - r1 = None - return r1 + return 1 def f(x): x :: __main__.C r0 :: object @@ -115,7 +107,6 @@ def f(x): r4 :: None r5 :: object r6 :: __main__.C - r7 :: None L0: r0 = x.get() r1 = unbox(int, r0) @@ -126,6 +117,4 @@ L0: r5 = box(short_int, 4) r6 = C(r5) x = r6 - r7 = None - return r7 - + return 1 diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test index cd28ec286791..dac0091d1705 100644 --- a/mypyc/test-data/irbuild-lists.test +++ b/mypyc/test-data/irbuild-lists.test @@ -53,12 +53,10 @@ def f(x): x :: list r0 :: object r1 :: bool - r2 :: None L0: r0 = box(short_int, 2) r1 = CPyList_SetItem(x, 0, r0) - r2 = None - return r2 + return 1 [case testNewListEmpty] from typing import List @@ -67,12 +65,10 @@ def f() -> None: [out] def f(): r0, x :: list - r1 :: None L0: r0 = [] x = r0 - r1 = None - return r1 + return 1 [case testNewListTwoItems] from typing import List @@ -82,14 +78,12 @@ def f() -> None: def f(): r0, r1 :: object r2, x :: list - r3 :: None L0: r0 = box(short_int, 2) r1 = box(short_int, 4) r2 = [r0, r1] x = r2 - r3 = None - return r3 + return 1 [case testListMultiply] from typing import List @@ -102,7 +96,6 @@ def f(a): r0, b :: list r1 :: object r2, r3 :: list - r4 :: None L0: r0 = CPySequence_Multiply(a, 4) b = r0 @@ -110,8 +103,7 @@ L0: r2 = [r1] r3 = CPySequence_RMultiply(6, r2) b = r3 - r4 = None - return r4 + return 1 [case testListLen] from typing import List @@ -139,13 +131,10 @@ def f(a, x): x :: int r0 :: object r1 :: int32 - r2, r3 :: None L0: r0 = box(int, x) r1 = PyList_Append(a, r0) - r2 = None - r3 = None - return r3 + return 1 [case testIndexLvalue] from typing import List diff --git a/mypyc/test-data/irbuild-nested.test b/mypyc/test-data/irbuild-nested.test index 62f8c07812d4..0676006074ff 100644 --- a/mypyc/test-data/irbuild-nested.test +++ b/mypyc/test-data/irbuild-nested.test @@ -51,15 +51,13 @@ def inner_a_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.inner_a_obj r0 :: __main__.a_env r1, inner :: object - r2 :: None - r3 :: object + r2 :: object L0: r0 = __mypyc_self__.__mypyc_env__ r1 = r0.inner inner = r1 - r2 = None - r3 = box(None, r2) - return r3 + r2 = box(None, 1) + return r2 def a(): r0 :: __main__.a_env r1 :: __main__.inner_a_obj diff --git a/mypyc/test-data/irbuild-optional.test b/mypyc/test-data/irbuild-optional.test index 320186fde8d8..431b8f473d25 100644 --- a/mypyc/test-data/irbuild-optional.test +++ b/mypyc/test-data/irbuild-optional.test @@ -10,14 +10,12 @@ def f(x: Optional[A]) -> int: [out] def f(x): x :: union[__main__.A, None] - r0 :: None - r1 :: object - r2 :: bool + r0 :: object + r1 :: bool L0: - r0 = None - r1 = box(None, r0) - r2 = x is r1 - if r2 goto L1 else goto L2 :: bool + r0 = box(None, 1) + r1 = x is r0 + if r1 goto L1 else goto L2 :: bool L1: return 2 L2: @@ -35,15 +33,13 @@ def f(x: Optional[A]) -> int: [out] def f(x): x :: union[__main__.A, None] - r0 :: None - r1 :: object - r2, r3 :: bool + r0 :: object + r1, r2 :: bool L0: - r0 = None - r1 = box(None, r0) - r2 = x is r1 - r3 = !r2 - if r3 goto L1 else goto L2 :: bool + r0 = box(None, 1) + r1 = x is r0 + r2 = !r1 + if r2 goto L1 else goto L2 :: bool L1: return 2 L2: @@ -89,10 +85,8 @@ def f(x: Optional[A]) -> int: [out] def B.__bool__(self): self :: __main__.B - r0 :: bool L0: - r0 = False - return r0 + return 0 def f(x): x :: union[__main__.A, None] r0 :: object @@ -130,35 +124,29 @@ def f(x: Optional[A], y: Optional[A], z: Optional[int]) -> None: def f(x, y, z): x, y :: union[__main__.A, None] z :: union[int, None] - r0 :: None - r1 :: object - r2 :: __main__.A - r3 :: object - r4, a :: __main__.A - r5 :: object - r6 :: bool - r7 :: None - r8 :: object - r9 :: bool - r10 :: None + r0 :: object + r1 :: __main__.A + r2 :: object + r3, a :: __main__.A + r4 :: object + r5 :: bool + r6 :: object + r7 :: bool L0: - r0 = None - r1 = box(None, r0) + r0 = box(None, 1) + x = r0 + r1 = A() x = r1 - r2 = A() - x = r2 x = y - r3 = box(short_int, 2) - z = r3 - r4 = A() - a = r4 - r5 = box(short_int, 2) - a.a = r5; r6 = is_error - r7 = None - r8 = box(None, r7) - a.a = r8; r9 = is_error - r10 = None - return r10 + r2 = box(short_int, 2) + z = r2 + r3 = A() + a = r3 + r4 = box(short_int, 2) + a.a = r4; r5 = is_error + r6 = box(None, 1) + a.a = r6; r7 = is_error + return 1 [case testBoxOptionalListItem] from typing import List, Optional @@ -171,18 +159,14 @@ def f(x): x :: list r0 :: object r1 :: bool - r2 :: None - r3 :: object - r4 :: bool - r5 :: None + r2 :: object + r3 :: bool L0: r0 = box(short_int, 0) r1 = CPyList_SetItem(x, 0, r0) - r2 = None - r3 = box(None, r2) - r4 = CPyList_SetItem(x, 2, r3) - r5 = None - return r5 + r2 = box(None, 1) + r3 = CPyList_SetItem(x, 2, r2) + return 1 [case testNarrowDownFromOptional] from typing import Optional @@ -199,23 +183,21 @@ def f(x: Optional[A]) -> A: def f(x): x :: union[__main__.A, None] r0, y :: __main__.A - r1 :: None - r2 :: object - r3, r4 :: bool - r5, r6 :: __main__.A + r1 :: object + r2, r3 :: bool + r4, r5 :: __main__.A L0: r0 = A() y = r0 - r1 = None - r2 = box(None, r1) - r3 = x is r2 - r4 = !r3 - if r4 goto L1 else goto L2 :: bool + r1 = box(None, 1) + r2 = x is r1 + r3 = !r2 + if r3 goto L1 else goto L2 :: bool L1: + r4 = cast(__main__.A, x) + y = r4 r5 = cast(__main__.A, x) - y = r5 - r6 = cast(__main__.A, x) - return r6 + return r5 L2: return y @@ -229,49 +211,43 @@ def f(y: int) -> None: [out] def f(y): y :: int - r0 :: None x :: union[int, None] - r1 :: object - r2 :: bool - r3 :: native_int - r4, r5, r6 :: bool + r0 :: object + r1 :: bool + r2 :: native_int + r3, r4, r5 :: bool + r6 :: object r7 :: object - r8 :: None - r9 :: object - r10, r11 :: bool - r12 :: int - r13 :: None + r8, r9 :: bool + r10 :: int L0: - r0 = None - r1 = box(None, r0) - x = r1 - r3 = y & 1 - r4 = r3 == 0 - if r4 goto L1 else goto L2 :: bool + r0 = box(None, 1) + x = r0 + r2 = y & 1 + r3 = r2 == 0 + if r3 goto L1 else goto L2 :: bool L1: - r5 = y == 2 - r2 = r5 + r4 = y == 2 + r1 = r4 goto L3 L2: - r6 = CPyTagged_IsEq_(y, 2) - r2 = r6 + r5 = CPyTagged_IsEq_(y, 2) + r1 = r5 L3: - if r2 goto L4 else goto L5 :: bool + if r1 goto L4 else goto L5 :: bool L4: - r7 = box(int, y) - x = r7 + r6 = box(int, y) + x = r6 L5: - r8 = None - r9 = box(None, r8) - r10 = x is r9 - r11 = !r10 - if r11 goto L6 else goto L7 :: bool + r7 = box(None, 1) + r8 = x is r7 + r9 = !r8 + if r9 goto L6 else goto L7 :: bool L6: - r12 = unbox(int, x) - y = r12 + r10 = unbox(int, x) + y = r10 L7: - r13 = None - return r13 + return 1 [case testUnionType] from typing import Union @@ -346,7 +322,6 @@ def get(o): r5 :: object r6 :: __main__.B r7, z :: object - r8 :: None L0: r1 = __main__.A :: type r2 = type_is o, r1 @@ -363,18 +338,15 @@ L2: r0 = r7 L3: z = r0 - r8 = None - return r8 + return 1 def set(o, s): o :: union[__main__.A, __main__.B] s, r0 :: str r1 :: int32 - r2 :: None L0: r0 = unicode_5 :: static ('a') r1 = PyObject_SetAttr(o, r0, s) - r2 = None - return r2 + return 1 [case testUnionMethodCall] from typing import Union @@ -420,7 +392,6 @@ def g(o): r12 :: object r13 :: int r14, z :: object - r15 :: None L0: r1 = __main__.A :: type r2 = type_is o, r1 @@ -449,8 +420,7 @@ L4: r0 = r14 L5: z = r0 - r15 = None - return r15 + return 1 [case testUnionWithNonNativeItem] from typing import Union @@ -481,7 +451,6 @@ def f(o): r6 :: str r7 :: object r8 :: int - r9 :: None L0: r1 = __main__.A :: type r2 = type_is o, r1 @@ -498,8 +467,7 @@ L2: r8 = unbox(int, r7) r0 = r8 L3: - r9 = None - return r9 + return 1 def g(o): o :: union[object, __main__.A] r0 :: int @@ -511,7 +479,6 @@ def g(o): r6 :: str r7 :: object r8 :: int - r9 :: None L0: r1 = __main__.A :: type r2 = type_is o, r1 @@ -528,8 +495,7 @@ L2: r8 = unbox(int, r7) r0 = r8 L3: - r9 = None - return r9 + return 1 [case testUnionWithNoNativeItems] from typing import Union @@ -550,12 +516,11 @@ def f(o): r0, r1 :: object r2 :: str r3 :: object - r4 :: None L0: r1 = o r2 = unicode_6 :: static ('x') r3 = CPyObject_GetAttr(r1, r2) r0 = r3 L1: - r4 = None - return r4 + return 1 + diff --git a/mypyc/test-data/irbuild-set.test b/mypyc/test-data/irbuild-set.test index 7a8e8edf2fc1..6c5214521944 100644 --- a/mypyc/test-data/irbuild-set.test +++ b/mypyc/test-data/irbuild-set.test @@ -112,13 +112,11 @@ def f(): r0, x :: set r1 :: object r2 :: bool - r3 :: None L0: r0 = set x = r0 r1 = box(short_int, 2) r2 = CPySet_Remove(x, r1) - r3 = None return x [case testSetDiscard] @@ -132,13 +130,11 @@ def f(): r0, x :: set r1 :: object r2 :: int32 - r3 :: None L0: r0 = set x = r0 r1 = box(short_int, 2) r2 = PySet_Discard(x, r1) - r3 = None return x [case testSetAdd] @@ -152,13 +148,11 @@ def f(): r0, x :: set r1 :: object r2 :: int32 - r3 :: None L0: r0 = set x = r0 r1 = box(short_int, 2) r2 = PySet_Add(x, r1) - r3 = None return x [case testSetClear] @@ -171,12 +165,10 @@ def f() -> Set[int]: def f(): r0, x :: set r1 :: int32 - r2 :: None L0: r0 = set x = r0 r1 = PySet_Clear(x) - r2 = None return x [case testSetPop] @@ -202,12 +194,9 @@ def update(s, x): s :: set x :: list r0 :: int32 - r1, r2 :: None L0: r0 = _PySet_Update(s, x) - r1 = None - r2 = None - return r2 + return 1 [case testSetDisplay] from typing import Set diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index 4140d42e9998..5ed1c1e12e3d 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -11,7 +11,6 @@ def f(): r1 :: bool r2 :: int r3 :: short_int - r4 :: None L0: x = 0 r0 = 0 @@ -28,8 +27,7 @@ L3: i = r3 goto L1 L4: - r4 = None - return r4 + return 1 [case testForInNegativeRange] def f() -> None: @@ -41,7 +39,6 @@ def f(): i :: int r1 :: bool r2 :: short_int - r3 :: None L0: r0 = 20 i = r0 @@ -55,8 +52,7 @@ L3: i = r2 goto L1 L4: - r3 = None - return r3 + return 1 [case testBreak] def f() -> None: @@ -71,7 +67,6 @@ def f(): r2 :: bool r3 :: native_int r4, r5, r6, r7 :: bool - r8 :: None L0: n = 0 L1: @@ -92,8 +87,7 @@ L4: if r0 goto L5 else goto L6 :: bool L5: L6: - r8 = None - return r8 + return 1 [case testBreakFor] def f() -> None: @@ -105,7 +99,6 @@ def f(): n :: int r1 :: bool r2 :: short_int - r3 :: None L0: r0 = 0 n = r0 @@ -120,8 +113,7 @@ L3: n = r2 goto L1 L4: - r3 = None - return r3 + return 1 [case testBreakNested] def f() -> None: @@ -143,7 +135,6 @@ def f(): r10 :: bool r11 :: native_int r12, r13, r14, r15 :: bool - r16 :: None L0: n = 0 L1: @@ -182,8 +173,7 @@ L9: L10: L11: L12: - r16 = None - return r16 + return 1 [case testContinue] def f() -> None: @@ -198,7 +188,6 @@ def f(): r2 :: bool r3 :: native_int r4, r5, r6, r7 :: bool - r8 :: None L0: n = 0 L1: @@ -220,8 +209,7 @@ L4: L5: goto L1 L6: - r8 = None - return r8 + return 1 [case testContinueFor] def f() -> None: @@ -233,7 +221,6 @@ def f(): n :: int r1 :: bool r2 :: short_int - r3 :: None L0: r0 = 0 n = r0 @@ -247,8 +234,7 @@ L3: n = r2 goto L1 L4: - r3 = None - return r3 + return 1 [case testContinueNested] def f() -> None: @@ -270,7 +256,6 @@ def f(): r10 :: bool r11 :: native_int r12, r13, r14, r15 :: bool - r16 :: None L0: n = 0 L1: @@ -311,8 +296,7 @@ L10: L11: goto L1 L12: - r16 = None - return r16 + return 1 [case testForList] from typing import List @@ -377,7 +361,6 @@ def f(d): r9, r10 :: object r11 :: int r12, r13 :: bool - r14 :: None L0: r0 = 0 r1 = PyDict_Size(d) @@ -402,8 +385,7 @@ L3: L4: r13 = CPy_NoErrOccured() L5: - r14 = None - return r14 + return 1 [case testForDictContinue] from typing import Dict @@ -501,14 +483,12 @@ def from_tuple(t): y :: str r0 :: int r1 :: str - r2 :: None L0: r0 = t[0] x = r0 r1 = t[1] y = r1 - r2 = None - return r2 + return 1 def from_any(a): a, x, y, r0, r1 :: object r2 :: bool @@ -516,7 +496,6 @@ def from_any(a): r4 :: bool r5 :: object r6 :: bool - r7 :: None L0: r0 = PyObject_GetIter(a) r1 = PyIter_Next(r0) @@ -539,8 +518,7 @@ L5: raise ValueError('too many values to unpack') unreachable L6: - r7 = None - return r7 + return 1 [case testMultiAssignmentCoercions] from typing import Tuple, Any @@ -560,7 +538,6 @@ def from_tuple(t): y, r0 :: int r1, r2 :: object r3 :: int - r4 :: None L0: r0 = t[0] r1 = box(int, r0) @@ -568,8 +545,7 @@ L0: r2 = t[1] r3 = unbox(int, r2) y = r3 - r4 = None - return r4 + return 1 def from_any(a): a :: object x :: int @@ -580,7 +556,6 @@ def from_any(a): r5 :: bool r6 :: object r7 :: bool - r8 :: None L0: r0 = PyObject_GetIter(a) r1 = PyIter_Next(r0) @@ -604,8 +579,7 @@ L5: raise ValueError('too many values to unpack') unreachable L6: - r8 = None - return r8 + return 1 [case testMultiAssignmentNested] from typing import Tuple, Any, List @@ -629,7 +603,6 @@ def multi_assign(t, a, l): r4 :: bool r5 :: object r6 :: int - r7 :: None L0: r0 = t[0] a.x = r0; r1 = is_error @@ -639,8 +612,7 @@ L0: r5 = r2[1] r6 = unbox(int, r5) z = r6 - r7 = None - return r7 + return 1 [case testAssert] from typing import Optional @@ -686,7 +658,6 @@ def complex_msg(x, s): r4 :: object r5 :: str r6, r7 :: object - r8 :: None L0: r0 = builtins.None :: object r1 = x is not r0 @@ -703,8 +674,7 @@ L2: CPy_Raise(r7) unreachable L3: - r8 = None - return r8 + return 1 [case testDelList] def delList() -> None: @@ -719,7 +689,6 @@ def delList(): r2, l :: list r3 :: object r4 :: int32 - r5 :: None L0: r0 = box(short_int, 2) r1 = box(short_int, 4) @@ -727,8 +696,7 @@ L0: l = r2 r3 = box(short_int, 2) r4 = PyObject_DelItem(l, r3) - r5 = None - return r5 + return 1 def delListMultiple(): r0, r1, r2, r3, r4, r5, r6 :: object r7, l :: list @@ -738,7 +706,6 @@ def delListMultiple(): r11 :: int32 r12 :: object r13 :: int32 - r14 :: None L0: r0 = box(short_int, 2) r1 = box(short_int, 4) @@ -755,8 +722,7 @@ L0: r11 = PyObject_DelItem(l, r10) r12 = box(short_int, 6) r13 = PyObject_DelItem(l, r12) - r14 = None - return r14 + return 1 [case testDelDict] def delDict() -> None: @@ -773,7 +739,6 @@ def delDict(): r4, d :: dict r5 :: str r6 :: int32 - r7 :: None L0: r0 = unicode_1 :: static ('one') r1 = unicode_2 :: static ('two') @@ -783,8 +748,7 @@ L0: d = r4 r5 = unicode_1 :: static ('one') r6 = PyObject_DelItem(d, r5) - r7 = None - return r7 + return 1 def delDictMultiple(): r0 :: str r1 :: str @@ -794,7 +758,6 @@ def delDictMultiple(): r8, d :: dict r9, r10 :: str r11, r12 :: int32 - r13 :: None L0: r0 = unicode_1 :: static ('one') r1 = unicode_2 :: static ('two') @@ -810,8 +773,7 @@ L0: r10 = unicode_4 :: static ('four') r11 = PyObject_DelItem(d, r9) r12 = PyObject_DelItem(d, r10) - r13 = None - return r13 + return 1 [case testDelAttribute] class Dummy(): @@ -829,31 +791,26 @@ def Dummy.__init__(self, x, y): self :: __main__.Dummy x, y :: int r0, r1 :: bool - r2 :: None L0: self.x = x; r0 = is_error self.y = y; r1 = is_error - r2 = None - return r2 + return 1 def delAttribute(): r0, dummy :: __main__.Dummy r1 :: str r2 :: int32 - r3 :: None L0: r0 = Dummy(2, 4) dummy = r0 r1 = unicode_3 :: static ('x') r2 = PyObject_DelAttr(dummy, r1) - r3 = None - return r3 + return 1 def delAttributeMultiple(): r0, dummy :: __main__.Dummy r1 :: str r2 :: int32 r3 :: str r4 :: int32 - r5 :: None L0: r0 = Dummy(2, 4) dummy = r0 @@ -861,8 +818,7 @@ L0: r2 = PyObject_DelAttr(dummy, r1) r3 = unicode_4 :: static ('y') r4 = PyObject_DelAttr(dummy, r3) - r5 = None - return r5 + return 1 [case testForEnumerate] from typing import List, Iterable @@ -886,7 +842,6 @@ def f(a): r6 :: object x, r7, r8 :: int r9, r10 :: short_int - r11 :: None L0: r0 = 0 i = 0 @@ -911,8 +866,7 @@ L3: goto L1 L4: L5: - r11 = None - return r11 + return 1 def g(x): x :: object r0 :: short_int @@ -921,7 +875,6 @@ def g(x): r3, n :: int r4 :: short_int r5 :: bool - r6 :: None L0: r0 = 0 i = 0 @@ -940,8 +893,7 @@ L3: L4: r5 = CPy_NoErrOccured() L5: - r6 = None - return r6 + return 1 [case testForZip] from typing import List, Iterable @@ -969,7 +921,6 @@ def f(a, b): r9, y, r10 :: bool r11 :: short_int r12 :: bool - r13 :: None L0: r0 = 0 r1 = PyObject_GetIter(b) @@ -1000,8 +951,7 @@ L6: L7: r12 = CPy_NoErrOccured() L8: - r13 = None - return r13 + return 1 def g(a, b): a :: object b :: list @@ -1015,10 +965,8 @@ def g(a, b): r7, r8, r9, x :: bool r10 :: object y, r11 :: int - r12 :: bool - r13, r14 :: short_int - r15 :: bool - r16 :: None + r12, r13 :: short_int + r14 :: bool L0: r0 = PyObject_GetIter(a) r1 = 0 @@ -1042,18 +990,16 @@ L4: r10 = b[r1] :: unsafe list r11 = unbox(int, r10) y = r11 - r12 = False - x = r12 + x = 0 L5: - r13 = r1 + 2 - r1 = r13 - r14 = r2 + 2 - r2 = r14 - z = r14 + r12 = r1 + 2 + r1 = r12 + r13 = r2 + 2 + r2 = r13 + z = r13 goto L1 L6: - r15 = CPy_NoErrOccured() + r14 = CPy_NoErrOccured() L7: - r16 = None - return r16 + return 1 diff --git a/mypyc/test-data/irbuild-try.test b/mypyc/test-data/irbuild-try.test index 6ac7faeaf3ba..edcb29b06959 100644 --- a/mypyc/test-data/irbuild-try.test +++ b/mypyc/test-data/irbuild-try.test @@ -15,7 +15,6 @@ def g(): r7 :: str r8, r9 :: object r10 :: bool - r11 :: None L0: L1: r0 = builtins :: module @@ -38,8 +37,7 @@ L4: (handler for L2) r10 = keep_propagating unreachable L5: - r11 = None - return r11 + return 1 [case testTryExcept2] def g(b: bool) -> None: @@ -63,7 +61,6 @@ def g(b): r9 :: str r10, r11 :: object r12 :: bool - r13 :: None L0: L1: if b goto L2 else goto L3 :: bool @@ -93,8 +90,7 @@ L7: (handler for L5) r12 = keep_propagating unreachable L8: - r13 = None - return r13 + return 1 [case testTryExcept3] def g() -> None: @@ -131,7 +127,6 @@ def g(): r24 :: str r25, r26 :: object r27 :: bool - r28 :: None L0: L1: r0 = unicode_1 :: static ('a') @@ -188,8 +183,7 @@ L11: (handler for L9) r27 = keep_propagating unreachable L12: - r28 = None - return r28 + return 1 [case testTryExcept4] def g() -> None: @@ -218,7 +212,6 @@ def g(): r16 :: str r17, r18 :: object r19 :: bool - r20 :: None L0: L1: goto L9 @@ -260,8 +253,7 @@ L8: (handler for L2, L3, L4, L5, L6) r19 = keep_propagating unreachable L9: - r20 = None - return r20 + return 1 [case testTryFinally] def a(b: bool) -> None: @@ -283,7 +275,6 @@ def a(b): r10 :: str r11, r12 :: object r13 :: bool - r14 :: None L0: L1: if b goto L2 else goto L3 :: bool @@ -324,8 +315,7 @@ L12: r13 = keep_propagating unreachable L13: - r14 = None - return r14 + return 1 [case testWith] from typing import Any @@ -339,21 +329,19 @@ def foo(x): r3 :: object r4 :: str r5, r6 :: object - r7, r8 :: bool + r7 :: bool y :: object - r9 :: str - r10 :: object - r11 :: str - r12, r13 :: object + r8 :: str + r9 :: object + r10 :: str + r11, r12 :: object + r13 :: tuple[object, object, object] r14 :: tuple[object, object, object] - r15 :: bool - r16 :: tuple[object, object, object] - r17, r18, r19, r20 :: object - r21, r22 :: bool - r23, r24, r25 :: tuple[object, object, object] - r26, r27 :: object - r28 :: bool - r29 :: None + r15, r16, r17, r18 :: object + r19, r20 :: bool + r21, r22, r23 :: tuple[object, object, object] + r24, r25 :: object + r26 :: bool L0: r0 = py_call(x) r1 = PyObject_Type(r0) @@ -362,68 +350,65 @@ L0: r4 = unicode_4 :: static ('__enter__') r5 = CPyObject_GetAttr(r1, r4) r6 = py_call(r5, r0) - r7 = True - r8 = r7 + r7 = 1 L1: L2: y = r6 - r9 = unicode_5 :: static ('hello') - r10 = builtins :: module - r11 = unicode_6 :: static ('print') - r12 = CPyObject_GetAttr(r10, r11) - r13 = py_call(r12, r9) + r8 = unicode_5 :: static ('hello') + r9 = builtins :: module + r10 = unicode_6 :: static ('print') + r11 = CPyObject_GetAttr(r9, r10) + r12 = py_call(r11, r8) goto L8 L3: (handler for L2) - r14 = CPy_CatchError() - r15 = False - r8 = r15 - r16 = CPy_GetExcInfo() - r17 = r16[0] - r18 = r16[1] - r19 = r16[2] - r20 = py_call(r3, r0, r17, r18, r19) - r21 = bool r20 :: object - if r21 goto L5 else goto L4 :: bool + r13 = CPy_CatchError() + r7 = 0 + r14 = CPy_GetExcInfo() + r15 = r14[0] + r16 = r14[1] + r17 = r14[2] + r18 = py_call(r3, r0, r15, r16, r17) + r19 = bool r18 :: object + if r19 goto L5 else goto L4 :: bool L4: CPy_Reraise() unreachable L5: L6: - CPy_RestoreExcInfo(r14) + CPy_RestoreExcInfo(r13) goto L8 L7: (handler for L3, L4, L5) - CPy_RestoreExcInfo(r14) - r22 = keep_propagating + CPy_RestoreExcInfo(r13) + r20 = keep_propagating unreachable L8: L9: L10: - r24 = :: tuple[object, object, object] - r23 = r24 + r22 = :: tuple[object, object, object] + r21 = r22 goto L12 L11: (handler for L1, L6, L7, L8) - r25 = CPy_CatchError() - r23 = r25 + r23 = CPy_CatchError() + r21 = r23 L12: - if r8 goto L13 else goto L14 :: bool + if r7 goto L13 else goto L14 :: bool L13: - r26 = builtins.None :: object - r27 = py_call(r3, r0, r26, r26, r26) + r24 = builtins.None :: object + r25 = py_call(r3, r0, r24, r24, r24) L14: - if is_error(r23) goto L16 else goto L15 + if is_error(r21) goto L16 else goto L15 L15: CPy_Reraise() unreachable L16: goto L20 L17: (handler for L12, L13, L14, L15) - if is_error(r23) goto L19 else goto L18 + if is_error(r21) goto L19 else goto L18 L18: - CPy_RestoreExcInfo(r23) + CPy_RestoreExcInfo(r21) L19: - r28 = keep_propagating + r26 = keep_propagating unreachable L20: - r29 = None - return r29 + return 1 diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index 41c6bedf9d88..34ae5423ba63 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -21,15 +21,13 @@ def f() -> int: return t[1] [out] def f(): - r0 :: bool - r1, t :: tuple[bool, int] - r2 :: int + r0, t :: tuple[bool, int] + r1 :: int L0: - r0 = True - r1 = (r0, 2) - t = r1 - r2 = t[1] - return r2 + r0 = (1, 2) + t = r0 + r1 = t[1] + return r1 [case testTupleLen] from typing import Tuple @@ -132,7 +130,6 @@ def f(xs): r5 :: object x, r6 :: str r7 :: short_int - r8 :: None L0: r0 = 0 L1: @@ -150,8 +147,7 @@ L3: r0 = r7 goto L1 L4: - r8 = None - return r8 + return 1 [case testNamedTupleAttribute] from typing import NamedTuple diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index 20025102e4c8..878acd578c8f 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -367,15 +367,13 @@ def f() -> None: def f(): x :: int r0 :: int - r1 :: None L0: x = 2 r0 = CPyTagged_Add(x, 2) dec_ref x :: int x = r0 dec_ref x :: int - r1 = None - return r1 + return 1 [case testAdd1] def f() -> None: @@ -385,15 +383,13 @@ def f() -> None: def f(): y :: int r0, x :: int - r1 :: None L0: y = 2 r0 = CPyTagged_Add(y, 2) dec_ref y :: int x = r0 dec_ref x :: int - r1 = None - return r1 + return 1 [case testAdd2] def f(a: int) -> int: @@ -438,7 +434,6 @@ def f(a: int) -> None: def f(a): a, r0, x :: int y, r1, z :: int - r2 :: None L0: r0 = CPyTagged_Add(a, a) x = r0 @@ -448,8 +443,7 @@ L0: dec_ref y :: int z = r1 dec_ref z :: int - r2 = None - return r2 + return 1 [case testAdd5] def f(a: int) -> None: @@ -460,7 +454,6 @@ def f(a: int) -> None: def f(a): a, r0 :: int x, r1 :: int - r2 :: None L0: r0 = CPyTagged_Add(a, a) a = r0 @@ -470,8 +463,7 @@ L0: dec_ref x :: int x = r1 dec_ref x :: int - r2 = None - return r2 + return 1 [case testReturnInMiddleOfFunction] def f() -> int: @@ -624,15 +616,13 @@ def f(a, b): r1 :: int r2 :: object r3 :: bool - r4 :: None L0: r0 = CPyList_GetItemShort(b, 0) r1 = unbox(int, r0) dec_ref r0 r2 = box(int, r1) r3 = CPyList_SetItem(a, 0, r2) - r4 = None - return r4 + return 1 [case testTupleRefcount] from typing import Tuple @@ -659,15 +649,13 @@ def f() -> None: def f(): r0, c, r1 :: __main__.C r2 :: bool - r3 :: None L0: r0 = C() c = r0 r1 = C() c.x = r1; r2 = is_error dec_ref c - r3 = None - return r3 + return 1 [case testCastRefCount] class C: pass @@ -681,7 +669,6 @@ def f(): r1, a :: list r2 :: object r3, d :: __main__.C - r4 :: None L0: r0 = C() r1 = [r0] @@ -691,8 +678,7 @@ L0: r3 = cast(__main__.C, r2) d = r3 dec_ref d - r4 = None - return r4 + return 1 [case testUnaryBranchSpecialCase] def f(x: bool) -> int: @@ -757,15 +743,12 @@ def f(a, x): x :: int r0 :: object r1 :: int32 - r2, r3 :: None L0: inc_ref x :: int r0 = box(int, x) r1 = PyList_Append(a, r0) dec_ref r0 - r2 = None - r3 = None - return r3 + return 1 [case testForDict] from typing import Dict @@ -788,7 +771,6 @@ def f(d): r9, r10 :: object r11 :: int r12, r13 :: bool - r14 :: None L0: r0 = 0 r1 = PyDict_Size(d) @@ -818,8 +800,7 @@ L3: L4: r13 = CPy_NoErrOccured() L5: - r14 = None - return r14 + return 1 L6: dec_ref r3 dec_ref r4 @@ -834,25 +815,19 @@ def make_garbage(arg: object) -> None: [out] def make_garbage(arg): arg :: object - r0, b :: bool - r1 :: None - r2 :: object - r3 :: bool - r4 :: None + b :: bool + r0 :: object L0: - r0 = True - b = r0 + b = 1 L1: if b goto L2 else goto L3 :: bool L2: - r1 = None - r2 = box(None, r1) - inc_ref r2 - arg = r2 + r0 = box(None, 1) + inc_ref r0 + arg = r0 dec_ref arg - r3 = False - b = r3 + b = 0 goto L1 L3: - r4 = None - return r4 + return 1 + diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index aa538939146f..1b11b9fc7a58 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -23,7 +23,7 @@ from mypyc.codegen.emit import Emitter, EmitterContext from mypyc.codegen.emitfunc import generate_native_function, FunctionEmitterVisitor from mypyc.primitives.registry import binary_ops, c_binary_ops -from mypyc.primitives.misc_ops import none_object_op, true_op, false_op +from mypyc.primitives.misc_ops import none_object_op from mypyc.primitives.list_ops import ( list_get_item_op, list_set_item_op, new_list_op, list_append_op ) @@ -92,12 +92,6 @@ def test_tuple_get(self) -> None: def test_load_None(self) -> None: self.assert_emit(PrimitiveOp([], none_object_op, 0), "cpy_r_r0 = Py_None;") - def test_load_True(self) -> None: - self.assert_emit(PrimitiveOp([], true_op, 0), "cpy_r_r0 = 1;") - - def test_load_False(self) -> None: - self.assert_emit(PrimitiveOp([], false_op, 0), "cpy_r_r0 = 0;") - def test_assign_int(self) -> None: self.assert_emit(Assign(self.m, self.n), "cpy_r_m = cpy_r_n;") From ce2a2168ce0a8d6d2bec604dbcba595fdaf968e9 Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Thu, 13 Aug 2020 21:45:21 +0800 Subject: [PATCH 118/138] [mypyc] New style new_tuple_op (#9298) This PR supports new-style new_tuple_op, which completes the support of all tuple ops. --- mypyc/irbuild/builder.py | 3 +++ mypyc/irbuild/classdef.py | 10 ++++------ mypyc/irbuild/ll_builder.py | 9 +++++++-- mypyc/primitives/tuple_ops.py | 18 +++++++++--------- mypyc/test-data/irbuild-basic.test | 13 +++++++------ mypyc/test-data/irbuild-classes.test | 8 ++++---- mypyc/test-data/refcount.test | 2 +- 7 files changed, 35 insertions(+), 28 deletions(-) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 2b9c994ae47c..b9e7b9cf7c76 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -250,6 +250,9 @@ def compare_tagged(self, lhs: Value, rhs: Value, op: str, line: int) -> Value: def builtin_len(self, val: Value, line: int) -> Value: return self.builder.builtin_len(val, line) + def new_tuple(self, items: List[Value], line: int) -> Value: + return self.builder.new_tuple(items, line) + @property def environment(self) -> Environment: return self.builder.environment diff --git a/mypyc/irbuild/classdef.py b/mypyc/irbuild/classdef.py index cf88d9896db1..cfce0b369ef7 100644 --- a/mypyc/irbuild/classdef.py +++ b/mypyc/irbuild/classdef.py @@ -22,7 +22,6 @@ not_implemented_op ) from mypyc.primitives.dict_ops import dict_set_item_op, dict_new_op -from mypyc.primitives.tuple_ops import new_tuple_op from mypyc.common import SELF_NAME from mypyc.irbuild.util import ( is_dataclass_decorator, get_func_def, is_dataclass, is_constant, add_self_to_env @@ -165,7 +164,7 @@ def allocate_class(builder: IRBuilder, cdef: ClassDef) -> Value: base_exprs = cdef.base_type_exprs + cdef.removed_base_type_exprs if base_exprs: bases = [builder.accept(x) for x in base_exprs] - tp_bases = builder.primitive_op(new_tuple_op, bases, cdef.line) + tp_bases = builder.new_tuple(bases, cdef.line) else: tp_bases = builder.add(LoadErrorValue(object_rprimitive, is_borrowed=True)) modname = builder.load_static_unicode(builder.module_name) @@ -219,7 +218,7 @@ def populate_non_ext_bases(builder: IRBuilder, cdef: ClassDef) -> Value: base = builder.load_global_str(cls.name, cdef.line) bases.append(base) - return builder.primitive_op(new_tuple_op, bases, cdef.line) + return builder.new_tuple(bases, cdef.line) def find_non_ext_metaclass(builder: IRBuilder, cdef: ClassDef, bases: Value) -> Value: @@ -465,9 +464,8 @@ def create_mypyc_attrs_tuple(builder: IRBuilder, ir: ClassIR, line: int) -> Valu attrs = [name for ancestor in ir.mro for name in ancestor.attributes] if ir.inherits_python: attrs.append('__dict__') - return builder.primitive_op(new_tuple_op, - [builder.load_static_unicode(attr) for attr in attrs], - line) + items = [builder.load_static_unicode(attr) for attr in attrs] + return builder.new_tuple(items, line) def finish_non_ext_dict(builder: IRBuilder, non_ext: NonExtClassInfo, line: int) -> None: diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 0e07092fdb63..293e3df2d720 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -267,7 +267,7 @@ def py_call(self, if len(star_arg_values) == 0: # We can directly construct a tuple if there are no star args. - pos_args_tuple = self.primitive_op(new_tuple_op, pos_arg_values, line) + pos_args_tuple = self.new_tuple(pos_arg_values, line) else: # Otherwise we construct a list and call extend it with the star args, since tuples # don't have an extend method. @@ -338,7 +338,8 @@ def native_args_to_positional(self, for lst, arg in zip(formal_to_actual, sig.args): output_arg = None if arg.kind == ARG_STAR: - output_arg = self.primitive_op(new_tuple_op, [args[i] for i in lst], line) + items = [args[i] for i in lst] + output_arg = self.new_tuple(items, line) elif arg.kind == ARG_STAR2: dict_entries = [(self.load_static_unicode(cast(str, arg_names[i])), args[i]) for i in lst] @@ -844,6 +845,10 @@ def builtin_len(self, val: Value, line: int) -> Value: else: return self.call_c(generic_len_op, [val], line) + def new_tuple(self, items: List[Value], line: int) -> Value: + load_size_op = self.add(LoadInt(len(items), -1, c_pyssize_t_rprimitive)) + return self.call_c(new_tuple_op, [load_size_op] + items, line) + # Internal helpers def decompose_union_helper(self, diff --git a/mypyc/primitives/tuple_ops.py b/mypyc/primitives/tuple_ops.py index 02746e7dd462..edd3d91a70a4 100644 --- a/mypyc/primitives/tuple_ops.py +++ b/mypyc/primitives/tuple_ops.py @@ -5,9 +5,11 @@ """ from mypyc.ir.ops import ERR_MAGIC -from mypyc.ir.rtypes import tuple_rprimitive, int_rprimitive, list_rprimitive, object_rprimitive +from mypyc.ir.rtypes import ( + tuple_rprimitive, int_rprimitive, list_rprimitive, object_rprimitive, c_pyssize_t_rprimitive +) from mypyc.primitives.registry import ( - c_method_op, custom_op, simple_emit, c_function_op + c_method_op, c_function_op, c_custom_op ) @@ -20,14 +22,12 @@ error_kind=ERR_MAGIC) # Construct a boxed tuple from items: (item1, item2, ...) -new_tuple_op = custom_op( - arg_types=[object_rprimitive], - result_type=tuple_rprimitive, - is_var_arg=True, +new_tuple_op = c_custom_op( + arg_types=[c_pyssize_t_rprimitive], + return_type=tuple_rprimitive, + c_function_name='PyTuple_Pack', error_kind=ERR_MAGIC, - steals=False, - format_str='{dest} = ({comma_args}) :: tuple', - emit=simple_emit('{dest} = PyTuple_Pack({num_args}{comma_if_args}{comma_args});')) + var_arg_type=object_rprimitive) # Construct tuple from a list. list_tuple_op = c_function_op( diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index a6691ac610e6..4d16f5d2c527 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -1258,7 +1258,7 @@ def call_python_function_with_keyword_arg(x): L0: r0 = load_address PyLong_Type r1 = unicode_3 :: static ('base') - r2 = (x) :: tuple + r2 = PyTuple_Pack(1, x) r3 = box(short_int, 4) r4 = CPyDict_Build(1, r1, r3) r5 = py_call_with_kwargs(r0, r2, r4) @@ -1287,7 +1287,7 @@ L0: r1 = CPyObject_GetAttr(xs, r0) r2 = unicode_5 :: static ('x') r3 = box(short_int, 0) - r4 = (r3) :: tuple + r4 = PyTuple_Pack(1, r3) r5 = box(int, first) r6 = CPyDict_Build(1, r2, r5) r7 = py_call_with_kwargs(r1, r4, r6) @@ -1295,7 +1295,7 @@ L0: r9 = CPyObject_GetAttr(xs, r8) r10 = unicode_5 :: static ('x') r11 = unicode_6 :: static ('i') - r12 = () :: tuple + r12 = PyTuple_Pack(0) r13 = box(int, second) r14 = box(short_int, 2) r15 = CPyDict_Build(2, r10, r13, r11, r14) @@ -1828,7 +1828,7 @@ L0: r7 = __main__.globals :: static r8 = unicode_6 :: static ('f') r9 = CPyDict_GetItem(r7, r8) - r10 = () :: tuple + r10 = PyTuple_Pack(0) r11 = PyDict_New() r12 = CPyDict_UpdateInDisplay(r11, r6) r13 = py_call_with_kwargs(r9, r10, r11) @@ -1840,7 +1840,8 @@ def h(): r2, r3 :: object r4, r5 :: dict r6 :: str - r7, r8 :: object + r7 :: object + r8 :: object r9 :: tuple r10 :: dict r11 :: int32 @@ -1856,7 +1857,7 @@ L0: r6 = unicode_6 :: static ('f') r7 = CPyDict_GetItem(r5, r6) r8 = box(short_int, 2) - r9 = (r8) :: tuple + r9 = PyTuple_Pack(1, r8) r10 = PyDict_New() r11 = CPyDict_UpdateInDisplay(r10, r4) r12 = py_call_with_kwargs(r7, r9, r10) diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index d2cf53e90c78..fd67f9246318 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -410,7 +410,7 @@ L6: r42 = pytype_from_template(r41, r39, r40) r43 = C_trait_vtable_setup() r44 = unicode_8 :: static ('__mypyc_attrs__') - r45 = () :: tuple + r45 = PyTuple_Pack(0) r46 = PyObject_SetAttr(r42, r44, r45) __main__.C = r42 :: type r47 = __main__.globals :: static @@ -421,7 +421,7 @@ L6: r52 = __main__.S_template :: type r53 = pytype_from_template(r52, r50, r51) r54 = unicode_8 :: static ('__mypyc_attrs__') - r55 = () :: tuple + r55 = PyTuple_Pack(0) r56 = PyObject_SetAttr(r53, r54, r55) __main__.S = r53 :: type r57 = __main__.globals :: static @@ -436,14 +436,14 @@ L6: r66 = unicode_6 :: static ('T') r67 = CPyDict_GetItem(r65, r66) r68 = PyObject_GetItem(r64, r67) - r69 = (r60, r61, r68) :: tuple + r69 = PyTuple_Pack(3, r60, r61, r68) r70 = unicode_7 :: static ('__main__') r71 = __main__.D_template :: type r72 = pytype_from_template(r71, r69, r70) r73 = D_trait_vtable_setup() r74 = unicode_8 :: static ('__mypyc_attrs__') r75 = unicode_11 :: static ('__dict__') - r76 = (r75) :: tuple + r76 = PyTuple_Pack(1, r75) r77 = PyObject_SetAttr(r72, r74, r76) __main__.D = r72 :: type r78 = __main__.globals :: static diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index 878acd578c8f..ec70afc6bb42 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -722,7 +722,7 @@ def g(x): L0: r0 = load_address PyLong_Type r1 = unicode_1 :: static ('base') - r2 = (x) :: tuple + r2 = PyTuple_Pack(1, x) r3 = box(short_int, 4) r4 = CPyDict_Build(1, r1, r3) dec_ref r3 From 049a879504273e1f37d530af0abebca4174045fa Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 13 Aug 2020 15:39:51 +0100 Subject: [PATCH 119/138] [mypyc] Don't free target of LoadMem too early (#9299) Add optional reference to the object from which we are reading from to `LoadMem` so that the object won't be freed before we read memory. Fixes mypyc/mypyc#756. --- mypyc/ir/ops.py | 29 ++++++++++++++++++++----- mypyc/irbuild/ll_builder.py | 4 ++-- mypyc/test-data/irbuild-basic.test | 10 ++++----- mypyc/test-data/irbuild-lists.test | 5 ++--- mypyc/test-data/irbuild-set.test | 3 +-- mypyc/test-data/irbuild-statements.test | 9 ++++---- mypyc/test-data/irbuild-tuple.test | 5 ++--- mypyc/test-data/refcount.test | 20 +++++++++++++++++ mypyc/test/test_emitfunc.py | 4 +++- 9 files changed, 62 insertions(+), 27 deletions(-) diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index aecf224f8f9c..e8d94d5fda33 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -1348,25 +1348,42 @@ def accept(self, visitor: 'OpVisitor[T]') -> T: class LoadMem(RegisterOp): - """Reading a memory location - - type ret = *(type*)src + """Read a memory location. + + type ret = *(type *)src + + Attributes: + type: Type of the read value + src: Pointer to memory to read + base: If not None, the object from which we are reading memory. + It's used to avoid the target object from being freed via + reference counting. If the target is not in reference counted + memory, or we know that the target won't be freed, it can be + None. """ error_kind = ERR_NEVER - def __init__(self, type: RType, src: Value, line: int = -1) -> None: + def __init__(self, type: RType, src: Value, base: Optional[Value], line: int = -1) -> None: super().__init__(line) self.type = type # TODO: for now we enforce that the src memory address should be Py_ssize_t # later we should also support same width unsigned int assert is_pointer_rprimitive(src.type) self.src = src + self.base = base def sources(self) -> List[Value]: - return [self.src] + if self.base: + return [self.src, self.base] + else: + return [self.src] def to_str(self, env: Environment) -> str: - return env.format("%r = load_mem %r :: %r*", self, self.src, self.type) + if self.base: + base = env.format(', %r', self.base) + else: + base = '' + return env.format("%r = load_mem %r%s :: %r*", self, self.src, base, self.type) def accept(self, visitor: 'OpVisitor[T]') -> T: return visitor.visit_load_mem(self) diff --git a/mypyc/irbuild/ll_builder.py b/mypyc/irbuild/ll_builder.py index 293e3df2d720..68fa357a52ec 100644 --- a/mypyc/irbuild/ll_builder.py +++ b/mypyc/irbuild/ll_builder.py @@ -826,7 +826,7 @@ def builtin_len(self, val: Value, line: int) -> Value: typ = val.type if is_list_rprimitive(typ) or is_tuple_rprimitive(typ): elem_address = self.add(GetElementPtr(val, PyVarObject, 'ob_size')) - size_value = self.add(LoadMem(c_pyssize_t_rprimitive, elem_address)) + size_value = self.add(LoadMem(c_pyssize_t_rprimitive, elem_address, val)) offset = self.add(LoadInt(1, line, rtype=c_pyssize_t_rprimitive)) return self.binary_int_op(short_int_rprimitive, size_value, offset, BinaryIntOp.LEFT_SHIFT, line) @@ -837,7 +837,7 @@ def builtin_len(self, val: Value, line: int) -> Value: BinaryIntOp.LEFT_SHIFT, line) elif is_set_rprimitive(typ): elem_address = self.add(GetElementPtr(val, PySetObject, 'used')) - size_value = self.add(LoadMem(c_pyssize_t_rprimitive, elem_address)) + size_value = self.add(LoadMem(c_pyssize_t_rprimitive, elem_address, val)) offset = self.add(LoadInt(1, line, rtype=c_pyssize_t_rprimitive)) return self.binary_int_op(short_int_rprimitive, size_value, offset, BinaryIntOp.LEFT_SHIFT, line) diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 4d16f5d2c527..bcaf44b8aabe 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -1368,7 +1368,7 @@ def lst(x): r3 :: bool L0: r0 = get_element_ptr x ob_size :: PyVarObject - r1 = load_mem r0 :: native_int* + r1 = load_mem r0, x :: native_int* r2 = r1 << 1 r3 = r2 != 0 if r3 goto L1 else goto L2 :: bool @@ -1980,7 +1980,7 @@ L0: r5 = 0 L1: r6 = get_element_ptr r4 ob_size :: PyVarObject - r7 = load_mem r6 :: native_int* + r7 = load_mem r6, r4 :: native_int* r8 = r7 << 1 r9 = r5 < r8 :: signed if r9 goto L2 else goto L14 :: bool @@ -2065,7 +2065,7 @@ L0: r5 = 0 L1: r6 = get_element_ptr r4 ob_size :: PyVarObject - r7 = load_mem r6 :: native_int* + r7 = load_mem r6, r4 :: native_int* r8 = r7 << 1 r9 = r5 < r8 :: signed if r9 goto L2 else goto L14 :: bool @@ -2152,7 +2152,7 @@ L0: r0 = 0 L1: r1 = get_element_ptr l ob_size :: PyVarObject - r2 = load_mem r1 :: native_int* + r2 = load_mem r1, l :: native_int* r3 = r2 << 1 r4 = r0 < r3 :: signed if r4 goto L2 else goto L4 :: bool @@ -2174,7 +2174,7 @@ L4: r12 = 0 L5: r13 = get_element_ptr l ob_size :: PyVarObject - r14 = load_mem r13 :: native_int* + r14 = load_mem r13, l :: native_int* r15 = r14 << 1 r16 = r12 < r15 :: signed if r16 goto L6 else goto L8 :: bool diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test index dac0091d1705..8b276c066cf8 100644 --- a/mypyc/test-data/irbuild-lists.test +++ b/mypyc/test-data/irbuild-lists.test @@ -117,7 +117,7 @@ def f(a): r2 :: short_int L0: r0 = get_element_ptr a ob_size :: PyVarObject - r1 = load_mem r0 :: native_int* + r1 = load_mem r0, a :: native_int* r2 = r1 << 1 return r2 @@ -156,7 +156,7 @@ def increment(l): r9 :: short_int L0: r0 = get_element_ptr l ob_size :: PyVarObject - r1 = load_mem r0 :: native_int* + r1 = load_mem r0, l :: native_int* r2 = r1 << 1 r3 = 0 i = r3 @@ -196,4 +196,3 @@ L0: r5 = box(short_int, 6) r6 = PyList_Append(r2, r5) return r2 - diff --git a/mypyc/test-data/irbuild-set.test b/mypyc/test-data/irbuild-set.test index 6c5214521944..3003b2948e6c 100644 --- a/mypyc/test-data/irbuild-set.test +++ b/mypyc/test-data/irbuild-set.test @@ -69,7 +69,7 @@ L0: r5 = box(short_int, 6) r6 = PySet_Add(r0, r5) r7 = get_element_ptr r0 used :: PySetObject - r8 = load_mem r7 :: native_int* + r8 = load_mem r7, r0 :: native_int* r9 = r8 << 1 return r9 @@ -223,4 +223,3 @@ L0: r7 = box(short_int, 6) r8 = PySet_Add(r0, r7) return r0 - diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index 5ed1c1e12e3d..045d1a959633 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -323,7 +323,7 @@ L0: r0 = 0 L1: r1 = get_element_ptr ls ob_size :: PyVarObject - r2 = load_mem r1 :: native_int* + r2 = load_mem r1, ls :: native_int* r3 = r2 << 1 r4 = r0 < r3 :: signed if r4 goto L2 else goto L4 :: bool @@ -848,7 +848,7 @@ L0: r1 = 0 L1: r2 = get_element_ptr a ob_size :: PyVarObject - r3 = load_mem r2 :: native_int* + r3 = load_mem r2, a :: native_int* r4 = r3 << 1 r5 = r1 < r4 :: signed if r5 goto L2 else goto L4 :: bool @@ -926,7 +926,7 @@ L0: r1 = PyObject_GetIter(b) L1: r2 = get_element_ptr a ob_size :: PyVarObject - r3 = load_mem r2 :: native_int* + r3 = load_mem r2, a :: native_int* r4 = r3 << 1 r5 = r0 < r4 :: signed if r5 goto L2 else goto L7 :: bool @@ -977,7 +977,7 @@ L1: if is_error(r3) goto L6 else goto L2 L2: r4 = get_element_ptr b ob_size :: PyVarObject - r5 = load_mem r4 :: native_int* + r5 = load_mem r4, b :: native_int* r6 = r5 << 1 r7 = r1 < r6 :: signed if r7 goto L3 else goto L6 :: bool @@ -1002,4 +1002,3 @@ L6: r14 = CPy_NoErrOccured() L7: return 1 - diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index 34ae5423ba63..04298b0d6c6d 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -67,7 +67,7 @@ def f(x): r2 :: short_int L0: r0 = get_element_ptr x ob_size :: PyVarObject - r1 = load_mem r0 :: native_int* + r1 = load_mem r0, x :: native_int* r2 = r1 << 1 return r2 @@ -134,7 +134,7 @@ L0: r0 = 0 L1: r1 = get_element_ptr xs ob_size :: PyVarObject - r2 = load_mem r1 :: native_int* + r2 = load_mem r1, xs :: native_int* r3 = r2 << 1 r4 = r0 < r3 :: signed if r4 goto L2 else goto L4 :: bool @@ -176,4 +176,3 @@ L2: r2 = CPySequenceTuple_GetItem(nt, 2) r3 = unbox(int, r2) return r3 - diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index ec70afc6bb42..ef17040dfba1 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -831,3 +831,23 @@ L2: L3: return 1 +[case testGetElementPtrLifeTime] +from typing import List + +def f() -> int: + x: List[str] = [] + return len(x) +[out] +def f(): + r0, x :: list + r1 :: ptr + r2 :: native_int + r3 :: short_int +L0: + r0 = [] + x = r0 + r1 = get_element_ptr x ob_size :: PyVarObject + r2 = load_mem r1, x :: native_int* + dec_ref x + r3 = r2 << 1 + return r3 diff --git a/mypyc/test/test_emitfunc.py b/mypyc/test/test_emitfunc.py index 1b11b9fc7a58..a58e23b9f903 100644 --- a/mypyc/test/test_emitfunc.py +++ b/mypyc/test/test_emitfunc.py @@ -262,8 +262,10 @@ def test_binary_int_op(self) -> None: """cpy_r_r04 = (uint64_t)cpy_r_i64 < (uint64_t)cpy_r_i64_1;""") def test_load_mem(self) -> None: - self.assert_emit(LoadMem(bool_rprimitive, self.ptr), + self.assert_emit(LoadMem(bool_rprimitive, self.ptr, None), """cpy_r_r0 = *(char *)cpy_r_ptr;""") + self.assert_emit(LoadMem(bool_rprimitive, self.ptr, self.s1), + """cpy_r_r00 = *(char *)cpy_r_ptr;""") def test_get_element_ptr(self) -> None: r = RStruct("Foo", ["b", "i32", "i64"], [bool_rprimitive, From bdc5afb01a6df77be1dd569145f8514b5bdc895d Mon Sep 17 00:00:00 2001 From: Xuanda Yang Date: Fri, 14 Aug 2020 00:53:50 +0800 Subject: [PATCH 120/138] [mypyc] Fix to_lines to show same type registers on the same line (#9300) This PR fixes Environment.to_lines so that continuous registers with the same type are shown on the same line. --- mypyc/ir/ops.py | 11 ++--- mypyc/test-data/analysis.test | 32 ++++--------- mypyc/test-data/exceptions.test | 7 +-- mypyc/test-data/irbuild-basic.test | 61 ++++++++----------------- mypyc/test-data/irbuild-classes.test | 24 ++++------ mypyc/test-data/irbuild-dict.test | 6 +-- mypyc/test-data/irbuild-generics.test | 3 +- mypyc/test-data/irbuild-lists.test | 6 +-- mypyc/test-data/irbuild-nested.test | 9 ++-- mypyc/test-data/irbuild-optional.test | 6 +-- mypyc/test-data/irbuild-set.test | 3 +- mypyc/test-data/irbuild-statements.test | 20 +++----- mypyc/test-data/irbuild-try.test | 3 +- mypyc/test-data/irbuild-tuple.test | 6 +-- mypyc/test-data/refcount.test | 55 +++++++--------------- 15 files changed, 80 insertions(+), 172 deletions(-) diff --git a/mypyc/ir/ops.py b/mypyc/ir/ops.py index e8d94d5fda33..1b980306a63f 100644 --- a/mypyc/ir/ops.py +++ b/mypyc/ir/ops.py @@ -243,18 +243,13 @@ def to_lines(self, const_regs: Optional[Dict[str, int]] = None) -> List[str]: regs = list(self.regs()) if const_regs is None: const_regs = {} + regs = [reg for reg in regs if reg.name not in const_regs] while i < len(regs): i0 = i - if regs[i0].name not in const_regs: - group = [regs[i0].name] - else: - group = [] - i += 1 - continue + group = [regs[i0].name] while i + 1 < len(regs) and regs[i + 1].type == regs[i0].type: i += 1 - if regs[i].name not in const_regs: - group.append(regs[i].name) + group.append(regs[i].name) i += 1 result.append('%s :: %s' % (', '.join(group), regs[i0].type)) return result diff --git a/mypyc/test-data/analysis.test b/mypyc/test-data/analysis.test index 5b36c03f596f..8658c045bfa7 100644 --- a/mypyc/test-data/analysis.test +++ b/mypyc/test-data/analysis.test @@ -9,13 +9,11 @@ def f(a: int) -> None: z = 1 [out] def f(a): - a :: int - x :: int + a, x :: int r0 :: bool r1 :: native_int r2, r3, r4 :: bool - y :: int - z :: int + y, z :: int L0: x = 2 r1 = x & 1 @@ -69,8 +67,7 @@ def f(a: int) -> int: return x [out] def f(a): - a :: int - x :: int + a, x :: int r0 :: bool r1 :: native_int r2, r3, r4 :: bool @@ -121,8 +118,7 @@ def f() -> int: return x [out] def f(): - x :: int - y :: int + x, y :: int L0: x = 2 y = 2 @@ -171,8 +167,7 @@ def f(a): r0 :: bool r1 :: native_int r2, r3, r4 :: bool - y :: int - x :: int + y, x :: int L0: r1 = a & 1 r2 = r1 == 0 @@ -304,15 +299,12 @@ def f(n: int) -> None: n = x [out] def f(n): - n :: int - x :: int - y :: int + n, x, y :: int r0 :: bool r1 :: native_int r2 :: bool r3 :: native_int - r4, r5, r6, r7 :: bool - r8 :: bool + r4, r5, r6, r7, r8 :: bool r9 :: native_int r10 :: bool r11 :: native_int @@ -419,8 +411,7 @@ def f(x: int) -> int: return f(a) + a [out] def f(x): - x :: int - r0, a, r1, r2, r3 :: int + x, r0, a, r1, r2, r3 :: int L0: r0 = f(2) if is_error(r0) goto L3 (error at f:2) else goto L1 @@ -644,16 +635,13 @@ def f(a: int) -> int: return sum [out] def f(a): - a :: int - sum :: int - i :: int + a, sum, i :: int r0 :: bool r1 :: native_int r2 :: bool r3 :: native_int r4, r5, r6, r7, r8 :: bool - r9 :: int - r10 :: int + r9, r10 :: int L0: sum = 0 i = 0 diff --git a/mypyc/test-data/exceptions.test b/mypyc/test-data/exceptions.test index 125b57fc7a6c..dba4a2accab1 100644 --- a/mypyc/test-data/exceptions.test +++ b/mypyc/test-data/exceptions.test @@ -111,17 +111,14 @@ def sum(a: List[int], l: int) -> int: [out] def sum(a, l): a :: list - l :: int - sum :: int - i :: int + l, sum, i :: int r0 :: bool r1 :: native_int r2 :: bool r3 :: native_int r4, r5, r6, r7 :: bool r8 :: object - r9, r10 :: int - r11, r12 :: int + r9, r10, r11, r12 :: int L0: sum = 0 i = 0 diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index bcaf44b8aabe..55ef3e6641ec 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -51,8 +51,7 @@ def f(x: int) -> None: return [out] def f(x): - x :: int - y :: int + x, y :: int L0: y = 2 y = x @@ -63,8 +62,7 @@ def f(x: int, y: int) -> int: return x * (y + 1) [out] def f(x, y): - x, y :: int - r0, r1 :: int + x, y, r0, r1 :: int L0: r0 = CPyTagged_Add(y, 2) r1 = CPyTagged_Multiply(x, r0) @@ -541,8 +539,7 @@ def f(n): r2 :: bool r3 :: native_int r4, r5, r6, r7, r8 :: bool - r9, r10 :: int - r11, r12, r13 :: int + r9, r10, r11, r12, r13 :: int L0: r1 = n & 1 r2 = r1 == 0 @@ -651,8 +648,7 @@ def f(n: int) -> int: return -1 [out] def f(n): - n :: int - r0 :: int + n, r0 :: int L0: r0 = CPyTagged_Negate(2) return r0 @@ -695,8 +691,7 @@ def f() -> int: return x [out] def f(): - x :: int - r0 :: int + x, r0 :: int L0: x = 0 r0 = CPyTagged_Add(x, 2) @@ -796,8 +791,7 @@ def f(x): x :: int r0 :: object r1 :: str - r2 :: object - r3, r4 :: object + r2, r3, r4 :: object L0: r0 = builtins :: module r1 = unicode_1 :: static ('print') @@ -906,14 +900,12 @@ def g(y: object) -> object: return 3 [out] def g(y): - y :: object - r0, r1 :: object + y, r0, r1 :: object r2, a :: list r3 :: tuple[int, int] r4 :: object r5 :: bool - r6 :: object - r7 :: object + r6, r7 :: object L0: r0 = box(short_int, 2) r1 = g(r0) @@ -937,8 +929,7 @@ def f(a: A, o: object) -> None: [out] def f(a, o): a :: __main__.A - o :: object - r0 :: object + o, r0 :: object r1 :: bool r2 :: int r3 :: object @@ -1122,8 +1113,7 @@ def big_int() -> None: max_31_bit = 1073741823 [out] def big_int(): - r0, a_62_bit, r1, max_62_bit, r2, b_63_bit, r3, c_63_bit, r4, max_63_bit, r5, d_64_bit, r6, max_32_bit :: int - max_31_bit :: int + r0, a_62_bit, r1, max_62_bit, r2, b_63_bit, r3, c_63_bit, r4, max_63_bit, r5, d_64_bit, r6, max_32_bit, max_31_bit :: int L0: r0 = int_1 :: static (4611686018427387902) a_62_bit = r0 @@ -1580,8 +1570,7 @@ def f(x: str) -> int: ... def f(): r0 :: object r1 :: str - r2 :: object - r3, r4 :: object + r2, r3, r4 :: object r5 :: str L0: r0 = m :: module @@ -1805,9 +1794,7 @@ L0: r0 = (a, b, c) return r0 def g(): - r0 :: str - r1 :: str - r2 :: str + r0, r1, r2 :: str r3, r4, r5 :: object r6, r7 :: dict r8 :: str @@ -1835,13 +1822,11 @@ L0: r14 = unbox(tuple[int, int, int], r13) return r14 def h(): - r0 :: str - r1 :: str + r0, r1 :: str r2, r3 :: object r4, r5 :: dict r6 :: str - r7 :: object - r8 :: object + r7, r8 :: object r9 :: tuple r10 :: dict r11 :: int32 @@ -1874,8 +1859,7 @@ def g() -> None: [out] def f(x, y, z): x, y :: int - z :: str - r0 :: str + z, r0 :: str L0: if is_error(y) goto L1 else goto L2 L1: @@ -1914,8 +1898,7 @@ def g() -> None: def A.f(self, x, y, z): self :: __main__.A x, y :: int - z :: str - r0 :: str + z, r0 :: str L0: if is_error(y) goto L1 else goto L2 L1: @@ -1963,8 +1946,7 @@ def f(): x, r11 :: int r12 :: bool r13 :: native_int - r14, r15, r16, r17 :: bool - r18 :: bool + r14, r15, r16, r17, r18 :: bool r19 :: native_int r20, r21, r22, r23 :: bool r24 :: int @@ -2048,8 +2030,7 @@ def f(): x, r11 :: int r12 :: bool r13 :: native_int - r14, r15, r16, r17 :: bool - r18 :: bool + r14, r15, r16, r17, r18 :: bool r19 :: native_int r20, r21, r22, r23 :: bool r24 :: int @@ -2310,8 +2291,7 @@ L0: return r1 def BaseProperty.next(self): self :: __main__.BaseProperty - r0 :: int - r1 :: int + r0, r1 :: int r2 :: __main__.BaseProperty L0: r0 = self._incrementer @@ -3354,8 +3334,7 @@ def f(a: bool) -> int: [out] def C.__mypyc_defaults_setup(__mypyc_self__): __mypyc_self__ :: __main__.C - r0 :: bool - r1 :: bool + r0, r1 :: bool L0: __mypyc_self__.x = 2; r0 = is_error __mypyc_self__.y = 4; r1 = is_error diff --git a/mypyc/test-data/irbuild-classes.test b/mypyc/test-data/irbuild-classes.test index fd67f9246318..603ed614bfec 100644 --- a/mypyc/test-data/irbuild-classes.test +++ b/mypyc/test-data/irbuild-classes.test @@ -43,8 +43,7 @@ def f(): r2, a :: list r3 :: object r4, d :: __main__.C - r5 :: int - r6 :: int + r5, r6 :: int L0: r0 = C() c = r0 @@ -147,8 +146,7 @@ L0: return 1 def B.__init__(self): self :: __main__.B - r0 :: bool - r1 :: bool + r0, r1 :: bool L0: self.x = 40; r0 = is_error self.y = 60; r1 = is_error @@ -171,8 +169,7 @@ L0: return 1 def increment(o): o :: __main__.O - r0 :: int - r1 :: int + r0, r1 :: int r2 :: bool L0: r0 = o.x @@ -656,15 +653,13 @@ def lol() -> int: return C.foo(1) + C.bar(2) [out] def C.foo(x): - x :: int - r0 :: int + x, r0 :: int L0: r0 = CPyTagged_Add(20, x) return r0 def C.bar(cls, x): cls :: object - x :: int - r0 :: int + x, r0 :: int L0: r0 = CPyTagged_Add(20, x) return r0 @@ -793,8 +788,7 @@ def fOpt2(a: Derived, b: Derived) -> bool: [out] def Base.__eq__(self, other): self :: __main__.Base - other :: object - r0 :: object + other, r0 :: object L0: r0 = box(bool, 0) return r0 @@ -816,8 +810,7 @@ L2: return r1 def Derived.__eq__(self, other): self :: __main__.Derived - other :: object - r0 :: object + other, r0 :: object L0: r0 = box(bool, 1) return r0 @@ -910,8 +903,7 @@ L0: return r2 def Derived.__eq__(self, other): self :: __main__.Derived - other :: object - r0 :: object + other, r0 :: object L0: r0 = box(bool, 1) return r0 diff --git a/mypyc/test-data/irbuild-dict.test b/mypyc/test-data/irbuild-dict.test index 2ba45c4143ee..6dfb502ed654 100644 --- a/mypyc/test-data/irbuild-dict.test +++ b/mypyc/test-data/irbuild-dict.test @@ -138,8 +138,7 @@ def increment(d): r6 :: bool r7 :: object k, r8 :: str - r9 :: object - r10, r11 :: object + r9, r10, r11 :: object r12 :: int32 r13, r14 :: bool L0: @@ -214,8 +213,7 @@ def print_dict_methods(d1, d2): v, r8 :: int r9 :: object r10 :: int32 - r11 :: bool - r12, r13 :: bool + r11, r12, r13 :: bool r14 :: short_int r15 :: native_int r16 :: short_int diff --git a/mypyc/test-data/irbuild-generics.test b/mypyc/test-data/irbuild-generics.test index 855c031e55d2..d412d22bacc2 100644 --- a/mypyc/test-data/irbuild-generics.test +++ b/mypyc/test-data/irbuild-generics.test @@ -101,8 +101,7 @@ L0: def f(x): x :: __main__.C r0 :: object - r1, y :: int - r2 :: int + r1, y, r2 :: int r3 :: object r4 :: None r5 :: object diff --git a/mypyc/test-data/irbuild-lists.test b/mypyc/test-data/irbuild-lists.test index 8b276c066cf8..793623709af2 100644 --- a/mypyc/test-data/irbuild-lists.test +++ b/mypyc/test-data/irbuild-lists.test @@ -92,8 +92,7 @@ def f(a: List[int]) -> None: b = 3 * [4] [out] def f(a): - a :: list - r0, b :: list + a, r0, b :: list r1 :: object r2, r3 :: list L0: @@ -150,8 +149,7 @@ def increment(l): r2, r3 :: short_int i :: int r4 :: bool - r5 :: object - r6, r7 :: object + r5, r6, r7 :: object r8 :: bool r9 :: short_int L0: diff --git a/mypyc/test-data/irbuild-nested.test b/mypyc/test-data/irbuild-nested.test index 0676006074ff..38effe9af1b7 100644 --- a/mypyc/test-data/irbuild-nested.test +++ b/mypyc/test-data/irbuild-nested.test @@ -50,8 +50,7 @@ L2: def inner_a_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.inner_a_obj r0 :: __main__.a_env - r1, inner :: object - r2 :: object + r1, inner, r2 :: object L0: r0 = __mypyc_self__.__mypyc_env__ r1 = r0.inner @@ -494,8 +493,7 @@ def b_a_obj.__call__(__mypyc_self__): r1, b :: object r2 :: __main__.b_a_env r3 :: bool - r4 :: int - r5 :: int + r4, r5 :: int r6 :: bool r7 :: __main__.c_a_b_obj r8, r9 :: bool @@ -655,8 +653,7 @@ def foo_f_obj.__call__(__mypyc_self__): __mypyc_self__ :: __main__.foo_f_obj r0 :: __main__.f_env r1, foo :: object - r2 :: int - r3 :: int + r2, r3 :: int L0: r0 = __mypyc_self__.__mypyc_env__ r1 = r0.foo diff --git a/mypyc/test-data/irbuild-optional.test b/mypyc/test-data/irbuild-optional.test index 431b8f473d25..4345fe2858f8 100644 --- a/mypyc/test-data/irbuild-optional.test +++ b/mypyc/test-data/irbuild-optional.test @@ -216,8 +216,7 @@ def f(y): r1 :: bool r2 :: native_int r3, r4, r5 :: bool - r6 :: object - r7 :: object + r6, r7 :: object r8, r9 :: bool r10 :: int L0: @@ -266,8 +265,7 @@ def f(x): r0 :: object r1 :: int32 r2 :: bool - r3 :: int - r4 :: int + r3, r4 :: int r5 :: __main__.A r6 :: int L0: diff --git a/mypyc/test-data/irbuild-set.test b/mypyc/test-data/irbuild-set.test index 3003b2948e6c..b442c36309f5 100644 --- a/mypyc/test-data/irbuild-set.test +++ b/mypyc/test-data/irbuild-set.test @@ -204,8 +204,7 @@ def f(x: Set[int], y: Set[int]) -> Set[int]: return {1, 2, *x, *y, 3} [out] def f(x, y): - x, y :: set - r0 :: set + x, y, r0 :: set r1 :: object r2 :: int32 r3 :: object diff --git a/mypyc/test-data/irbuild-statements.test b/mypyc/test-data/irbuild-statements.test index 045d1a959633..8ea13df08388 100644 --- a/mypyc/test-data/irbuild-statements.test +++ b/mypyc/test-data/irbuild-statements.test @@ -129,8 +129,7 @@ def f(): r1 :: native_int r2 :: bool r3 :: native_int - r4, r5, r6, r7 :: bool - r8 :: bool + r4, r5, r6, r7, r8 :: bool r9 :: native_int r10 :: bool r11 :: native_int @@ -250,8 +249,7 @@ def f(): r1 :: native_int r2 :: bool r3 :: native_int - r4, r5, r6, r7 :: bool - r8 :: bool + r4, r5, r6, r7, r8 :: bool r9 :: native_int r10 :: bool r11 :: native_int @@ -411,8 +409,7 @@ def sum_over_even_values(d): r7 :: object key, r8 :: int r9, r10 :: object - r11 :: int - r12 :: int + r11, r12 :: int r13 :: bool r14 :: native_int r15, r16, r17, r18 :: bool @@ -595,8 +592,7 @@ def multi_assign(t, a, l): t :: tuple[int, tuple[str, object]] a :: __main__.A l :: list - z :: int - r0 :: int + z, r0 :: int r1 :: bool r2 :: tuple[str, object] r3 :: str @@ -733,8 +729,7 @@ def delDictMultiple() -> None: del d["one"], d["four"] [out] def delDict(): - r0 :: str - r1 :: str + r0, r1 :: str r2, r3 :: object r4, d :: dict r5 :: str @@ -750,10 +745,7 @@ L0: r6 = PyObject_DelItem(d, r5) return 1 def delDictMultiple(): - r0 :: str - r1 :: str - r2 :: str - r3 :: str + r0, r1, r2, r3 :: str r4, r5, r6, r7 :: object r8, d :: dict r9, r10 :: str diff --git a/mypyc/test-data/irbuild-try.test b/mypyc/test-data/irbuild-try.test index edcb29b06959..549fbcfa5488 100644 --- a/mypyc/test-data/irbuild-try.test +++ b/mypyc/test-data/irbuild-try.test @@ -335,8 +335,7 @@ def foo(x): r9 :: object r10 :: str r11, r12 :: object - r13 :: tuple[object, object, object] - r14 :: tuple[object, object, object] + r13, r14 :: tuple[object, object, object] r15, r16, r17, r18 :: object r19, r20 :: bool r21, r22, r23 :: tuple[object, object, object] diff --git a/mypyc/test-data/irbuild-tuple.test b/mypyc/test-data/irbuild-tuple.test index 04298b0d6c6d..9c38f8cb8015 100644 --- a/mypyc/test-data/irbuild-tuple.test +++ b/mypyc/test-data/irbuild-tuple.test @@ -80,8 +80,7 @@ def f() -> int: def f(): r0 :: tuple[int, int] t :: tuple - r1 :: object - r2 :: object + r1, r2 :: object r3 :: int L0: r0 = (2, 4) @@ -97,8 +96,7 @@ def f(x: Sequence[int], y: Sequence[int]) -> Tuple[int, ...]: return (1, 2, *x, *y, 3) [out] def f(x, y): - x, y :: object - r0, r1 :: object + x, y, r0, r1 :: object r2 :: list r3, r4, r5 :: object r6 :: int32 diff --git a/mypyc/test-data/refcount.test b/mypyc/test-data/refcount.test index ef17040dfba1..46f11e110bc7 100644 --- a/mypyc/test-data/refcount.test +++ b/mypyc/test-data/refcount.test @@ -62,8 +62,7 @@ def f() -> int: return y [out] def f(): - x :: int - y :: int + x, y :: int r0 :: bool r1 :: native_int r2, r3, r4 :: bool @@ -100,8 +99,7 @@ def f(a: int, b: int) -> int: return y [out] def f(a, b): - a, b :: int - r0, x, r1, y :: int + a, b, r0, x, r1, y :: int L0: r0 = CPyTagged_Add(a, 2) x = r0 @@ -118,8 +116,7 @@ def f(a: int) -> int: return x + y [out] def f(a): - a, x, y :: int - r0 :: int + a, x, y, r0 :: int L0: inc_ref a :: int x = a @@ -139,8 +136,7 @@ def f(a: int) -> int: return y [out] def f(a): - a :: int - y :: int + a, y :: int L0: a = 2 y = a @@ -171,8 +167,7 @@ def f(a: int) -> int: return a [out] def f(a): - a :: int - x, y :: int + a, x, y :: int L0: x = 2 inc_ref x :: int @@ -205,8 +200,7 @@ def f(a): r0 :: bool r1 :: native_int r2, r3, r4 :: bool - x :: int - r5, y :: int + x, r5, y :: int L0: r1 = a & 1 r2 = r1 == 0 @@ -250,8 +244,7 @@ def f(a): r0 :: bool r1 :: native_int r2, r3, r4 :: bool - x :: int - r5, y :: int + x, r5, y :: int L0: r1 = a & 1 r2 = r1 == 0 @@ -321,8 +314,7 @@ def f(a: int) -> int: -- This is correct but bad code [out] def f(a): - a :: int - x, r0 :: int + a, x, r0 :: int L0: inc_ref a :: int a = a @@ -343,10 +335,7 @@ def f(a: int) -> int: return a + x [out] def f(a): - a :: int - r0 :: int - x :: int - r1, r2 :: int + a, r0, x, r1, r2 :: int L0: r0 = CPyTagged_Add(a, 2) a = r0 @@ -365,8 +354,7 @@ def f() -> None: x = x + 1 [out] def f(): - x :: int - r0 :: int + x, r0 :: int L0: x = 2 r0 = CPyTagged_Add(x, 2) @@ -381,8 +369,7 @@ def f() -> None: x = y + 1 [out] def f(): - y :: int - r0, x :: int + y, r0, x :: int L0: y = 2 r0 = CPyTagged_Add(y, 2) @@ -432,8 +419,7 @@ def f(a: int) -> None: z = y + y [out] def f(a): - a, r0, x :: int - y, r1, z :: int + a, r0, x, y, r1, z :: int L0: r0 = CPyTagged_Add(a, a) x = r0 @@ -452,8 +438,7 @@ def f(a: int) -> None: x = x + x [out] def f(a): - a, r0 :: int - x, r1 :: int + a, r0, x, r1 :: int L0: r0 = CPyTagged_Add(a, a) a = r0 @@ -476,9 +461,7 @@ def f() -> int: return x + y - a [out] def f(): - x :: int - y :: int - z :: int + x, y, z :: int r0 :: bool r1 :: native_int r2, r3, r4 :: bool @@ -528,16 +511,13 @@ def f(a: int) -> int: return sum [out] def f(a): - a :: int - sum :: int - i :: int + a, sum, i :: int r0 :: bool r1 :: native_int r2 :: bool r3 :: native_int r4, r5, r6, r7, r8 :: bool - r9 :: int - r10 :: int + r9, r10 :: int L0: sum = 0 i = 0 @@ -577,8 +557,7 @@ def f(a: int) -> int: return f(a + 1) [out] def f(a): - a :: int - r0, r1 :: int + a, r0, r1 :: int L0: r0 = CPyTagged_Add(a, 2) r1 = f(r0) From fbc45aabdf46c820e81e3fc85e3d50b3ebf28ad0 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 13 Aug 2020 18:48:54 +0100 Subject: [PATCH 121/138] Don't simplify away Any when joining unions (#9301) Previously join of `Union[int, Any]` and `Union[int, None]` could be `Union[int, None]` (we lost the `Any` type). Fix this by replacing a subtype check with a proper subtype check when joining unions. --- mypy/join.py | 2 +- test-data/unit/check-unions.test | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/mypy/join.py b/mypy/join.py index 736e10fd20f2..4cd0da163e13 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -119,7 +119,7 @@ def visit_unbound_type(self, t: UnboundType) -> ProperType: return AnyType(TypeOfAny.special_form) def visit_union_type(self, t: UnionType) -> ProperType: - if is_subtype(self.s, t): + if is_proper_subtype(self.s, t): return t else: return mypy.typeops.make_simplified_union([self.s, t]) diff --git a/test-data/unit/check-unions.test b/test-data/unit/check-unions.test index 4fcc1007ae48..4a163136d553 100644 --- a/test-data/unit/check-unions.test +++ b/test-data/unit/check-unions.test @@ -1048,3 +1048,14 @@ def foo(a: T2, b: T2) -> T2: def bar(a: T4, b: T4) -> T4: # test multi-level alias return a + b [builtins fixtures/ops.pyi] + +[case testJoinUnionWithUnionAndAny] +# flags: --strict-optional +from typing import TypeVar, Union, Any +T = TypeVar("T") +def f(x: T, y: T) -> T: + return x +x: Union[None, Any] +y: Union[int, None] +reveal_type(f(x, y)) # N: Revealed type is 'Union[None, Any, builtins.int]' +reveal_type(f(y, x)) # N: Revealed type is 'Union[builtins.int, None, Any]' From c2c72da2d378c4f2121236a4e7ce65328bc34f4f Mon Sep 17 00:00:00 2001 From: "Michael J. Sullivan" Date: Fri, 14 Aug 2020 03:49:37 -0700 Subject: [PATCH 122/138] Don't infinite loop on self deps in --follow-imports=normal (#9302) There are situations in complex SCCs where the semantic analyzer will infer self dependencies, which will cause an infinite loop in `dmypy --follow-imports=normal`. It's probably a bug that we do that, and that should be fixed to, but fixing a graph algorithm to not infinite loop on self edges seems like a reasonable thing to do in any case. I don't have a minimized test case yet, and am submitting this without one because it should be harmless and because I want it to get into the release. --- mypy/dmypy_server.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mypy/dmypy_server.py b/mypy/dmypy_server.py index 52db838d038e..157850b39ee9 100644 --- a/mypy/dmypy_server.py +++ b/mypy/dmypy_server.py @@ -557,6 +557,10 @@ def fine_grained_increment_follow_imports(self, sources: List[BuildSource]) -> L if module[0] not in graph: continue sources2 = self.direct_imports(module, graph) + # Filter anything already seen before. This prevents + # infinite looping if there are any self edges. (Self + # edges are maybe a bug, but...) + sources2 = [source for source in sources2 if source.module not in seen] changed, new_files = self.find_reachable_changed_modules( sources2, graph, seen, changed_paths ) From eb6afc61247f8e9d89d4aad875272a63aeda263e Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Sun, 16 Aug 2020 17:52:42 -0700 Subject: [PATCH 123/138] Sync typeshed (#9317) Co-authored-by: hauntsaninja <> --- mypy/typeshed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/typeshed b/mypy/typeshed index 276d0428b90c..0ebe4c2b6544 160000 --- a/mypy/typeshed +++ b/mypy/typeshed @@ -1 +1 @@ -Subproject commit 276d0428b90c7454d008dd6d794dad727a609492 +Subproject commit 0ebe4c2b6544668e937ee1a404d67d831ce16114 From 1c22371949196713493e30cb5667a6c6ad54f031 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 25 Aug 2020 18:20:57 +0100 Subject: [PATCH 124/138] Sync typeshed (#9352) --- mypy/typeshed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/typeshed b/mypy/typeshed index 0ebe4c2b6544..ab0f5519a970 160000 --- a/mypy/typeshed +++ b/mypy/typeshed @@ -1 +1 @@ -Subproject commit 0ebe4c2b6544668e937ee1a404d67d831ce16114 +Subproject commit ab0f5519a97021426ed20831cb0ef2dad7004438 From d284d19bdca488929ab5e9cb66cf03bdd7adc535 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 28 Aug 2020 17:48:34 +0100 Subject: [PATCH 125/138] Sync typeshed (#9370) --- mypy/typeshed | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/typeshed b/mypy/typeshed index ab0f5519a970..5be9c915181f 160000 --- a/mypy/typeshed +++ b/mypy/typeshed @@ -1 +1 @@ -Subproject commit ab0f5519a97021426ed20831cb0ef2dad7004438 +Subproject commit 5be9c915181ffb3e12d61ce0d740098cc9dfcbd1 From 91b8610da939531ca540ddec00202b2730710f7e Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 28 Aug 2020 18:14:56 +0100 Subject: [PATCH 126/138] Make None compatible with Hashable (#9371) Fixes #8768. --- mypy/subtypes.py | 13 ++++++---- test-data/unit/check-protocols.test | 37 +++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 693af526aa72..107a5abbebab 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -207,10 +207,15 @@ def visit_any(self, left: AnyType) -> bool: def visit_none_type(self, left: NoneType) -> bool: if state.strict_optional: - return (isinstance(self.right, NoneType) or - is_named_instance(self.right, 'builtins.object') or - isinstance(self.right, Instance) and self.right.type.is_protocol and - not self.right.type.protocol_members) + if isinstance(self.right, NoneType) or is_named_instance(self.right, + 'builtins.object'): + return True + if isinstance(self.right, Instance) and self.right.type.is_protocol: + members = self.right.type.protocol_members + # None is compatible with Hashable (and other similar protocols). This is + # slightly sloppy since we don't check the signature of "__hash__". + return not members or members == ["__hash__"] + return False else: return True diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index b78becc88be4..0c0865c0540e 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -2499,3 +2499,40 @@ reveal_type(abs(3)) # N: Revealed type is 'builtins.int*' reveal_type(abs(ALL)) # N: Revealed type is 'builtins.int*' [builtins fixtures/float.pyi] [typing fixtures/typing-full.pyi] + +[case testProtocolWithSlots] +from typing import Protocol + +class A(Protocol): + __slots__ = () + +[builtins fixtures/tuple.pyi] + +[case testNoneVsProtocol] +# mypy: strict-optional +from typing_extensions import Protocol + +class MyHashable(Protocol): + def __hash__(self) -> int: ... + +def f(h: MyHashable) -> None: pass +f(None) + +class Proto(Protocol): + def __hash__(self) -> int: ... + def method(self) -> None: ... + +def g(h: Proto) -> None: pass +g(None) # E: Argument 1 to "g" has incompatible type "None"; expected "Proto" + +class Proto2(Protocol): + def hash(self) -> None: ... + +def h(h: Proto2) -> None: pass +h(None) # E: Argument 1 to "h" has incompatible type "None"; expected "Proto2" + +class EmptyProto(Protocol): ... + +def hh(h: EmptyProto) -> None: pass +hh(None) +[builtins fixtures/tuple.pyi] From 1648823f828151e41180fbfd42fcc1f080599fb2 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 29 Sep 2020 14:18:39 +0100 Subject: [PATCH 127/138] Empty dummy commit to re-trigger builds From 4aacfd55c38ae9370db1d075014db3bca54d7d71 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 30 Sep 2020 11:39:02 +0100 Subject: [PATCH 128/138] Remove test case that was added accidentally --- test-data/unit/check-protocols.test | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index 0c0865c0540e..4c191ed63f9e 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -2500,12 +2500,6 @@ reveal_type(abs(ALL)) # N: Revealed type is 'builtins.int*' [builtins fixtures/float.pyi] [typing fixtures/typing-full.pyi] -[case testProtocolWithSlots] -from typing import Protocol - -class A(Protocol): - __slots__ = () - [builtins fixtures/tuple.pyi] [case testNoneVsProtocol] From 9d560ce0f5759a54d74cbf073d71711e5f972b22 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 30 Sep 2020 13:53:57 +0100 Subject: [PATCH 129/138] Fix bad test case description Oops. --- test-data/unit/check-protocols.test | 2 -- 1 file changed, 2 deletions(-) diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index 4c191ed63f9e..ba8da159f337 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -2500,8 +2500,6 @@ reveal_type(abs(ALL)) # N: Revealed type is 'builtins.int*' [builtins fixtures/float.pyi] [typing fixtures/typing-full.pyi] -[builtins fixtures/tuple.pyi] - [case testNoneVsProtocol] # mypy: strict-optional from typing_extensions import Protocol From a2bdf0428d511cb5220e173a4768c438eb5d428f Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 30 Sep 2020 14:27:46 +0100 Subject: [PATCH 130/138] Empty dummy commit to re-trigger builds From ab114f3db4696e0187d47137147e648cb28134a0 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Tue, 29 Sep 2020 14:27:20 +0100 Subject: [PATCH 131/138] [mypyc] Try to fix errors about int32_t on Python 3.5 and Appveyor (#9502) Explicitly include `stdint.h`. I'll monitor the Appveyor build to see if this helps. Work on #9501. --- mypyc/lib-rt/CPy.h | 1 + 1 file changed, 1 insertion(+) diff --git a/mypyc/lib-rt/CPy.h b/mypyc/lib-rt/CPy.h index bec7fd6e19e5..403cdda778fc 100644 --- a/mypyc/lib-rt/CPy.h +++ b/mypyc/lib-rt/CPy.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "pythonsupport.h" #include "mypyc_util.h" From 498769b5531630016341cb55ead6df3682240368 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 30 Sep 2020 13:51:10 +0100 Subject: [PATCH 132/138] Always type check arguments when using --disallow-untyped-calls (#9510) Fixes #9509. --- mypy/checkexpr.py | 2 +- test-data/unit/check-flags.test | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index aa371548127e..d459d5952e14 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -314,7 +314,7 @@ def visit_call_expr_inner(self, e: CallExpr, allow_none_return: bool = False) -> self.chk.in_checked_function() and isinstance(callee_type, CallableType) and callee_type.implicit): - return self.msg.untyped_function_call(callee_type, e) + self.msg.untyped_function_call(callee_type, e) # Figure out the full name of the callee for plugin lookup. object_type = None member = None diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 865995376f5d..048d49d91843 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -1575,3 +1575,12 @@ def bad_return_type() -> str: return None # E: Incompatible return value type (got "None", expected "str") [return-value] bad_return_type('no args taken!') + +[case testDisallowUntypedCallsArgType] +# flags: --disallow-untyped-calls +def f(x): + pass + +y = 1 +f(reveal_type(y)) # E: Call to untyped function "f" in typed context \ + # N: Revealed type is 'builtins.int' From bbfad4630c06a941c29b47530f9fdb37baa997c1 Mon Sep 17 00:00:00 2001 From: Lawrence Chan Date: Sat, 26 Sep 2020 20:27:34 -0500 Subject: [PATCH 133/138] Store the type for assignment expr (walrus) targets (#9479) Fixes #9054 --- mypy/checkexpr.py | 3 +++ test-data/unit/check-python38.test | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index d459d5952e14..103de804a1f0 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -3778,6 +3778,9 @@ def accept(self, assert typ is not None self.chk.store_type(node, typ) + if isinstance(node, AssignmentExpr) and not has_uninhabited_component(typ): + self.chk.store_type(node.target, typ) + if (self.chk.options.disallow_any_expr and not always_allow_any and not self.chk.is_stub and diff --git a/test-data/unit/check-python38.test b/test-data/unit/check-python38.test index 78d62ae43ba4..8e013751835f 100644 --- a/test-data/unit/check-python38.test +++ b/test-data/unit/check-python38.test @@ -377,3 +377,13 @@ def check_partial_list() -> None: z.append(3) reveal_type(z) # N: Revealed type is 'builtins.list[builtins.int]' [builtins fixtures/list.pyi] + +[case testWalrusExpr] +def func() -> None: + foo = Foo() + if x := foo.x: + pass + +class Foo: + def __init__(self) -> None: + self.x = 123 From 1bed354d5d4f08aa59a199db1a62e5b049f24756 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 28 Sep 2020 21:39:14 -0700 Subject: [PATCH 134/138] Fix partial type crash during protocol checking (#9495) In particular, this affected hashables. Fixes #9437 Co-authored-by: hauntsaninja <> --- mypy/subtypes.py | 4 +++ test-data/unit/check-protocols.test | 38 +++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 107a5abbebab..81726b1f9884 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -543,6 +543,10 @@ def f(self) -> A: ... # print(member, 'of', right, 'has type', supertype) if not subtype: return False + if isinstance(subtype, PartialType): + subtype = NoneType() if subtype.type is None else Instance( + subtype.type, [AnyType(TypeOfAny.unannotated)] * len(subtype.type.type_vars) + ) if not proper_subtype: # Nominal check currently ignores arg names # NOTE: If we ever change this, be sure to also change the call to diff --git a/test-data/unit/check-protocols.test b/test-data/unit/check-protocols.test index ba8da159f337..5fd08916dddc 100644 --- a/test-data/unit/check-protocols.test +++ b/test-data/unit/check-protocols.test @@ -2528,3 +2528,41 @@ class EmptyProto(Protocol): ... def hh(h: EmptyProto) -> None: pass hh(None) [builtins fixtures/tuple.pyi] + + +[case testPartialTypeProtocol] +from typing import Protocol + +class Flapper(Protocol): + def flap(self) -> int: ... + +class Blooper: + flap = None + + def bloop(self, x: Flapper) -> None: + reveal_type([self, x]) # N: Revealed type is 'builtins.list[builtins.object*]' + +class Gleemer: + flap = [] # E: Need type annotation for 'flap' (hint: "flap: List[] = ...") + + def gleem(self, x: Flapper) -> None: + reveal_type([self, x]) # N: Revealed type is 'builtins.list[builtins.object*]' +[builtins fixtures/tuple.pyi] + + +[case testPartialTypeProtocolHashable] +# flags: --no-strict-optional +from typing import Protocol + +class Hashable(Protocol): + def __hash__(self) -> int: ... + +class ObjectHashable: + def __hash__(self) -> int: ... + +class DataArray(ObjectHashable): + __hash__ = None + + def f(self, x: Hashable) -> None: + reveal_type([self, x]) # N: Revealed type is 'builtins.list[builtins.object*]' +[builtins fixtures/tuple.pyi] From 9503938eb815928f93eacbdc283ae3d04f716db1 Mon Sep 17 00:00:00 2001 From: Aristotelis Mikropoulos Date: Tue, 6 Oct 2020 07:43:20 +0300 Subject: [PATCH 135/138] Document --disable-error-code config option (#9539) Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com> --- docs/source/config_file.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index f45eceacbe67..28aa58bb56a6 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -529,6 +529,12 @@ Miscellaneous strictness flags Allows variables to be redefined with an arbitrary type, as long as the redefinition is in the same block and nesting level as the original definition. +.. confval:: disable_error_code + + :type: comma-separated list of strings + + Allows disabling one or multiple error codes globally. + .. confval:: implicit_reexport :type: boolean From 58cbfb4475a573888758bf0f8688efed4610bd31 Mon Sep 17 00:00:00 2001 From: Aristotelis Mikropoulos Date: Mon, 5 Oct 2020 22:45:15 +0300 Subject: [PATCH 136/138] Add test case for disable_error_code config file option (#9538) --- mypy/config_parser.py | 2 ++ test-data/unit/check-flags.test | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/mypy/config_parser.py b/mypy/config_parser.py index 7e1f16f56b25..27aeca3fe5b9 100644 --- a/mypy/config_parser.py +++ b/mypy/config_parser.py @@ -94,6 +94,8 @@ def check_follow_imports(choice: str) -> str: 'plugins': lambda s: [p.strip() for p in s.split(',')], 'always_true': lambda s: [p.strip() for p in s.split(',')], 'always_false': lambda s: [p.strip() for p in s.split(',')], + 'disable_error_code': lambda s: [p.strip() for p in s.split(',')], + 'enable_error_code': lambda s: [p.strip() for p in s.split(',')], 'package_root': lambda s: [p.strip() for p in s.split(',')], 'cache_dir': expand_path, 'python_executable': expand_path, diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 048d49d91843..1cee362422f8 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -1078,6 +1078,15 @@ always_true = YOLO1, YOLO always_false = BLAH, BLAH1 [builtins fixtures/bool.pyi] +[case testDisableErrorCodeConfigFile] +# flags: --config-file tmp/mypy.ini --disallow-untyped-defs +import foo +def bar(): + pass +[file mypy.ini] +\[mypy] +disable_error_code = import, no-untyped-def + [case testCheckDisallowAnyGenericsNamedTuple] # flags: --disallow-any-generics from typing import NamedTuple From 54712a70d0861073d453a3b40afb2a3e9d0fb817 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 8 Oct 2020 17:39:39 +0100 Subject: [PATCH 137/138] Update wheel download script to use the correct file names for macOS (#9559) We switched the macOS version in the mypy_mypyc-wheels repository for Python 3.6 and 3.7 wheels. Examples of wheel names: https://github.com/mypyc/mypy_mypyc-wheels/releases/tag/v0.790%2Bdev.7273e9ab1664b59a74d9bd1d2361bbeb9864b7ab --- misc/download-mypyc-wheels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/download-mypyc-wheels.py b/misc/download-mypyc-wheels.py index ca18effe7cfd..0b9722cabd57 100755 --- a/misc/download-mypyc-wheels.py +++ b/misc/download-mypyc-wheels.py @@ -29,7 +29,7 @@ def download_files(version): for pyver in range(MIN_VER, MAX_VER + 1): for platform in PLATFORMS: abi_tag = "" if pyver >= 8 else "m" - macos_ver = 9 if pyver >= 8 else 6 + macos_ver = 9 if pyver >= 6 else 6 url = URL.format( base=BASE_URL, version=version, From 69a055a7632e2444fcb2bfb022d04f4546358d50 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 9 Oct 2020 10:36:32 +0100 Subject: [PATCH 138/138] Bump version to 0.790 --- mypy/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/version.py b/mypy/version.py index bf89a897e083..9f2a13a74db3 100644 --- a/mypy/version.py +++ b/mypy/version.py @@ -5,7 +5,7 @@ # - Release versions have the form "0.NNN". # - Dev versions have the form "0.NNN+dev" (PLUS sign to conform to PEP 440). # - For 1.0 we'll switch back to 1.2.3 form. -__version__ = '0.790+dev' +__version__ = '0.790' base_version = __version__ mypy_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))