From 970cd4fa5254879bf0a95256a910dba9ac1e5f0c Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 02:18:54 -0400 Subject: [PATCH 01/30] [mypyc] feat: add IndexOp support to `constant_fold_expr` --- mypyc/irbuild/constant_fold.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mypyc/irbuild/constant_fold.py b/mypyc/irbuild/constant_fold.py index 12a4b15dd40c..cdff929224a0 100644 --- a/mypyc/irbuild/constant_fold.py +++ b/mypyc/irbuild/constant_fold.py @@ -72,6 +72,15 @@ def constant_fold_expr(builder: IRBuilder, expr: Expression) -> ConstantValue | value = constant_fold_expr(builder, expr.expr) if value is not None and not isinstance(value, bytes): return constant_fold_unary_op(expr.op, value) + elif isinstance(expr, IndexOp): + base = constant_fold_expr(builder, expr.base) + if base is not None: + index = constant_fold_expr(builder, expr.base) + if index is not None: + try: + return base[index] + except Exception: + return None return None From 51fdd2d75b568d1813b87044d52d2054416038a4 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 02:21:48 -0400 Subject: [PATCH 02/30] Update constant_fold.py --- mypy/constant_fold.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 4582b2a7396d..b0aede20cd1b 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -11,6 +11,7 @@ ComplexExpr, Expression, FloatExpr, + IndexExpr, IntExpr, NameExpr, OpExpr, @@ -73,6 +74,15 @@ def constant_fold_expr(expr: Expression, cur_mod_id: str) -> ConstantValue | Non value = constant_fold_expr(expr.expr, cur_mod_id) if value is not None: return constant_fold_unary_op(expr.op, value) + elif isinstance(expr, IndexExpr): + base = constant_fold_expr(expr.base, cur_mod_id) + if base is not None: + index = constant_fold_expr(expr.index, cur_mod_id) + if index is not None: + try: + return base[index] + except Exception: + return None return None From 4ad507aaf1c2a8b7c5e089e5524095b01a192adf Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 02:22:08 -0400 Subject: [PATCH 03/30] Update constant_fold.py --- mypyc/irbuild/constant_fold.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/constant_fold.py b/mypyc/irbuild/constant_fold.py index cdff929224a0..3fc1e2f04d16 100644 --- a/mypyc/irbuild/constant_fold.py +++ b/mypyc/irbuild/constant_fold.py @@ -18,6 +18,7 @@ ComplexExpr, Expression, FloatExpr, + IndexExpr, IntExpr, MemberExpr, NameExpr, @@ -72,7 +73,7 @@ def constant_fold_expr(builder: IRBuilder, expr: Expression) -> ConstantValue | value = constant_fold_expr(builder, expr.expr) if value is not None and not isinstance(value, bytes): return constant_fold_unary_op(expr.op, value) - elif isinstance(expr, IndexOp): + elif isinstance(expr, IndexExpr): base = constant_fold_expr(builder, expr.base) if base is not None: index = constant_fold_expr(builder, expr.base) From bed37d604c94d8f05ee35fdda0df2ad4a62d3808 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 02:38:14 -0400 Subject: [PATCH 04/30] Update constant_fold.py --- mypy/constant_fold.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index b0aede20cd1b..136ece0f0ee7 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -80,7 +80,7 @@ def constant_fold_expr(expr: Expression, cur_mod_id: str) -> ConstantValue | Non index = constant_fold_expr(expr.index, cur_mod_id) if index is not None: try: - return base[index] + return base[index] # type: ignore [index] except Exception: return None return None From d7f75f418533055bba978507b9ab3c1e552f85cd Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 02:38:34 -0400 Subject: [PATCH 05/30] Update constant_fold.py --- mypyc/irbuild/constant_fold.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/irbuild/constant_fold.py b/mypyc/irbuild/constant_fold.py index 3fc1e2f04d16..3e5a38d042bc 100644 --- a/mypyc/irbuild/constant_fold.py +++ b/mypyc/irbuild/constant_fold.py @@ -79,7 +79,7 @@ def constant_fold_expr(builder: IRBuilder, expr: Expression) -> ConstantValue | index = constant_fold_expr(builder, expr.base) if index is not None: try: - return base[index] + return base[index] # type: ignore [index] except Exception: return None return None From 558dee190efd2b5d723e98932704e5624bd47e67 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 03:32:58 -0400 Subject: [PATCH 06/30] Update constant_fold.py --- mypy/constant_fold.py | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 136ece0f0ee7..38a45e5dc021 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -77,12 +77,37 @@ def constant_fold_expr(expr: Expression, cur_mod_id: str) -> ConstantValue | Non elif isinstance(expr, IndexExpr): base = constant_fold_expr(expr.base, cur_mod_id) if base is not None: - index = constant_fold_expr(expr.index, cur_mod_id) - if index is not None: - try: - return base[index] # type: ignore [index] - except Exception: + index_expr = expr.index + if not isinstance(index_expr, SliceExpr): + index = constant_fold_expr(index_expr, cur_mod_id) + if index is not None: + try: + return base[index] # type: ignore [index] + except Exception: + return None + + if index_expr.begin_index is None: + begin_index = None + else: + begin_index = constant_fold_expr(index_expr.begin_index, cur_mod_id) + if begin_index is None: + return None + if index_expr.end_index is None: + end_index = None + else: + end_index = constant_fold_expr(index_expr.end_index, cur_mod_id) + if end_index is None: return None + if index_expr.stride is None: + stride = None + else: + stride = constant_fold_expr(index_expr.stride, cur_mod_id) + if stride is None: + return None + try: + return base[begin_index:end_index:stride] + except Exception: + return None return None From 3f18ed04257388b8bcd4d12b57d3b067abcb6a90 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 07:34:18 +0000 Subject: [PATCH 07/30] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/constant_fold.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 38a45e5dc021..e6619f4e86f3 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -85,7 +85,7 @@ def constant_fold_expr(expr: Expression, cur_mod_id: str) -> ConstantValue | Non return base[index] # type: ignore [index] except Exception: return None - + if index_expr.begin_index is None: begin_index = None else: From 692844c44ae3c462cc8330afc5d9e8aab0cfc5b4 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 03:36:09 -0400 Subject: [PATCH 08/30] Update constant_fold.py --- mypyc/irbuild/constant_fold.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/constant_fold.py b/mypyc/irbuild/constant_fold.py index 3e5a38d042bc..93680f290673 100644 --- a/mypyc/irbuild/constant_fold.py +++ b/mypyc/irbuild/constant_fold.py @@ -23,6 +23,7 @@ MemberExpr, NameExpr, OpExpr, + SliceExpr, StrExpr, UnaryExpr, Var, @@ -76,7 +77,32 @@ def constant_fold_expr(builder: IRBuilder, expr: Expression) -> ConstantValue | elif isinstance(expr, IndexExpr): base = constant_fold_expr(builder, expr.base) if base is not None: - index = constant_fold_expr(builder, expr.base) + index_expr = expr.index + if isinstance(index_expr, SliceExpr): + if index_expr.begin_index is None: + begin_index = None + else: + begin_index = constant_fold_expr(builder, index_expr.begin_index) + if begin_index is None: + return None + if index_expr.end_index is None: + end_index = None + else: + end_index = constant_fold_expr(builder, index_expr.end_index) + if end_index is None: + return None + if index_expr.stride is None: + stride = None + else: + stride = constant_fold_expr(builder, index_expr.stride) + if stride is None: + return None + try: + return base[begin_index:end_index:stride] + except Exception: + return None + + index = constant_fold_expr(builder, index_expr) if index is not None: try: return base[index] # type: ignore [index] From 869616ca412990ab1eb5a18abd5b823d754c267a Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 03:37:13 -0400 Subject: [PATCH 09/30] Update constant_fold.py --- mypy/constant_fold.py | 54 +++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index e6619f4e86f3..dc3e91c38468 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -78,36 +78,36 @@ def constant_fold_expr(expr: Expression, cur_mod_id: str) -> ConstantValue | Non base = constant_fold_expr(expr.base, cur_mod_id) if base is not None: index_expr = expr.index - if not isinstance(index_expr, SliceExpr): - index = constant_fold_expr(index_expr, cur_mod_id) - if index is not None: - try: - return base[index] # type: ignore [index] - except Exception: + if isinstance(index_expr, SliceExpr): + if index_expr.begin_index is None: + begin_index = None + else: + begin_index = constant_fold_expr(index_expr.begin_index, cur_mod_id) + if begin_index is None: return None - - if index_expr.begin_index is None: - begin_index = None - else: - begin_index = constant_fold_expr(index_expr.begin_index, cur_mod_id) - if begin_index is None: - return None - if index_expr.end_index is None: - end_index = None - else: - end_index = constant_fold_expr(index_expr.end_index, cur_mod_id) - if end_index is None: + if index_expr.end_index is None: + end_index = None + else: + end_index = constant_fold_expr(index_expr.end_index, cur_mod_id) + if end_index is None: + return None + if index_expr.stride is None: + stride = None + else: + stride = constant_fold_expr(index_expr.stride, cur_mod_id) + if stride is None: + return None + try: + return base[begin_index:end_index:stride] + except Exception: return None - if index_expr.stride is None: - stride = None - else: - stride = constant_fold_expr(index_expr.stride, cur_mod_id) - if stride is None: + + index = constant_fold_expr(index_expr, cur_mod_id) + if index is not None: + try: + return base[index] # type: ignore [index] + except Exception: return None - try: - return base[begin_index:end_index:stride] - except Exception: - return None return None From 22651fe52d14ae81040ddbea28fc3a1a253af469 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 07:37:30 +0000 Subject: [PATCH 10/30] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/constant_fold.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/irbuild/constant_fold.py b/mypyc/irbuild/constant_fold.py index 93680f290673..d3fa7a13cbf7 100644 --- a/mypyc/irbuild/constant_fold.py +++ b/mypyc/irbuild/constant_fold.py @@ -101,7 +101,7 @@ def constant_fold_expr(builder: IRBuilder, expr: Expression) -> ConstantValue | return base[begin_index:end_index:stride] except Exception: return None - + index = constant_fold_expr(builder, index_expr) if index is not None: try: From 9bd78f3743e0d6f26d846f2469dd20abaabdaad3 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 03:43:04 -0400 Subject: [PATCH 11/30] Update constant_fold.py --- mypyc/irbuild/constant_fold.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/irbuild/constant_fold.py b/mypyc/irbuild/constant_fold.py index d3fa7a13cbf7..8179c8efdfe1 100644 --- a/mypyc/irbuild/constant_fold.py +++ b/mypyc/irbuild/constant_fold.py @@ -98,7 +98,7 @@ def constant_fold_expr(builder: IRBuilder, expr: Expression) -> ConstantValue | if stride is None: return None try: - return base[begin_index:end_index:stride] + return base[begin_index:end_index:stride] # type: ignore [index, misc] except Exception: return None From 60574fc9b9336cb9d694671d28e82181d4b923dc Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 03:43:20 -0400 Subject: [PATCH 12/30] Update constant_fold.py --- mypy/constant_fold.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index dc3e91c38468..1834f8a0d837 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -98,7 +98,7 @@ def constant_fold_expr(expr: Expression, cur_mod_id: str) -> ConstantValue | Non if stride is None: return None try: - return base[begin_index:end_index:stride] + return base[begin_index:end_index:stride] # type: ignore [index, misc] except Exception: return None From daeaa50d93da30d29d4c120cbdc851442f1ecbde Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 07:44:24 +0000 Subject: [PATCH 13/30] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/constant_fold.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 1834f8a0d837..35e23fc2e5aa 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -101,7 +101,7 @@ def constant_fold_expr(expr: Expression, cur_mod_id: str) -> ConstantValue | Non return base[begin_index:end_index:stride] # type: ignore [index, misc] except Exception: return None - + index = constant_fold_expr(index_expr, cur_mod_id) if index is not None: try: From 785c2214e847805f5a9f2e41848f9efef5603f4a Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 03:51:40 -0400 Subject: [PATCH 14/30] Update constant_fold.py --- mypy/constant_fold.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 35e23fc2e5aa..e98041b69b05 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -15,6 +15,7 @@ IntExpr, NameExpr, OpExpr, + SliceExpr, StrExpr, UnaryExpr, Var, From 042f72683b6c3d18df5a368b34e204413377d6ed Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 14:47:56 -0400 Subject: [PATCH 15/30] Update irbuild-constant-fold.test --- mypyc/test-data/irbuild-constant-fold.test | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/mypyc/test-data/irbuild-constant-fold.test b/mypyc/test-data/irbuild-constant-fold.test index cd953c84c541..723871f1be11 100644 --- a/mypyc/test-data/irbuild-constant-fold.test +++ b/mypyc/test-data/irbuild-constant-fold.test @@ -478,3 +478,16 @@ L0: r3 = (-1.5+2j) neg_2 = r3 return 1 + +[case testIndexExprConstantFolding] +from typing import Final + +long_string: Final = "long string" + +def f() -> None: + a = long_string[5:] +[out] +def f(): + a :: str +L0: + a = "string" From fe2f670998780030bc20804d59f7e09399b5a281 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 14:52:22 -0400 Subject: [PATCH 16/30] Update irbuild-constant-fold.test --- mypyc/test-data/irbuild-constant-fold.test | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/mypyc/test-data/irbuild-constant-fold.test b/mypyc/test-data/irbuild-constant-fold.test index 723871f1be11..fe5520a3f66f 100644 --- a/mypyc/test-data/irbuild-constant-fold.test +++ b/mypyc/test-data/irbuild-constant-fold.test @@ -484,10 +484,22 @@ from typing import Final long_string: Final = "long string" -def f() -> None: +def pos_index() -> None: + a = long_string[5] +def neg_index() -> None: + a = long_string[-5] +def slice_index() -> None: a = long_string[5:] [out] -def f(): +def pos_index(): + a :: str +L0: + a = "s" +def neg_index(): + a :: str +L0: + a = "t" +def slice_index(): a :: str L0: a = "string" From 377b31e3c839c9759829ed146b42ded62de058a8 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 15:27:22 -0400 Subject: [PATCH 17/30] refactor --- mypyc/irbuild/expression.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 59ecc4ac2c5c..6021c0e5d69c 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -113,6 +113,9 @@ from mypyc.primitives.str_ops import str_slice_op from mypyc.primitives.tuple_ops import list_tuple_op, tuple_slice_op +TransformFunc = Callable[[IRBuilder, Expression], Value | None] + + # Name and attribute references @@ -527,11 +530,8 @@ def translate_cast_expr(builder: IRBuilder, expr: CastExpr) -> Value: # Operators +@folding_candidate def transform_unary_expr(builder: IRBuilder, expr: UnaryExpr) -> Value: - folded = try_constant_fold(builder, expr) - if folded: - return folded - return builder.unary_op(builder.accept(expr.expr), expr.op, expr.line) @@ -582,6 +582,7 @@ def try_optimize_int_floor_divide(builder: IRBuilder, expr: OpExpr) -> OpExpr: return expr +@folding_candidate def transform_index_expr(builder: IRBuilder, expr: IndexExpr) -> Value: index = expr.index base_type = builder.node_type(expr.base) @@ -615,6 +616,19 @@ def try_constant_fold(builder: IRBuilder, expr: Expression) -> Value | None: return None +def folding_candidate(transform: TransformFunc) -> TransformFunc: + """Mark a transform function as a candidate for constant folding. + + Candidate functions will attempt to short-circuit the transformation + by constant folding the expression and will only proceed to transform + the expression if folding is not possible. + """ + def constant_fold_wrap(builder: IRBuilder, expr: Expression) -> Value | None: + folded = try_constant_fold(builder, expr) + return folded if folded is not None else transform(builder, expr) + return constant_fold_wrap + + def try_gen_slice_op(builder: IRBuilder, base: Value, index: SliceExpr) -> Value | None: """Generate specialized slice op for some index expressions. From adae39f132fa4becdcf9a04c7e080b3853933755 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 15:28:59 -0400 Subject: [PATCH 18/30] Update expression.py --- mypyc/irbuild/expression.py | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/mypyc/irbuild/expression.py b/mypyc/irbuild/expression.py index 6021c0e5d69c..8b4d6fcb1051 100644 --- a/mypyc/irbuild/expression.py +++ b/mypyc/irbuild/expression.py @@ -83,7 +83,7 @@ ) from mypyc.irbuild.ast_helpers import is_borrow_friendly_expr, process_conditional from mypyc.irbuild.builder import IRBuilder, int_borrow_friendly_op -from mypyc.irbuild.constant_fold import constant_fold_expr +from mypyc.irbuild.constant_fold import constant_fold_expr, folding_candidate, try_constant_fold from mypyc.irbuild.for_helpers import ( comprehension_helper, raise_error_if_contains_unreachable_names, @@ -113,9 +113,6 @@ from mypyc.primitives.str_ops import str_slice_op from mypyc.primitives.tuple_ops import list_tuple_op, tuple_slice_op -TransformFunc = Callable[[IRBuilder, Expression], Value | None] - - # Name and attribute references @@ -605,30 +602,6 @@ def transform_index_expr(builder: IRBuilder, expr: IndexExpr) -> Value: ) -def try_constant_fold(builder: IRBuilder, expr: Expression) -> Value | None: - """Return the constant value of an expression if possible. - - Return None otherwise. - """ - value = constant_fold_expr(builder, expr) - if value is not None: - return builder.load_literal_value(value) - return None - - -def folding_candidate(transform: TransformFunc) -> TransformFunc: - """Mark a transform function as a candidate for constant folding. - - Candidate functions will attempt to short-circuit the transformation - by constant folding the expression and will only proceed to transform - the expression if folding is not possible. - """ - def constant_fold_wrap(builder: IRBuilder, expr: Expression) -> Value | None: - folded = try_constant_fold(builder, expr) - return folded if folded is not None else transform(builder, expr) - return constant_fold_wrap - - def try_gen_slice_op(builder: IRBuilder, base: Value, index: SliceExpr) -> Value | None: """Generate specialized slice op for some index expressions. From 35b0dfcd24aca7c650608df4b1659767fd8478fa Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 15:30:26 -0400 Subject: [PATCH 19/30] Update constant_fold.py --- mypyc/irbuild/constant_fold.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/mypyc/irbuild/constant_fold.py b/mypyc/irbuild/constant_fold.py index ce07dfe87624..253fc80b3356 100644 --- a/mypyc/irbuild/constant_fold.py +++ b/mypyc/irbuild/constant_fold.py @@ -131,3 +131,29 @@ def constant_fold_binary_op_extended( return left * right return None + + +def try_constant_fold(builder: IRBuilder, expr: Expression) -> Value | None: + """Return the constant value of an expression if possible. + + Return None otherwise. + """ + value = constant_fold_expr(builder, expr) + if value is not None: + return builder.load_literal_value(value) + return None + + +def folding_candidate( + transform: Callable[[IRBuilder, Expression], Value | None], +) -> Callable[[IRBuilder, Expression], Value | None]: + """Mark a transform function as a candidate for constant folding. + + Candidate functions will attempt to short-circuit the transformation + by constant folding the expression and will only proceed to transform + the expression if folding is not possible. + """ + def constant_fold_wrap(builder: IRBuilder, expr: Expression) -> Value | None: + folded = try_constant_fold(builder, expr) + return folded if folded is not None else transform(builder, expr) + return constant_fold_wrap From 6a25595bf6c406975168d279312dade1e48a9bd1 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 15:30:43 -0400 Subject: [PATCH 20/30] Update constant_fold.py --- mypyc/irbuild/constant_fold.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/irbuild/constant_fold.py b/mypyc/irbuild/constant_fold.py index 253fc80b3356..e95f8e5278df 100644 --- a/mypyc/irbuild/constant_fold.py +++ b/mypyc/irbuild/constant_fold.py @@ -10,7 +10,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Final, Union +from typing import TYPE_CHECKING, Callable, Final, Union from mypy.constant_fold import constant_fold_binary_op, constant_fold_unary_op from mypy.nodes import ( From 8add115bf132860a307136cf9ec50733f70e3822 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 15:31:15 -0400 Subject: [PATCH 21/30] Update constant_fold.py --- mypyc/irbuild/constant_fold.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mypyc/irbuild/constant_fold.py b/mypyc/irbuild/constant_fold.py index e95f8e5278df..f0d825cb898f 100644 --- a/mypyc/irbuild/constant_fold.py +++ b/mypyc/irbuild/constant_fold.py @@ -28,6 +28,7 @@ UnaryExpr, Var, ) +from mypyc.ir.ops import Value from mypyc.irbuild.util import bytes_from_str if TYPE_CHECKING: From 286a108d981ff600875786e1725e8ae0059d1521 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 19:32:39 +0000 Subject: [PATCH 22/30] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/constant_fold.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mypyc/irbuild/constant_fold.py b/mypyc/irbuild/constant_fold.py index f0d825cb898f..502d87e34161 100644 --- a/mypyc/irbuild/constant_fold.py +++ b/mypyc/irbuild/constant_fold.py @@ -154,7 +154,9 @@ def folding_candidate( by constant folding the expression and will only proceed to transform the expression if folding is not possible. """ + def constant_fold_wrap(builder: IRBuilder, expr: Expression) -> Value | None: folded = try_constant_fold(builder, expr) return folded if folded is not None else transform(builder, expr) + return constant_fold_wrap From 0a143598c3b943034d5ce165b412aff997c9f855 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 15:38:07 -0400 Subject: [PATCH 23/30] Update constant_fold.py --- mypyc/irbuild/constant_fold.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mypyc/irbuild/constant_fold.py b/mypyc/irbuild/constant_fold.py index 502d87e34161..f6e8683f5082 100644 --- a/mypyc/irbuild/constant_fold.py +++ b/mypyc/irbuild/constant_fold.py @@ -146,8 +146,8 @@ def try_constant_fold(builder: IRBuilder, expr: Expression) -> Value | None: def folding_candidate( - transform: Callable[[IRBuilder, Expression], Value | None], -) -> Callable[[IRBuilder, Expression], Value | None]: + transform: Callable[[IRBuilder, Expression], Value], +) -> Callable[[IRBuilder, Expression], Value]: """Mark a transform function as a candidate for constant folding. Candidate functions will attempt to short-circuit the transformation @@ -155,7 +155,7 @@ def folding_candidate( the expression if folding is not possible. """ - def constant_fold_wrap(builder: IRBuilder, expr: Expression) -> Value | None: + def constant_fold_wrap(builder: IRBuilder, expr: Expression) -> Value: folded = try_constant_fold(builder, expr) return folded if folded is not None else transform(builder, expr) From 2d768df1498dd73789c5cf05a49b33c366df93c3 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 15:44:30 -0400 Subject: [PATCH 24/30] Update constant_fold.py --- mypyc/irbuild/constant_fold.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mypyc/irbuild/constant_fold.py b/mypyc/irbuild/constant_fold.py index f6e8683f5082..5efb67ff57da 100644 --- a/mypyc/irbuild/constant_fold.py +++ b/mypyc/irbuild/constant_fold.py @@ -38,6 +38,8 @@ ConstantValue = Union[int, float, complex, str, bytes] CONST_TYPES: Final = (int, float, complex, str, bytes) +E = TypeVar("E", Expression) + def constant_fold_expr(builder: IRBuilder, expr: Expression) -> ConstantValue | None: """Return the constant value of an expression for supported operations. @@ -146,8 +148,8 @@ def try_constant_fold(builder: IRBuilder, expr: Expression) -> Value | None: def folding_candidate( - transform: Callable[[IRBuilder, Expression], Value], -) -> Callable[[IRBuilder, Expression], Value]: + transform: Callable[[IRBuilder, E], Value], +) -> Callable[[IRBuilder, E], Value]: """Mark a transform function as a candidate for constant folding. Candidate functions will attempt to short-circuit the transformation @@ -155,7 +157,7 @@ def folding_candidate( the expression if folding is not possible. """ - def constant_fold_wrap(builder: IRBuilder, expr: Expression) -> Value: + def constant_fold_wrap(builder: IRBuilder, expr: E) -> Value: folded = try_constant_fold(builder, expr) return folded if folded is not None else transform(builder, expr) From 4bcd5d6bd4f25812e462adcf3512e30ea978d5a8 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 15:46:19 -0400 Subject: [PATCH 25/30] Update constant_fold.py --- mypyc/irbuild/constant_fold.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/irbuild/constant_fold.py b/mypyc/irbuild/constant_fold.py index 5efb67ff57da..db29cef2ee32 100644 --- a/mypyc/irbuild/constant_fold.py +++ b/mypyc/irbuild/constant_fold.py @@ -10,7 +10,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Callable, Final, Union +from typing import TYPE_CHECKING, Callable, Final, TypeVar, Union from mypy.constant_fold import constant_fold_binary_op, constant_fold_unary_op from mypy.nodes import ( From a62ef3a6d9bc6a704397633556a1d409d5f3d57e Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 15:47:10 -0400 Subject: [PATCH 26/30] Update constant_fold.py --- mypyc/irbuild/constant_fold.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mypyc/irbuild/constant_fold.py b/mypyc/irbuild/constant_fold.py index db29cef2ee32..1d8c93750c3c 100644 --- a/mypyc/irbuild/constant_fold.py +++ b/mypyc/irbuild/constant_fold.py @@ -38,7 +38,7 @@ ConstantValue = Union[int, float, complex, str, bytes] CONST_TYPES: Final = (int, float, complex, str, bytes) -E = TypeVar("E", Expression) +Expr = TypeVar("Expr", Expression) def constant_fold_expr(builder: IRBuilder, expr: Expression) -> ConstantValue | None: @@ -148,8 +148,8 @@ def try_constant_fold(builder: IRBuilder, expr: Expression) -> Value | None: def folding_candidate( - transform: Callable[[IRBuilder, E], Value], -) -> Callable[[IRBuilder, E], Value]: + transform: Callable[[IRBuilder, Expr], Value], +) -> Callable[[IRBuilder, Expr], Value]: """Mark a transform function as a candidate for constant folding. Candidate functions will attempt to short-circuit the transformation @@ -157,7 +157,7 @@ def folding_candidate( the expression if folding is not possible. """ - def constant_fold_wrap(builder: IRBuilder, expr: E) -> Value: + def constant_fold_wrap(builder: IRBuilder, expr: Expr) -> Value: folded = try_constant_fold(builder, expr) return folded if folded is not None else transform(builder, expr) From 3ff12ecbf51162cdf60a0f5455fea882f19c3aed Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 15:49:24 -0400 Subject: [PATCH 27/30] Update constant_fold.py --- mypyc/irbuild/constant_fold.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/irbuild/constant_fold.py b/mypyc/irbuild/constant_fold.py index 1d8c93750c3c..c4c8ea21a85c 100644 --- a/mypyc/irbuild/constant_fold.py +++ b/mypyc/irbuild/constant_fold.py @@ -38,7 +38,7 @@ ConstantValue = Union[int, float, complex, str, bytes] CONST_TYPES: Final = (int, float, complex, str, bytes) -Expr = TypeVar("Expr", Expression) +Expr = TypeVar("Expr", bound=Expression) def constant_fold_expr(builder: IRBuilder, expr: Expression) -> ConstantValue | None: From 45bcc665cd22cc5b57683898a65362e4c9ff2716 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 16:00:25 -0400 Subject: [PATCH 28/30] Update irbuild-constant-fold.test --- mypyc/test-data/irbuild-constant-fold.test | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/mypyc/test-data/irbuild-constant-fold.test b/mypyc/test-data/irbuild-constant-fold.test index fe5520a3f66f..621874e70ef4 100644 --- a/mypyc/test-data/irbuild-constant-fold.test +++ b/mypyc/test-data/irbuild-constant-fold.test @@ -492,14 +492,20 @@ def slice_index() -> None: a = long_string[5:] [out] def pos_index(): - a :: str + r0, a :: str L0: - a = "s" + r0 = "s" + a = r0 + return 1 def neg_index(): - a :: str + r0, a :: str L0: - a = "t" + r0 = "t" + a = r0 + return 1 def slice_index(): - a :: str + r0, a :: str L0: - a = "string" + r0 = "string" + a = r0 + return 1 From e2fa664f6c69e209345d98439d6ddcaf18c33823 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 16:26:41 -0400 Subject: [PATCH 29/30] Update run-strings.test --- mypyc/test-data/run-strings.test | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test index 6a62db6ee3ee..22f3a5906f66 100644 --- a/mypyc/test-data/run-strings.test +++ b/mypyc/test-data/run-strings.test @@ -151,7 +151,7 @@ def test_unicode() -> None: assert ne("\U0001f4a9foo", "\U0001f4a8foo" + str()) [case testStringOps] -from typing import List, Optional, Tuple +from typing import Final, List, Optional, Tuple from testutil import assertRaises def do_split(s: str, sep: Optional[str] = None, max_split: Optional[int] = None) -> List[str]: @@ -226,6 +226,12 @@ def contains(s: str, o: str) -> bool: def getitem(s: str, index: int) -> str: return s[index] +final_string: Final = "abc" +final_int: Final = 1 + +def getitem_folded() -> str: + return final_string[final_int] + final_string[-1] + def find(s: str, substr: str, start: Optional[int] = None, end: Optional[int] = None) -> int: if start is not None: if end is not None: @@ -263,6 +269,7 @@ def test_getitem() -> None: getitem(s, 4) with assertRaises(IndexError, "string index out of range"): getitem(s, -4) + assert getitem_folded() == "bc" def test_find() -> None: s = "abcab" From 27193d882391fc8a9a5f694ff0eb97342c1808f6 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 2 Oct 2025 16:41:44 -0400 Subject: [PATCH 30/30] Update irbuild-constant-fold.test --- mypyc/test-data/irbuild-constant-fold.test | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mypyc/test-data/irbuild-constant-fold.test b/mypyc/test-data/irbuild-constant-fold.test index 621874e70ef4..aca649be7d28 100644 --- a/mypyc/test-data/irbuild-constant-fold.test +++ b/mypyc/test-data/irbuild-constant-fold.test @@ -494,18 +494,18 @@ def slice_index() -> None: def pos_index(): r0, a :: str L0: - r0 = "s" + r0 = 's' a = r0 return 1 def neg_index(): r0, a :: str L0: - r0 = "t" + r0 = 't' a = r0 return 1 def slice_index(): r0, a :: str L0: - r0 = "string" + r0 = 'string' a = r0 return 1