| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,4 +1,5 @@ | |||
| 1 | 1 | import contextlib | |
| 2 | + import itertools | ||
| 2 | 3 | import sys | |
| 3 | 4 | import textwrap | |
| 4 | 5 | import unittest | |
@@ -1511,6 +1512,49 @@ def test_jit_error_pops(self): | |||
| 1511 | 1512 | with self.assertRaises(TypeError): | |
| 1512 | 1513 | {item for item in items} | |
| 1513 | 1514 | ||
| 1515 | + def test_power_type_depends_on_input_values(self): | ||
| 1516 | + template = textwrap.dedent(""" | ||
| 1517 | + import _testinternalcapi | ||
| 1518 | + | ||
| 1519 | + L, R, X, Y = {l}, {r}, {x}, {y} | ||
| 1520 | + | ||
| 1521 | + def check(actual: complex, expected: complex) -> None: | ||
| 1522 | + assert actual == expected, (actual, expected) | ||
| 1523 | + assert type(actual) is type(expected), (actual, expected) | ||
| 1524 | + | ||
| 1525 | + def f(l: complex, r: complex) -> None: | ||
| 1526 | + expected_local_local = pow(l, r) + pow(l, r) | ||
| 1527 | + expected_const_local = pow(L, r) + pow(L, r) | ||
| 1528 | + expected_local_const = pow(l, R) + pow(l, R) | ||
| 1529 | + expected_const_const = pow(L, R) + pow(L, R) | ||
| 1530 | + for _ in range(_testinternalcapi.TIER2_THRESHOLD): | ||
| 1531 | + # Narrow types: | ||
| 1532 | + l + l, r + r | ||
| 1533 | + # The powers produce results, and the addition is unguarded: | ||
| 1534 | + check(l ** r + l ** r, expected_local_local) | ||
| 1535 | + check(L ** r + L ** r, expected_const_local) | ||
| 1536 | + check(l ** R + l ** R, expected_local_const) | ||
| 1537 | + check(L ** R + L ** R, expected_const_const) | ||
| 1538 | + | ||
| 1539 | + # JIT for one pair of values... | ||
| 1540 | + f(L, R) | ||
| 1541 | + # ...then run with another: | ||
| 1542 | + f(X, Y) | ||
| 1543 | + """) | ||
| 1544 | + interesting = [ | ||
| 1545 | + (1, 1), # int ** int -> int | ||
| 1546 | + (1, -1), # int ** int -> float | ||
| 1547 | + (1.0, 1), # float ** int -> float | ||
| 1548 | + (1, 1.0), # int ** float -> float | ||
| 1549 | + (-1, 0.5), # int ** float -> complex | ||
| 1550 | + (1.0, 1.0), # float ** float -> float | ||
| 1551 | + (-1.0, 0.5), # float ** float -> complex | ||
| 1552 | + ] | ||
| 1553 | + for (l, r), (x, y) in itertools.product(interesting, repeat=2): | ||
| 1554 | + s = template.format(l=l, r=r, x=x, y=y) | ||
| 1555 | + with self.subTest(l=l, r=r, x=x, y=y): | ||
| 1556 | + script_helper.assert_python_ok("-c", s) | ||
| 1557 | + | ||
| 1514 | 1558 | ||
| 1515 | 1559 | def global_identity(x): | |
| 1516 | 1560 | return x | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,2 @@ | |||
| 1 | + Fix an issue where the experimental JIT may infer an incorrect result type | ||
| 2 | + for exponentiation (``**`` and ``**=``), leading to bugs or crashes. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -530,6 +530,8 @@ dummy_func( | |||
| 530 | 530 | pure op(_BINARY_OP_MULTIPLY_INT, (left, right -- res)) { | |
| 531 | 531 | PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); | |
| 532 | 532 | PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); | |
| 533 | + assert(PyLong_CheckExact(left_o)); | ||
| 534 | + assert(PyLong_CheckExact(right_o)); | ||
| 533 | 535 | ||
| 534 | 536 | STAT_INC(BINARY_OP, hit); | |
| 535 | 537 | PyObject *res_o = _PyLong_Multiply((PyLongObject *)left_o, (PyLongObject *)right_o); | |
@@ -543,6 +545,8 @@ dummy_func( | |||
| 543 | 545 | pure op(_BINARY_OP_ADD_INT, (left, right -- res)) { | |
| 544 | 546 | PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); | |
| 545 | 547 | PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); | |
| 548 | + assert(PyLong_CheckExact(left_o)); | ||
| 549 | + assert(PyLong_CheckExact(right_o)); | ||
| 546 | 550 | ||
| 547 | 551 | STAT_INC(BINARY_OP, hit); | |
| 548 | 552 | PyObject *res_o = _PyLong_Add((PyLongObject *)left_o, (PyLongObject *)right_o); | |
@@ -556,6 +560,8 @@ dummy_func( | |||
| 556 | 560 | pure op(_BINARY_OP_SUBTRACT_INT, (left, right -- res)) { | |
| 557 | 561 | PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); | |
| 558 | 562 | PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); | |
| 563 | + assert(PyLong_CheckExact(left_o)); | ||
| 564 | + assert(PyLong_CheckExact(right_o)); | ||
| 559 | 565 | ||
| 560 | 566 | STAT_INC(BINARY_OP, hit); | |
| 561 | 567 | PyObject *res_o = _PyLong_Subtract((PyLongObject *)left_o, (PyLongObject *)right_o); | |
@@ -593,6 +599,8 @@ dummy_func( | |||
| 593 | 599 | pure op(_BINARY_OP_MULTIPLY_FLOAT, (left, right -- res)) { | |
| 594 | 600 | PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); | |
| 595 | 601 | PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); | |
| 602 | + assert(PyFloat_CheckExact(left_o)); | ||
| 603 | + assert(PyFloat_CheckExact(right_o)); | ||
| 596 | 604 | ||
| 597 | 605 | STAT_INC(BINARY_OP, hit); | |
| 598 | 606 | double dres = | |
@@ -607,6 +615,8 @@ dummy_func( | |||
| 607 | 615 | pure op(_BINARY_OP_ADD_FLOAT, (left, right -- res)) { | |
| 608 | 616 | PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); | |
| 609 | 617 | PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); | |
| 618 | + assert(PyFloat_CheckExact(left_o)); | ||
| 619 | + assert(PyFloat_CheckExact(right_o)); | ||
| 610 | 620 | ||
| 611 | 621 | STAT_INC(BINARY_OP, hit); | |
| 612 | 622 | double dres = | |
@@ -621,6 +631,8 @@ dummy_func( | |||
| 621 | 631 | pure op(_BINARY_OP_SUBTRACT_FLOAT, (left, right -- res)) { | |
| 622 | 632 | PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); | |
| 623 | 633 | PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); | |
| 634 | + assert(PyFloat_CheckExact(left_o)); | ||
| 635 | + assert(PyFloat_CheckExact(right_o)); | ||
| 624 | 636 | ||
| 625 | 637 | STAT_INC(BINARY_OP, hit); | |
| 626 | 638 | double dres = | |
@@ -650,6 +662,8 @@ dummy_func( | |||
| 650 | 662 | pure op(_BINARY_OP_ADD_UNICODE, (left, right -- res)) { | |
| 651 | 663 | PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); | |
| 652 | 664 | PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); | |
| 665 | + assert(PyUnicode_CheckExact(left_o)); | ||
| 666 | + assert(PyUnicode_CheckExact(right_o)); | ||
| 653 | 667 | ||
| 654 | 668 | STAT_INC(BINARY_OP, hit); | |
| 655 | 669 | PyObject *res_o = PyUnicode_Concat(left_o, right_o); | |
@@ -672,6 +686,8 @@ dummy_func( | |||
| 672 | 686 | op(_BINARY_OP_INPLACE_ADD_UNICODE, (left, right --)) { | |
| 673 | 687 | PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); | |
| 674 | 688 | PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); | |
| 689 | + assert(PyUnicode_CheckExact(left_o)); | ||
| 690 | + assert(PyUnicode_CheckExact(right_o)); | ||
| 675 | 691 | ||
| 676 | 692 | int next_oparg; | |
| 677 | 693 | #if TIER_ONE | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -167,23 +167,56 @@ dummy_func(void) { | |||
| 167 | 167 | } | |
| 168 | 168 | ||
| 169 | 169 | op(_BINARY_OP, (left, right -- res)) { | |
| 170 | - PyTypeObject *ltype = sym_get_type(left); | ||
| 171 | - PyTypeObject *rtype = sym_get_type(right); | ||
| 172 | - if (ltype != NULL && (ltype == &PyLong_Type || ltype == &PyFloat_Type) && | ||
| 173 | - rtype != NULL && (rtype == &PyLong_Type || rtype == &PyFloat_Type)) | ||
| 174 | - { | ||
| 175 | - if (oparg != NB_TRUE_DIVIDE && oparg != NB_INPLACE_TRUE_DIVIDE && | ||
| 176 | - ltype == &PyLong_Type && rtype == &PyLong_Type) { | ||
| 177 | - /* If both inputs are ints and the op is not division the result is an int */ | ||
| 178 | - res = sym_new_type(ctx, &PyLong_Type); | ||
| 170 | + bool lhs_int = sym_matches_type(left, &PyLong_Type); | ||
| 171 | + bool rhs_int = sym_matches_type(right, &PyLong_Type); | ||
| 172 | + bool lhs_float = sym_matches_type(left, &PyFloat_Type); | ||
| 173 | + bool rhs_float = sym_matches_type(right, &PyFloat_Type); | ||
| 174 | + if (!((lhs_int || lhs_float) && (rhs_int || rhs_float))) { | ||
| 175 | + // There's something other than an int or float involved: | ||
| 176 | + res = sym_new_unknown(ctx); | ||
| 177 | + } | ||
| 178 | + else if (oparg == NB_POWER || oparg == NB_INPLACE_POWER) { | ||
| 179 | + // This one's fun... the *type* of the result depends on the | ||
| 180 | + // *values* being exponentiated. However, exponents with one | ||
| 181 | + // constant part are reasonably common, so it's probably worth | ||
| 182 | + // trying to infer some simple cases: | ||
| 183 | + // - A: 1 ** 1 -> 1 (int ** int -> int) | ||
| 184 | + // - B: 1 ** -1 -> 1.0 (int ** int -> float) | ||
| 185 | + // - C: 1.0 ** 1 -> 1.0 (float ** int -> float) | ||
| 186 | + // - D: 1 ** 1.0 -> 1.0 (int ** float -> float) | ||
| 187 | + // - E: -1 ** 0.5 ~> 1j (int ** float -> complex) | ||
| 188 | + // - F: 1.0 ** 1.0 -> 1.0 (float ** float -> float) | ||
| 189 | + // - G: -1.0 ** 0.5 ~> 1j (float ** float -> complex) | ||
| 190 | + if (rhs_float) { | ||
| 191 | + // Case D, E, F, or G... can't know without the sign of the LHS | ||
| 192 | + // or whether the RHS is whole, which isn't worth the effort: | ||
| 193 | + res = sym_new_unknown(ctx); | ||
| 179 | 194 | } | |
| 180 | - else { | ||
| 181 | - /* For any other op combining ints/floats the result is a float */ | ||
| 195 | + else if (lhs_float) { | ||
| 196 | + // Case C: | ||
| 182 | 197 | res = sym_new_type(ctx, &PyFloat_Type); | |
| 183 | 198 | } | |
| 199 | + else if (!sym_is_const(right)) { | ||
| 200 | + // Case A or B... can't know without the sign of the RHS: | ||
| 201 | + res = sym_new_unknown(ctx); | ||
| 202 | + } | ||
| 203 | + else if (_PyLong_IsNegative((PyLongObject *)sym_get_const(right))) { | ||
| 204 | + // Case B: | ||
| 205 | + res = sym_new_type(ctx, &PyFloat_Type); | ||
| 206 | + } | ||
| 207 | + else { | ||
| 208 | + // Case A: | ||
| 209 | + res = sym_new_type(ctx, &PyLong_Type); | ||
| 210 | + } | ||
| 211 | + } | ||
| 212 | + else if (oparg == NB_TRUE_DIVIDE || oparg == NB_INPLACE_TRUE_DIVIDE) { | ||
| 213 | + res = sym_new_type(ctx, &PyFloat_Type); | ||
| 214 | + } | ||
| 215 | + else if (lhs_int && rhs_int) { | ||
| 216 | + res = sym_new_type(ctx, &PyLong_Type); | ||
| 184 | 217 | } | |
| 185 | 218 | else { | |
| 186 | - res = sym_new_unknown(ctx); | ||
| 219 | + res = sym_new_type(ctx, &PyFloat_Type); | ||
| 187 | 220 | } | |
| 188 | 221 | } | |
| 189 | 222 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments