Closed
Description
Crash report
**
is weird in that the type of the result depends on the values of the inputs. The logic for int
/float
power is:
def type_of_pow(lhs: float, rhs: float) -> type[complex]:
if isinstance(lhs, int) and isinstance(rhs, int) and rhs >= 0:
return int
if lhs < 0 and not rhs.is_integer():
return complex
return float
However, our optimizer wrongly assumes:
def type_of_pow(lhs: float, rhs: float) -> type[float]:
if isinstance(lhs, int) and isinstance(rhs, int):
return int
return float
This means that tons of different poorly-chosen values can cause JIT code to crash:
import _testinternalcapi
import itertools
def f(a, b):
for _ in range(_testinternalcapi.TIER2_THRESHOLD):
a + b # Remove guards...
a ** b + a # ...BOOM!
for la, ra, lb, rb in itertools.product([1, -1, 1.0, -1.0, 0.5, -0.5], repeat=4):
f(la, ra)
f(lb, rb)
Normally we could just ignore the problem and produce an unknown type during abstract interpretation, but a **
containing at least one constant value is actually reasonably common (think x ** 2
, 2 ** n
, or s ** 0.5
).
We should probably teach the optimizer how to handle these properly.