diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py new file mode 100644 index 000000000000..a22c1b9ba9e5 --- /dev/null +++ b/mypy/constant_fold.py @@ -0,0 +1,116 @@ +"""Constant folding of expressions. + +For example, 3 + 5 can be constant folded into 8. +""" + +from __future__ import annotations + +from typing import Union +from typing_extensions import Final + +from mypy.nodes import Expression, FloatExpr, IntExpr, NameExpr, OpExpr, StrExpr, UnaryExpr, Var + +# All possible result types of constant folding +ConstantValue = Union[int, bool, float, str] +CONST_TYPES: Final = (int, bool, float, str) + + +def constant_fold_expr(expr: Expression, cur_mod_id: str) -> ConstantValue | None: + """Return the constant value of an expression for supported operations. + + Among other things, support int arithmetic and string + concatenation. For example, the expression 3 + 5 has the constant + value 8. + + Also bind simple references to final constants defined in the + current module (cur_mod_id). Binding to references is best effort + -- we don't bind references to other modules. Mypyc trusts these + to be correct in compiled modules, so that it can replace a + constant expression (or a reference to one) with the statically + computed value. We don't want to infer constant values based on + stubs, in particular, as these might not match the implementation + (due to version skew, for example). + + Return None if unsuccessful. + """ + if isinstance(expr, IntExpr): + return expr.value + if isinstance(expr, StrExpr): + return expr.value + if isinstance(expr, FloatExpr): + return expr.value + elif isinstance(expr, NameExpr): + if expr.name == "True": + return True + elif expr.name == "False": + return False + node = expr.node + if ( + isinstance(node, Var) + and node.is_final + and node.fullname.rsplit(".", 1)[0] == cur_mod_id + ): + value = node.final_value + if isinstance(value, (CONST_TYPES)): + return value + elif isinstance(expr, OpExpr): + left = constant_fold_expr(expr.left, cur_mod_id) + right = constant_fold_expr(expr.right, cur_mod_id) + if isinstance(left, int) and isinstance(right, int): + return constant_fold_binary_int_op(expr.op, left, right) + elif isinstance(left, str) and isinstance(right, str): + return constant_fold_binary_str_op(expr.op, left, right) + elif isinstance(expr, UnaryExpr): + value = constant_fold_expr(expr.expr, cur_mod_id) + if isinstance(value, int): + return constant_fold_unary_int_op(expr.op, value) + return None + + +def constant_fold_binary_int_op(op: str, left: int, right: int) -> int | None: + if op == "+": + return left + right + if op == "-": + return left - right + elif op == "*": + return left * right + elif op == "//": + if right != 0: + return left // right + elif op == "%": + if right != 0: + return left % right + elif op == "&": + return left & right + elif op == "|": + return left | right + elif op == "^": + return left ^ right + elif op == "<<": + if right >= 0: + return left << right + elif op == ">>": + if right >= 0: + return left >> right + elif op == "**": + if right >= 0: + ret = left**right + assert isinstance(ret, int) + return ret + return None + + +def constant_fold_unary_int_op(op: str, value: int) -> int | None: + if op == "-": + return -value + elif op == "~": + return ~value + elif op == "+": + return value + return None + + +def constant_fold_binary_str_op(op: str, left: str, right: str) -> str | None: + if op == "+": + return left + right + return None diff --git a/mypy/semanal.py b/mypy/semanal.py index 266dc891b697..fee66ae9b2cc 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -55,6 +55,7 @@ from typing_extensions import Final, TypeAlias as _TypeAlias from mypy import errorcodes as codes, message_registry +from mypy.constant_fold import constant_fold_expr from mypy.errorcodes import ErrorCode from mypy.errors import Errors, report_internal_error from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type @@ -91,7 +92,6 @@ AwaitExpr, Block, BreakStmt, - BytesExpr, CallExpr, CastExpr, ClassDef, @@ -108,7 +108,6 @@ Expression, ExpressionStmt, FakeExpression, - FloatExpr, ForStmt, FuncBase, FuncDef, @@ -121,7 +120,6 @@ ImportBase, ImportFrom, IndexExpr, - IntExpr, LambdaExpr, ListComprehension, ListExpr, @@ -250,7 +248,6 @@ FunctionLike, Instance, LiteralType, - LiteralValue, NoneType, Overloaded, Parameters, @@ -3138,7 +3135,8 @@ def store_final_status(self, s: AssignmentStmt) -> None: node = s.lvalues[0].node if isinstance(node, Var): node.is_final = True - node.final_value = self.unbox_literal(s.rvalue) + if s.type: + node.final_value = constant_fold_expr(s.rvalue, self.cur_mod_id) if self.is_class_scope() and ( isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs ): @@ -3198,13 +3196,6 @@ def flatten_lvalues(self, lvalues: list[Expression]) -> list[Expression]: res.append(lv) return res - def unbox_literal(self, e: Expression) -> int | float | bool | str | None: - if isinstance(e, (IntExpr, FloatExpr, StrExpr)): - return e.value - elif isinstance(e, NameExpr) and e.name in ("True", "False"): - return True if e.name == "True" else False - return None - def process_type_annotation(self, s: AssignmentStmt) -> None: """Analyze type annotation or infer simple literal type.""" if s.type: @@ -3259,39 +3250,33 @@ def is_annotated_protocol_member(self, s: AssignmentStmt) -> bool: def analyze_simple_literal_type(self, rvalue: Expression, is_final: bool) -> Type | None: """Return builtins.int if rvalue is an int literal, etc. - If this is a 'Final' context, we return "Literal[...]" instead.""" - if self.options.semantic_analysis_only or self.function_stack: - # Skip this if we're only doing the semantic analysis pass. - # This is mostly to avoid breaking unit tests. - # Also skip inside a function; this is to avoid confusing + + If this is a 'Final' context, we return "Literal[...]" instead. + """ + if self.function_stack: + # Skip inside a function; this is to avoid confusing # the code that handles dead code due to isinstance() # inside type variables with value restrictions (like # AnyStr). return None - if isinstance(rvalue, FloatExpr): - return self.named_type_or_none("builtins.float") - - value: LiteralValue | None = None - type_name: str | None = None - if isinstance(rvalue, IntExpr): - value, type_name = rvalue.value, "builtins.int" - if isinstance(rvalue, StrExpr): - value, type_name = rvalue.value, "builtins.str" - if isinstance(rvalue, BytesExpr): - value, type_name = rvalue.value, "builtins.bytes" - - if type_name is not None: - assert value is not None - typ = self.named_type_or_none(type_name) - if typ and is_final: - return typ.copy_modified( - last_known_value=LiteralType( - value=value, fallback=typ, line=typ.line, column=typ.column - ) - ) - return typ - return None + value = constant_fold_expr(rvalue, self.cur_mod_id) + if value is None: + return None + + if isinstance(value, bool): + type_name = "builtins.bool" + elif isinstance(value, int): + type_name = "builtins.int" + elif isinstance(value, str): + type_name = "builtins.str" + elif isinstance(value, float): + type_name = "builtins.float" + + typ = self.named_type_or_none(type_name) + if typ and is_final: + return typ.copy_modified(last_known_value=LiteralType(value=value, fallback=typ)) + return typ def analyze_alias( self, name: str, rvalue: Expression, allow_placeholder: bool = False @@ -3827,6 +3812,14 @@ def store_declared_types(self, lvalue: Lvalue, typ: Type) -> None: var = lvalue.node var.type = typ var.is_ready = True + typ = get_proper_type(typ) + if ( + var.is_final + and isinstance(typ, Instance) + and typ.last_known_value + and (not self.type or not self.type.is_enum) + ): + var.final_value = typ.last_known_value.value # If node is not a variable, we'll catch it elsewhere. elif isinstance(lvalue, TupleExpr): typ = get_proper_type(typ) diff --git a/mypy/types.py b/mypy/types.py index b5a4f90d5ec3..0ba0985436ed 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -67,7 +67,10 @@ # Note: Although "Literal[None]" is a valid type, we internally always convert # such a type directly into "None". So, "None" is not a valid parameter of # LiteralType and is omitted from this list. -LiteralValue: _TypeAlias = Union[int, str, bool] +# +# Note: Float values are only used internally. They are not accepted within +# Literal[...]. +LiteralValue: _TypeAlias = Union[int, str, bool, float] # If we only import type_visitor in the middle of the file, mypy diff --git a/mypyc/irbuild/constant_fold.py b/mypyc/irbuild/constant_fold.py index 8d0a7fea5d90..4e9eb53b9222 100644 --- a/mypyc/irbuild/constant_fold.py +++ b/mypyc/irbuild/constant_fold.py @@ -1,6 +1,11 @@ """Constant folding of IR values. For example, 3 + 5 can be constant folded into 8. + +This is mostly like mypy.constant_fold, but we can bind some additional +NameExpr and MemberExpr references here, since we have more knowledge +about which definitions can be trusted -- we constant fold only references +to other compiled modules in the same compilation unit. """ from __future__ import annotations @@ -8,6 +13,11 @@ from typing import Union from typing_extensions import Final +from mypy.constant_fold import ( + constant_fold_binary_int_op, + constant_fold_binary_str_op, + constant_fold_unary_int_op, +) from mypy.nodes import Expression, IntExpr, MemberExpr, NameExpr, OpExpr, StrExpr, UnaryExpr, Var from mypyc.irbuild.builder import IRBuilder @@ -51,52 +61,3 @@ def constant_fold_expr(builder: IRBuilder, expr: Expression) -> ConstantValue | if isinstance(value, int): return constant_fold_unary_int_op(expr.op, value) return None - - -def constant_fold_binary_int_op(op: str, left: int, right: int) -> int | None: - if op == "+": - return left + right - if op == "-": - return left - right - elif op == "*": - return left * right - elif op == "//": - if right != 0: - return left // right - elif op == "%": - if right != 0: - return left % right - elif op == "&": - return left & right - elif op == "|": - return left | right - elif op == "^": - return left ^ right - elif op == "<<": - if right >= 0: - return left << right - elif op == ">>": - if right >= 0: - return left >> right - elif op == "**": - if right >= 0: - ret = left**right - assert isinstance(ret, int) - return ret - return None - - -def constant_fold_unary_int_op(op: str, value: int) -> int | None: - if op == "-": - return -value - elif op == "~": - return ~value - elif op == "+": - return value - return None - - -def constant_fold_binary_str_op(op: str, left: str, right: str) -> str | None: - if op == "+": - return left + right - return None diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 4f5c9487bb1d..16b085ad4927 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -3273,7 +3273,7 @@ L2: [case testFinalStaticInt] from typing import Final -x: Final = 1 + 1 +x: Final = 1 + int() def f() -> int: return x - 1 diff --git a/mypyc/test-data/irbuild-constant-fold.test b/mypyc/test-data/irbuild-constant-fold.test index dd75c01443f1..7d9127887aa6 100644 --- a/mypyc/test-data/irbuild-constant-fold.test +++ b/mypyc/test-data/irbuild-constant-fold.test @@ -205,23 +205,13 @@ Y: Final = 2 + 4 def f() -> None: a = X + 1 - # TODO: Constant fold this as well a = Y + 1 [out] def f(): - a, r0 :: int - r1 :: bool - r2 :: int + a :: int L0: a = 12 - r0 = __main__.Y :: static - if is_error(r0) goto L1 else goto L2 -L1: - r1 = raise NameError('value for final name "Y" was not set') - unreachable -L2: - r2 = CPyTagged_Add(r0, 2) - a = r2 + a = 14 return 1 [case testIntConstantFoldingClassFinal] diff --git a/mypyc/test-data/irbuild-i64.test b/mypyc/test-data/irbuild-i64.test index ecedab2cd45d..9c942ea75219 100644 --- a/mypyc/test-data/irbuild-i64.test +++ b/mypyc/test-data/irbuild-i64.test @@ -1663,3 +1663,44 @@ L1: x = 12 L2: return 1 + +[case testI64FinalConstants] +from typing_extensions import Final +from mypy_extensions import i64 + +A: Final = -1 +B: Final = -(1 + 3*2) +C: Final = 0 +D: Final = A - B +E: Final[i64] = 1 + 3 + +def f1() -> i64: + return A + +def f2() -> i64: + return A + B + +def f3() -> i64: + return C + +def f4() -> i64: + return D + +def f5() -> i64: + return E +[out] +def f1(): +L0: + return -1 +def f2(): +L0: + return -8 +def f3(): +L0: + return 0 +def f4(): +L0: + return 6 +def f5(): +L0: + return 4 diff --git a/mypyc/test-data/irbuild-int.test b/mypyc/test-data/irbuild-int.test index 8bf43cfa4923..e193c16ef979 100644 --- a/mypyc/test-data/irbuild-int.test +++ b/mypyc/test-data/irbuild-int.test @@ -155,3 +155,51 @@ def divby8(x): L0: r0 = CPyTagged_Rshift(x, 6) return r0 + +[case testFinalConstantFolding] +from typing_extensions import Final + +X: Final = -1 +Y: Final = -(1 + 3*2) +Z: Final = Y + 1 + +class C: + A: Final = 1 + B: Final = -1 + +def f1() -> int: + return X + +def f2() -> int: + return X + Y + +def f3() -> int: + return Z + +def f4() -> int: + return C.A + +def f5() -> int: + return C.B +[out] +def C.__mypyc_defaults_setup(__mypyc_self__): + __mypyc_self__ :: __main__.C +L0: + __mypyc_self__.A = 2 + __mypyc_self__.B = -2 + return 1 +def f1(): +L0: + return -2 +def f2(): +L0: + return -16 +def f3(): +L0: + return -12 +def f4(): +L0: + return 2 +def f5(): +L0: + return -2 diff --git a/mypyc/test-data/run-classes.test b/mypyc/test-data/run-classes.test index 177bae0cc895..2af519dc7aa8 100644 --- a/mypyc/test-data/run-classes.test +++ b/mypyc/test-data/run-classes.test @@ -2256,3 +2256,14 @@ class Derived(Base): pass assert Derived()() == 1 + +[case testClassWithFinalAttribute] +from typing_extensions import Final + +class C: + A: Final = -1 + a: Final = [A] + +def test_final_attribute() -> None: + assert C.A == -1 + assert C.a == [-1] diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test index f7aa43d43f3e..6b42141b2b15 100644 --- a/test-data/unit/check-expressions.test +++ b/test-data/unit/check-expressions.test @@ -1724,7 +1724,7 @@ reveal_type = 1 [case testRevealForward] def f() -> None: reveal_type(x) -x = 1 + 1 +x = 1 + int() [out] main:2: note: Revealed type is "builtins.int" diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 6767f1c7995c..45a833e5210c 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -1979,7 +1979,7 @@ class A: [case testMultipassAndTopLevelVariable] y = x # E: Cannot determine type of "x" y() -x = 1+0 +x = 1+int() [out] [case testMultipassAndDecoratedMethod] diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index b3267f66653d..26bd0f92ed9e 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -1348,13 +1348,13 @@ import a import b def f() -> int: return b.x -y = 0 + 0 +y = 0 + int() [file b.py] import a def g() -> int: reveal_type(a.y) return a.y -x = 1 + 1 +x = 1 + int() [out] tmp/b.py:3: note: Revealed type is "builtins.int" @@ -1365,12 +1365,12 @@ import b def f() -> int: reveal_type(b.x) return b.x -y = 0 + 0 +y = 0 + int() [file b.py] import a def g() -> int: return a.y -x = 1 + 1 +x = 1 + int() [out] tmp/a.py:3: note: Revealed type is "builtins.int" @@ -1385,7 +1385,7 @@ class C: self.x2 = b.b [file b.py] import a -b = 1 + 1 +b = 1 + int() [out] tmp/a.py:4: error: Cannot determine type of "x2" @@ -1398,7 +1398,7 @@ def f() -> None: a + '' [file b.py] import a -x = 1 + 1 +x = 1 + int() [out] tmp/a.py:4: error: Unsupported operand types for + ("int" and "str") @@ -1411,7 +1411,7 @@ def f() -> None: a + '' [file b.py] import a -x = 1 + 1 +x = 1 + int() [out] tmp/a.py:4: error: Unsupported operand types for + ("int" and "str") @@ -1424,7 +1424,7 @@ def g() -> None: @b.deco def f(a: str) -> int: pass reveal_type(f) -x = 1 + 1 +x = 1 + int() [file b.py] from typing import Callable, TypeVar import a diff --git a/test-data/unit/errorstream.test b/test-data/unit/errorstream.test index 8a73748d27ff..46af433f8916 100644 --- a/test-data/unit/errorstream.test +++ b/test-data/unit/errorstream.test @@ -36,14 +36,14 @@ import b def f() -> int: reveal_type(b.x) return b.x -y = 0 + 0 +y = 0 + int() [file b.py] import a def g() -> int: reveal_type(a.y) return a.y 1 / '' -x = 1 + 1 +x = 1 + int() [out] ==== Errors flushed ==== diff --git a/test-data/unit/semanal-basic.test b/test-data/unit/semanal-basic.test index 4b60ab99f869..870c686807c3 100644 --- a/test-data/unit/semanal-basic.test +++ b/test-data/unit/semanal-basic.test @@ -8,8 +8,9 @@ x [out] MypyFile:1( AssignmentStmt:1( - NameExpr(x* [__main__.x]) - IntExpr(1)) + NameExpr(x [__main__.x]) + IntExpr(1) + builtins.int) ExpressionStmt:2( NameExpr(x [__main__.x]))) @@ -25,8 +26,9 @@ MypyFile:1( NameExpr(y* [__main__.y])) IntExpr(2)) AssignmentStmt:2( - NameExpr(z* [__main__.z]) - IntExpr(3)) + NameExpr(z [__main__.z]) + IntExpr(3) + builtins.int) ExpressionStmt:3( TupleExpr:3( NameExpr(x [__main__.x]) @@ -61,8 +63,9 @@ MypyFile:1( NameExpr(f [__main__.f]) Args())) AssignmentStmt:3( - NameExpr(x* [__main__.x]) - IntExpr(1)) + NameExpr(x [__main__.x]) + IntExpr(1) + builtins.int) FuncDef:4( f Block:4( @@ -117,8 +120,9 @@ MypyFile:1( NameExpr(g [__main__.g]) Args())))) AssignmentStmt:4( - NameExpr(x* [__main__.x]) - IntExpr(1)) + NameExpr(x [__main__.x]) + IntExpr(1) + builtins.int) FuncDef:5( g Block:5( @@ -134,8 +138,9 @@ def f(y): [out] MypyFile:1( AssignmentStmt:1( - NameExpr(x* [__main__.x]) - IntExpr(1)) + NameExpr(x [__main__.x]) + IntExpr(1) + builtins.int) AssignmentStmt:2( NameExpr(x [__main__.x]) IntExpr(2)) @@ -163,8 +168,9 @@ x [out] MypyFile:1( AssignmentStmt:1( - NameExpr(x* [__main__.x]) - IntExpr(1)) + NameExpr(x [__main__.x]) + IntExpr(1) + builtins.int) FuncDef:2( f Block:2( @@ -498,17 +504,21 @@ MypyFile:1( ExpressionStmt:3( Ellipsis))) AssignmentStmt:4( - NameExpr(x* [__main__.x] = 1) - IntExpr(1)) + NameExpr(x [__main__.x] = 1) + IntExpr(1) + Literal[1]?) AssignmentStmt:5( - NameExpr(y* [__main__.y] = 1.0) - FloatExpr(1.0)) + NameExpr(y [__main__.y] = 1.0) + FloatExpr(1.0) + Literal[1.0]?) AssignmentStmt:6( - NameExpr(s* [__main__.s] = hi) - StrExpr(hi)) + NameExpr(s [__main__.s] = hi) + StrExpr(hi) + Literal['hi']?) AssignmentStmt:7( - NameExpr(t* [__main__.t] = True) - NameExpr(True [builtins.True])) + NameExpr(t [__main__.t] = True) + NameExpr(True [builtins.True]) + Literal[True]?) AssignmentStmt:8( NameExpr(n* [__main__.n] = None) CallExpr:8( diff --git a/test-data/unit/semanal-classes.test b/test-data/unit/semanal-classes.test index 082a3fe69050..86f8b8656fb6 100644 --- a/test-data/unit/semanal-classes.test +++ b/test-data/unit/semanal-classes.test @@ -248,8 +248,9 @@ MypyFile:1( ClassDef:1( A AssignmentStmt:2( - NameExpr(x* [m]) - IntExpr(1)) + NameExpr(x [m]) + IntExpr(1) + builtins.int) AssignmentStmt:3( NameExpr(y* [m]) NameExpr(x [__main__.A.x])))) @@ -287,8 +288,9 @@ MypyFile:1( NameExpr(A [__main__.A])) Then( AssignmentStmt:3( - NameExpr(x* [m]) - IntExpr(1))) + NameExpr(x [m]) + IntExpr(1) + builtins.int)) Else( AssignmentStmt:5( NameExpr(x [__main__.A.x]) @@ -541,8 +543,9 @@ MypyFile:1( ClassDef:2( A AssignmentStmt:3( - NameExpr(X* [m]) - IntExpr(1)) + NameExpr(X [m]) + IntExpr(1) + builtins.int) FuncDef:4( f Args( diff --git a/test-data/unit/semanal-expressions.test b/test-data/unit/semanal-expressions.test index 98bf32708f1b..fa07e533a842 100644 --- a/test-data/unit/semanal-expressions.test +++ b/test-data/unit/semanal-expressions.test @@ -15,8 +15,9 @@ x.y [out] MypyFile:1( AssignmentStmt:1( - NameExpr(x* [__main__.x]) - IntExpr(1)) + NameExpr(x [__main__.x]) + IntExpr(1) + builtins.int) ExpressionStmt:2( MemberExpr:2( NameExpr(x [__main__.x]) @@ -80,8 +81,9 @@ not x [out] MypyFile:1( AssignmentStmt:1( - NameExpr(x* [__main__.x]) - IntExpr(1)) + NameExpr(x [__main__.x]) + IntExpr(1) + builtins.int) ExpressionStmt:2( UnaryExpr:2( - @@ -187,8 +189,9 @@ a = 0 [out] MypyFile:1( AssignmentStmt:1( - NameExpr(a* [__main__.a]) - IntExpr(0)) + NameExpr(a [__main__.a]) + IntExpr(0) + builtins.int) ExpressionStmt:2( ListComprehension:2( GeneratorExpr:2( @@ -223,8 +226,9 @@ b = [x for x in a if x] [out] MypyFile:1( AssignmentStmt:1( - NameExpr(a* [__main__.a]) - IntExpr(0)) + NameExpr(a [__main__.a]) + IntExpr(0) + builtins.int) AssignmentStmt:2( NameExpr(b* [__main__.b]) ListComprehension:2( @@ -240,8 +244,9 @@ a = 0 [out] MypyFile:1( AssignmentStmt:1( - NameExpr(a* [__main__.a]) - IntExpr(0)) + NameExpr(a [__main__.a]) + IntExpr(0) + builtins.int) ExpressionStmt:2( SetComprehension:2( GeneratorExpr:2( @@ -258,8 +263,9 @@ b = {x for x in a if x} [out] MypyFile:1( AssignmentStmt:1( - NameExpr(a* [__main__.a]) - IntExpr(0)) + NameExpr(a [__main__.a]) + IntExpr(0) + builtins.int) AssignmentStmt:2( NameExpr(b* [__main__.b]) SetComprehension:2( @@ -275,8 +281,9 @@ a = 0 [out] MypyFile:1( AssignmentStmt:1( - NameExpr(a* [__main__.a]) - IntExpr(0)) + NameExpr(a [__main__.a]) + IntExpr(0) + builtins.int) ExpressionStmt:2( DictionaryComprehension:2( NameExpr(x [l]) @@ -293,8 +300,9 @@ b = {x: x + 1 for x in a if x} [out] MypyFile:1( AssignmentStmt:1( - NameExpr(a* [__main__.a]) - IntExpr(0)) + NameExpr(a [__main__.a]) + IntExpr(0) + builtins.int) AssignmentStmt:2( NameExpr(b* [__main__.b]) DictionaryComprehension:2( @@ -313,8 +321,9 @@ a = 0 [out] MypyFile:1( AssignmentStmt:1( - NameExpr(a* [__main__.a]) - IntExpr(0)) + NameExpr(a [__main__.a]) + IntExpr(0) + builtins.int) ExpressionStmt:2( GeneratorExpr:2( NameExpr(x [l]) @@ -327,8 +336,9 @@ a = 0 [out] MypyFile:1( AssignmentStmt:1( - NameExpr(a* [__main__.a]) - IntExpr(0)) + NameExpr(a [__main__.a]) + IntExpr(0) + builtins.int) ExpressionStmt:2( GeneratorExpr:2( NameExpr(x [l]) @@ -345,8 +355,9 @@ lambda: x [out] MypyFile:1( AssignmentStmt:1( - NameExpr(x* [__main__.x]) - IntExpr(0)) + NameExpr(x [__main__.x]) + IntExpr(0) + builtins.int) ExpressionStmt:2( LambdaExpr:2( Block:2( diff --git a/test-data/unit/semanal-modules.test b/test-data/unit/semanal-modules.test index 16b9a9b18250..8ffd7d2488dc 100644 --- a/test-data/unit/semanal-modules.test +++ b/test-data/unit/semanal-modules.test @@ -16,8 +16,9 @@ MypyFile:1( MypyFile:1( tmp/x.py AssignmentStmt:1( - NameExpr(y* [x.y]) - IntExpr(1))) + NameExpr(y [x.y]) + IntExpr(1) + builtins.int)) [case testImportedNameInType] import m @@ -51,8 +52,9 @@ MypyFile:1( MypyFile:1( tmp/m.py AssignmentStmt:1( - NameExpr(y* [m.y]) - IntExpr(1))) + NameExpr(y [m.y]) + IntExpr(1) + builtins.int)) [case testImportFromType] from m import c @@ -342,8 +344,9 @@ MypyFile:1( MypyFile:1( tmp/m/n.py AssignmentStmt:1( - NameExpr(x* [m.n.x]) - IntExpr(1))) + NameExpr(x [m.n.x]) + IntExpr(1) + builtins.int)) [case testImportFromSubmodule] from m._n import x @@ -448,8 +451,9 @@ MypyFile:1( MypyFile:1( tmp/m/n/k.py AssignmentStmt:1( - NameExpr(x* [m.n.k.x]) - IntExpr(1))) + NameExpr(x [m.n.k.x]) + IntExpr(1) + builtins.int)) [case testImportInSubmodule] import m._n @@ -609,8 +613,9 @@ MypyFile:1( MypyFile:1( tmp/x.py AssignmentStmt:1( - NameExpr(y* [x.y]) - IntExpr(1))) + NameExpr(y [x.y]) + IntExpr(1) + builtins.int)) [case testRelativeImport0] import m.x @@ -637,8 +642,9 @@ MypyFile:1( MypyFile:1( tmp/m/z.py AssignmentStmt:1( - NameExpr(y* [m.z.y]) - IntExpr(1))) + NameExpr(y [m.z.y]) + IntExpr(1) + builtins.int)) [case testRelativeImport1] import m.t.b as b @@ -673,13 +679,15 @@ MypyFile:1( MypyFile:1( tmp/m/x.py AssignmentStmt:1( - NameExpr(y* [m.x.y]) - IntExpr(1))) + NameExpr(y [m.x.y]) + IntExpr(1) + builtins.int)) MypyFile:1( tmp/m/z.py AssignmentStmt:1( - NameExpr(y* [m.z.y]) - IntExpr(3))) + NameExpr(y [m.z.y]) + IntExpr(3) + builtins.int)) [case testRelativeImport2] import m.t.b as b @@ -712,13 +720,15 @@ MypyFile:1( MypyFile:1( tmp/m/x.py AssignmentStmt:1( - NameExpr(y* [m.x.y]) - IntExpr(1))) + NameExpr(y [m.x.y]) + IntExpr(1) + builtins.int)) MypyFile:1( tmp/m/z.py AssignmentStmt:1( - NameExpr(y* [m.z.y]) - IntExpr(3))) + NameExpr(y [m.z.y]) + IntExpr(3) + builtins.int)) [case testRelativeImport3] import m.t @@ -762,8 +772,9 @@ MypyFile:1( MypyFile:1( tmp/m/z.py AssignmentStmt:1( - NameExpr(zy* [m.z.zy]) - IntExpr(3))) + NameExpr(zy [m.z.zy]) + IntExpr(3) + builtins.int)) [case testRelativeImportFromSameModule] import m.x @@ -914,5 +925,6 @@ MypyFile:1( MypyFile:1( tmp/x.py AssignmentStmt:1( - NameExpr(a* [x.a]) - IntExpr(1))) + NameExpr(a [x.a]) + IntExpr(1) + builtins.int)) diff --git a/test-data/unit/semanal-python310.test b/test-data/unit/semanal-python310.test index a009636575dc..9418ac2912b2 100644 --- a/test-data/unit/semanal-python310.test +++ b/test-data/unit/semanal-python310.test @@ -8,8 +8,9 @@ match x: [out] MypyFile:1( AssignmentStmt:1( - NameExpr(x* [__main__.x]) - IntExpr(1)) + NameExpr(x [__main__.x]) + IntExpr(1) + builtins.int) MatchStmt:2( NameExpr(x [__main__.x]) Pattern( @@ -28,8 +29,9 @@ a [out] MypyFile:1( AssignmentStmt:1( - NameExpr(x* [__main__.x]) - IntExpr(1)) + NameExpr(x [__main__.x]) + IntExpr(1) + builtins.int) MatchStmt:2( NameExpr(x [__main__.x]) Pattern( @@ -49,8 +51,9 @@ match x: [out] MypyFile:1( AssignmentStmt:1( - NameExpr(x* [__main__.x]) - IntExpr(1)) + NameExpr(x [__main__.x]) + IntExpr(1) + builtins.int) MatchStmt:2( NameExpr(x [__main__.x]) Pattern( @@ -78,8 +81,9 @@ match x: [out] MypyFile:1( AssignmentStmt:1( - NameExpr(x* [__main__.x]) - IntExpr(1)) + NameExpr(x [__main__.x]) + IntExpr(1) + builtins.int) MatchStmt:2( NameExpr(x [__main__.x]) Pattern( @@ -99,8 +103,9 @@ match x: [out] MypyFile:1( AssignmentStmt:1( - NameExpr(x* [__main__.x]) - IntExpr(1)) + NameExpr(x [__main__.x]) + IntExpr(1) + builtins.int) MatchStmt:2( NameExpr(x [__main__.x]) Pattern( @@ -121,11 +126,13 @@ match x: [out] MypyFile:1( AssignmentStmt:1( - NameExpr(x* [__main__.x]) - IntExpr(1)) + NameExpr(x [__main__.x]) + IntExpr(1) + builtins.int) AssignmentStmt:2( - NameExpr(a* [__main__.a]) - IntExpr(1)) + NameExpr(a [__main__.a]) + IntExpr(1) + builtins.int) MatchStmt:3( NameExpr(x [__main__.x]) Pattern( @@ -144,8 +151,9 @@ match x: [out] MypyFile:1( AssignmentStmt:1( - NameExpr(x* [__main__.x]) - IntExpr(1)) + NameExpr(x [__main__.x]) + IntExpr(1) + builtins.int) MatchStmt:2( NameExpr(x [__main__.x]) Pattern( @@ -164,8 +172,9 @@ match x: [out] MypyFile:1( AssignmentStmt:1( - NameExpr(x* [__main__.x]) - IntExpr(1)) + NameExpr(x [__main__.x]) + IntExpr(1) + builtins.int) MatchStmt:2( NameExpr(x [__main__.x]) Pattern( @@ -191,8 +200,9 @@ b = 1 MypyFile:1( Import:1(_a) AssignmentStmt:3( - NameExpr(x* [__main__.x]) - IntExpr(1)) + NameExpr(x [__main__.x]) + IntExpr(1) + builtins.int) MatchStmt:4( NameExpr(x [__main__.x]) Pattern( diff --git a/test-data/unit/semanal-statements.test b/test-data/unit/semanal-statements.test index fdc5ca2bbbdd..27ff101c04d0 100644 --- a/test-data/unit/semanal-statements.test +++ b/test-data/unit/semanal-statements.test @@ -272,8 +272,9 @@ else: [out] MypyFile:1( AssignmentStmt:1( - NameExpr(x* [__main__.x]) - IntExpr(1)) + NameExpr(x [__main__.x]) + IntExpr(1) + builtins.int) IfStmt:2( If( NameExpr(x [__main__.x])) @@ -326,8 +327,9 @@ MypyFile:1( NameExpr(y* [__main__.y])) IntExpr(1)) AssignmentStmt:2( - NameExpr(xx* [__main__.xx]) - IntExpr(1)) + NameExpr(xx [__main__.xx]) + IntExpr(1) + builtins.int) AssignmentStmt:3( MemberExpr:3( NameExpr(x [__main__.x]) @@ -408,8 +410,9 @@ MypyFile:1( [out] MypyFile:1( AssignmentStmt:1( - NameExpr(x* [__main__.x]) - IntExpr(1)) + NameExpr(x [__main__.x]) + IntExpr(1) + builtins.int) AssignmentStmt:2( TupleExpr:2( NameExpr(y* [__main__.y])) @@ -436,8 +439,9 @@ y, x = 1 [out] MypyFile:1( AssignmentStmt:1( - NameExpr(x* [__main__.x]) - IntExpr(1)) + NameExpr(x [__main__.x]) + IntExpr(1) + builtins.int) AssignmentStmt:2( TupleExpr:2( NameExpr(y* [__main__.y]) @@ -450,8 +454,9 @@ y, (x, z) = 1 [out] MypyFile:1( AssignmentStmt:1( - NameExpr(x* [__main__.x]) - IntExpr(1)) + NameExpr(x [__main__.x]) + IntExpr(1) + builtins.int) AssignmentStmt:2( TupleExpr:2( NameExpr(y* [__main__.y]) @@ -468,8 +473,9 @@ if x: [out] MypyFile:1( AssignmentStmt:1( - NameExpr(x* [__main__.x]) - IntExpr(1)) + NameExpr(x [__main__.x]) + IntExpr(1) + builtins.int) IfStmt:2( If( NameExpr(x [__main__.x])) @@ -510,8 +516,9 @@ del x [out] MypyFile:1( AssignmentStmt:1( - NameExpr(x* [__main__.x]) - IntExpr(1)) + NameExpr(x [__main__.x]) + IntExpr(1) + builtins.int) DelStmt:2( NameExpr(x [__main__.x]))) @@ -961,16 +968,18 @@ MypyFile:1( Block:2( PassStmt:2())) AssignmentStmt:3( - NameExpr(x'* [__main__.x']) - IntExpr(0)) + NameExpr(x' [__main__.x']) + IntExpr(0) + builtins.int) ExpressionStmt:4( CallExpr:4( NameExpr(f [__main__.f]) Args( NameExpr(x' [__main__.x'])))) AssignmentStmt:5( - NameExpr(x* [__main__.x]) - StrExpr()) + NameExpr(x [__main__.x]) + StrExpr() + builtins.str) ExpressionStmt:6( CallExpr:6( NameExpr(f [__main__.f]) @@ -993,8 +1002,9 @@ MypyFile:1( Block:2( PassStmt:2())) AssignmentStmt:3( - NameExpr(x* [__main__.x]) - IntExpr(0)) + NameExpr(x [__main__.x]) + IntExpr(0) + builtins.int) ExpressionStmt:4( CallExpr:4( NameExpr(f [__main__.f]) @@ -1046,15 +1056,17 @@ x = '' [out] MypyFile:1( AssignmentStmt:2( - NameExpr(x* [__main__.x]) - IntExpr(0)) + NameExpr(x [__main__.x]) + IntExpr(0) + builtins.int) ExpressionStmt:3( NameExpr(x [__main__.x])) ClassDef:4( A AssignmentStmt:5( - NameExpr(x* [m]) - IntExpr(1))) + NameExpr(x [m]) + IntExpr(1) + builtins.int)) AssignmentStmt:6( NameExpr(x [__main__.x]) StrExpr())) @@ -1114,3 +1126,191 @@ MypyFile:1( AssignmentStmt:5( NameExpr(y [__main__.y]) IntExpr(1))))) + +[case testConstantFold1] +from typing_extensions import Final +add: Final = 15 + 47 +add_mul: Final = (2 + 3) * 5 +sub: Final = 7 - 11 +bit_and: Final = 6 & 10 +bit_or: Final = 6 | 10 +bit_xor: Final = 6 ^ 10 +lshift: Final = 5 << 2 +rshift: Final = 13 >> 2 +lshift0: Final = 5 << 0 +rshift0: Final = 13 >> 0 +[out] +MypyFile:1( + ImportFrom:1(typing_extensions, [Final]) + AssignmentStmt:2( + NameExpr(add [__main__.add] = 62) + OpExpr:2( + + + IntExpr(15) + IntExpr(47)) + Literal[62]?) + AssignmentStmt:3( + NameExpr(add_mul [__main__.add_mul] = 25) + OpExpr:3( + * + OpExpr:3( + + + IntExpr(2) + IntExpr(3)) + IntExpr(5)) + Literal[25]?) + AssignmentStmt:4( + NameExpr(sub [__main__.sub] = -4) + OpExpr:4( + - + IntExpr(7) + IntExpr(11)) + Literal[-4]?) + AssignmentStmt:5( + NameExpr(bit_and [__main__.bit_and] = 2) + OpExpr:5( + & + IntExpr(6) + IntExpr(10)) + Literal[2]?) + AssignmentStmt:6( + NameExpr(bit_or [__main__.bit_or] = 14) + OpExpr:6( + | + IntExpr(6) + IntExpr(10)) + Literal[14]?) + AssignmentStmt:7( + NameExpr(bit_xor [__main__.bit_xor] = 12) + OpExpr:7( + ^ + IntExpr(6) + IntExpr(10)) + Literal[12]?) + AssignmentStmt:8( + NameExpr(lshift [__main__.lshift] = 20) + OpExpr:8( + << + IntExpr(5) + IntExpr(2)) + Literal[20]?) + AssignmentStmt:9( + NameExpr(rshift [__main__.rshift] = 3) + OpExpr:9( + >> + IntExpr(13) + IntExpr(2)) + Literal[3]?) + AssignmentStmt:10( + NameExpr(lshift0 [__main__.lshift0] = 5) + OpExpr:10( + << + IntExpr(5) + IntExpr(0)) + Literal[5]?) + AssignmentStmt:11( + NameExpr(rshift0 [__main__.rshift0] = 13) + OpExpr:11( + >> + IntExpr(13) + IntExpr(0)) + Literal[13]?)) + +[case testConstantFold2] +from typing_extensions import Final +neg1: Final = -5 +neg2: Final = --1 +neg3: Final = -0 +pos: Final = +5 +inverted1: Final = ~0 +inverted2: Final = ~5 +inverted3: Final = ~3 +p0: Final = 3**0 +p1: Final = 3**5 +p2: Final = (-5)**3 +p3: Final = 0**0 +s: Final = 'x' + 'y' +[out] +MypyFile:1( + ImportFrom:1(typing_extensions, [Final]) + AssignmentStmt:2( + NameExpr(neg1 [__main__.neg1] = -5) + UnaryExpr:2( + - + IntExpr(5)) + Literal[-5]?) + AssignmentStmt:3( + NameExpr(neg2 [__main__.neg2] = 1) + UnaryExpr:3( + - + UnaryExpr:3( + - + IntExpr(1))) + Literal[1]?) + AssignmentStmt:4( + NameExpr(neg3 [__main__.neg3] = 0) + UnaryExpr:4( + - + IntExpr(0)) + Literal[0]?) + AssignmentStmt:5( + NameExpr(pos [__main__.pos] = 5) + UnaryExpr:5( + + + IntExpr(5)) + Literal[5]?) + AssignmentStmt:6( + NameExpr(inverted1 [__main__.inverted1] = -1) + UnaryExpr:6( + ~ + IntExpr(0)) + Literal[-1]?) + AssignmentStmt:7( + NameExpr(inverted2 [__main__.inverted2] = -6) + UnaryExpr:7( + ~ + IntExpr(5)) + Literal[-6]?) + AssignmentStmt:8( + NameExpr(inverted3 [__main__.inverted3] = -4) + UnaryExpr:8( + ~ + IntExpr(3)) + Literal[-4]?) + AssignmentStmt:9( + NameExpr(p0 [__main__.p0] = 1) + OpExpr:9( + ** + IntExpr(3) + IntExpr(0)) + Literal[1]?) + AssignmentStmt:10( + NameExpr(p1 [__main__.p1] = 243) + OpExpr:10( + ** + IntExpr(3) + IntExpr(5)) + Literal[243]?) + AssignmentStmt:11( + NameExpr(p2 [__main__.p2] = -125) + OpExpr:11( + ** + UnaryExpr:11( + - + IntExpr(5)) + IntExpr(3)) + Literal[-125]?) + AssignmentStmt:12( + NameExpr(p3 [__main__.p3] = 1) + OpExpr:12( + ** + IntExpr(0) + IntExpr(0)) + Literal[1]?) + AssignmentStmt:13( + NameExpr(s [__main__.s] = xy) + OpExpr:13( + + + StrExpr(x) + StrExpr(y)) + Literal['xy']?)) diff --git a/test-data/unit/semanal-symtable.test b/test-data/unit/semanal-symtable.test index bdf4f52ae5fc..c886080557b0 100644 --- a/test-data/unit/semanal-symtable.test +++ b/test-data/unit/semanal-symtable.test @@ -9,7 +9,7 @@ x = 1 [out] __main__: SymbolTable( - x : Gdef/Var (__main__.x)) + x : Gdef/Var (__main__.x) : builtins.int) [case testFuncDef] def f(): pass @@ -35,7 +35,7 @@ __main__: m : Gdef/MypyFile (m)) m: SymbolTable( - x : Gdef/Var (m.x)) + x : Gdef/Var (m.x) : builtins.int) [case testImportFromModule] from m import x @@ -49,7 +49,7 @@ __main__: m: SymbolTable( x : Gdef/TypeInfo (m.x) - y : Gdef/Var (m.y)) + y : Gdef/Var (m.y) : builtins.int) [case testImportAs] from m import x as xx @@ -63,7 +63,7 @@ __main__: m: SymbolTable( x : Gdef/TypeInfo (m.x) - y : Gdef/Var (m.y)) + y : Gdef/Var (m.y) : builtins.int) [case testFailingImports] from sys import non_existing1 # type: ignore @@ -80,7 +80,7 @@ __main__: non_existing4 : Gdef/Var (__main__.non_existing4) : Any) sys: SymbolTable( - platform : Gdef/Var (sys.platform) + platform : Gdef/Var (sys.platform) : builtins.str version_info : Gdef/Var (sys.version_info)) [case testDecorator] @@ -95,6 +95,6 @@ def g() -> None: [out] __main__: SymbolTable( - Callable : Gdef/Var (typing.Callable) + Callable : Gdef/Var (typing.Callable) : builtins.int dec : Gdef/FuncDef (__main__.dec) : def (f: def ()) -> def () g : Gdef/Decorator (__main__.g) : def ())