Constant fold more unary and binary expressions#15202
Conversation
Unfortunately mypy can't easily support storing the bytes value for final references. Other than preventing b"foo" + CONST_BYTES from being folded, it also means this commit is mypyc-only.
| try: | ||
| ret = left**right | ||
| except OverflowError: | ||
| return None |
There was a problem hiding this comment.
Digging through the CPython source, only power ops can raise an OverflowError.
|
|
||
| value = constant_fold_expr(rvalue, self.cur_mod_id) | ||
| if value is None: | ||
| if value is None or isinstance(value, complex): |
There was a problem hiding this comment.
I have no idea whether complex literals make any sense in mypy. PTAL.
| value = node.final_value | ||
| if isinstance(value, (CONST_TYPES)): | ||
| return value | ||
| final_value = node.final_value |
There was a problem hiding this comment.
The value local name should support ConstantValue | None, but Var.final_value does not support bytes. To avoid causing type errors later in the function from this assignment implicitly setting value's type, these variables were renamed.
| real = 1 | ||
| return 5j+real |
There was a problem hiding this comment.
The intent of this change is to preserve the old test case behavior where constant folding wasn't performed.
| div = 2.0 / 0.0 | ||
| floor_div = 2.0 // 0.0 | ||
| power_imag = (-2.0)**0.5 | ||
| power_overflow = 2.0**10000.0 |
There was a problem hiding this comment.
While floats usually don't raise an error on overflow, this is the one exception I know of. This is why there's a guard against OverflowError in mypy/constant_fold.py.
There was a problem hiding this comment.
A good catch! We could also generate a compile-time error, but it's not important.
This comment has been minimized.
This comment has been minimized.
JukkaL
left a comment
There was a problem hiding this comment.
Great, thanks! Finally got around to reviewing this. Looks good, just a few minor comments.
With these changes we probably have pretty much all the constant folding we need in the medium term at least. math constant support in final expressions might still be a good thing to have at some point, if it doesn't work yet (e.g. X: Final = math.pi * 2, see #15324).
| if right != 0: | ||
| return left % right | ||
| elif op == "**": | ||
| if (left < 0 and right >= 1 or right == 0) or (left >= 0 and right >= 0): |
There was a problem hiding this comment.
This could return a complex number, but the return type doesn't include complex (not sure if supporting complex results is worth it):
>>> (-1.2) ** 1.5
(-2.414760036730213e-16-1.3145341380123985j)
I used this fragment to look for other interesting cases, maybe it's helpful here:
values = -1.5, -1.0, -0.5, 0.0, 0.5, 1.0, 1.5
for x in values:
for y in values:
try:
print(x, y, x**y)
except Exception:
print(f'error: {x} ** {y}')| add_mul = (1.5 + 3.5) * 5.0 | ||
| sub = 7.0 - 7.5 | ||
| div = 3.0 / 2.0 | ||
| floor_div = 3.0 // 2.0 |
There was a problem hiding this comment.
Test with negative operands as well?
| div = 2.0 / 0.0 | ||
| floor_div = 2.0 // 0.0 | ||
| power_imag = (-2.0)**0.5 | ||
| power_overflow = 2.0**10000.0 |
There was a problem hiding this comment.
A good catch! We could also generate a compile-time error, but it's not important.
| from typing_extensions import Final | ||
|
|
||
| N: Final = 1 | ||
| FLOAT_N: Final = 1.5 |
There was a problem hiding this comment.
Is there a test case specific to a final float? If not, it would be good to add. Also test complex initializers such as N: Final = 1.5 * 2.
|
According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅ |
JukkaL
left a comment
There was a problem hiding this comment.
Thanks for the updates! LGTM
(#14838 had a lot of conflicts and it was easier to fix them in a new PR)
Now mypy can constant fold these additional operations:
While this can be useful with literal types, the main goal is to improve constant folding in mypyc (mypyc/mypyc#772).
mypyc can also fold bytes addition and multiplication, but mypy cannot as byte values can't be easily stored anywhere.