From 8750faa8fb93fff82835139173647276dcfec9a3 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Wed, 1 Oct 2025 23:53:15 +0000 Subject: [PATCH 1/6] [mypyc] feat: support constant folding in `convert_format_expr_to_str` --- mypyc/irbuild/format_str_tokenizer.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/mypyc/irbuild/format_str_tokenizer.py b/mypyc/irbuild/format_str_tokenizer.py index 5a35900006d2..c7560bc87bab 100644 --- a/mypyc/irbuild/format_str_tokenizer.py +++ b/mypyc/irbuild/format_str_tokenizer.py @@ -23,6 +23,7 @@ is_str_rprimitive, ) from mypyc.irbuild.builder import IRBuilder +from mypyc.irbuild.constant_fold import constant_fold_expr from mypyc.primitives.bytes_ops import bytes_build_op from mypyc.primitives.int_ops import int_to_str_op from mypyc.primitives.str_ops import str_build_op, str_op @@ -143,16 +144,18 @@ def convert_format_expr_to_str( for x, format_op in zip(exprs, format_ops): node_type = builder.node_type(x) if format_op == FormatOp.STR: - if is_str_rprimitive(node_type) or isinstance( - x, StrExpr - ): # NOTE: why does mypyc think our fake StrExprs are not str rprimitives? + if isinstance(folded := constant_fold_expr(builder, x), str): + var_str = builder.load_literal_value(folded) + elif is_str_rprimitive(node_type): var_str = builder.accept(x) elif is_int_rprimitive(node_type) or is_short_int_rprimitive(node_type): var_str = builder.primitive_op(int_to_str_op, [builder.accept(x)], line) else: var_str = builder.primitive_op(str_op, [builder.accept(x)], line) elif format_op == FormatOp.INT: - if is_int_rprimitive(node_type) or is_short_int_rprimitive(node_type): + if isinstance(folded := constant_fold_expr(builder, x), int): + var_str = builder.load_literal_value(str(folded)) + elif is_int_rprimitive(node_type) or is_short_int_rprimitive(node_type): var_str = builder.primitive_op(int_to_str_op, [builder.accept(x)], line) else: return None From 5bc0c10c43eb0309b2f56106a777a2213d88a4c7 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 00:08:19 +0000 Subject: [PATCH 2/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypyc/irbuild/format_str_tokenizer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/irbuild/format_str_tokenizer.py b/mypyc/irbuild/format_str_tokenizer.py index c7560bc87bab..2596e8e050a6 100644 --- a/mypyc/irbuild/format_str_tokenizer.py +++ b/mypyc/irbuild/format_str_tokenizer.py @@ -12,7 +12,7 @@ ) from mypy.errors import Errors from mypy.messages import MessageBuilder -from mypy.nodes import Context, Expression, StrExpr +from mypy.nodes import Context, Expression from mypy.options import Options from mypyc.ir.ops import Integer, Value from mypyc.ir.rtypes import ( From 4f9ddf6507d64241c0e32a167eb608a73e4cfa1b Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Tue, 6 Jan 2026 09:56:16 +0000 Subject: [PATCH 3/6] irtest --- mypyc/test-data/irbuild-constant-fold.test | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/mypyc/test-data/irbuild-constant-fold.test b/mypyc/test-data/irbuild-constant-fold.test index cd953c84c541..06831043ff10 100644 --- a/mypyc/test-data/irbuild-constant-fold.test +++ b/mypyc/test-data/irbuild-constant-fold.test @@ -186,6 +186,25 @@ L0: big5 = r4 return 1 +[case testConstantFoldFormatArgs] +# This only tests that the callee and args are constant folded, +# it is not intended to test the result. +from typing import Any, Final + +FMT: Final = "{:d} {}" + +def f() -> str: + return FMT.format(400 + 20, "roll" + "up") +[out] +def f(): + r0, r1, r2, r3 :: str +L0: + r0 = '420' + r1 = ' ' + r2 = 'rollup' + r3 = CPyStr_Build(3, r0, r1, r2) + return r3 + [case testIntConstantFoldingFinal] from typing import Final X: Final = 5 From 482624457c520362bf524de41fa1c8efa8bbb91d Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Tue, 6 Jan 2026 10:42:26 +0000 Subject: [PATCH 4/6] fix test --- mypyc/test-data/irbuild-constant-fold.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/test-data/irbuild-constant-fold.test b/mypyc/test-data/irbuild-constant-fold.test index 06831043ff10..803ce3b4d549 100644 --- a/mypyc/test-data/irbuild-constant-fold.test +++ b/mypyc/test-data/irbuild-constant-fold.test @@ -191,7 +191,7 @@ L0: # it is not intended to test the result. from typing import Any, Final -FMT: Final = "{:d} {}" +FMT: Final = "{} {}" def f() -> str: return FMT.format(400 + 20, "roll" + "up") From f07478a870480f651c6352d74db0f8c219d382aa Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Tue, 6 Jan 2026 11:02:46 +0000 Subject: [PATCH 5/6] fix test --- mypyc/test-data/irbuild-constant-fold.test | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mypyc/test-data/irbuild-constant-fold.test b/mypyc/test-data/irbuild-constant-fold.test index 803ce3b4d549..009a4e68f678 100644 --- a/mypyc/test-data/irbuild-constant-fold.test +++ b/mypyc/test-data/irbuild-constant-fold.test @@ -199,10 +199,10 @@ def f() -> str: def f(): r0, r1, r2, r3 :: str L0: - r0 = '420' - r1 = ' ' - r2 = 'rollup' - r3 = CPyStr_Build(3, r0, r1, r2) + r0 = CPyTagged_Str(840) + r1 = 'rollup' + r2 = ' ' + r3 = CPyStr_Build(3, r0, r2, r1) return r3 [case testIntConstantFoldingFinal] From 2ff69153271d9330413a4e742333ed78852e7b23 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Tue, 6 Jan 2026 18:14:15 -0400 Subject: [PATCH 6/6] Update run-strings.test --- mypyc/test-data/run-strings.test | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mypyc/test-data/run-strings.test b/mypyc/test-data/run-strings.test index 0ae67ed7f1c3..ea1015596ad5 100644 --- a/mypyc/test-data/run-strings.test +++ b/mypyc/test-data/run-strings.test @@ -1137,3 +1137,11 @@ def test_optional_ne() -> None: assert ne_s_opt_s_opt('y', 'x') assert ne_s_opt_s_opt(None, 'x') assert ne_s_opt_s_opt('x', None) + +[case testConstantFoldFormatArgs] +from typing import Final + +FMT: Final = "{} {}" + +def test_format() -> str: + assert FMT.format(400 + 20, "roll" + "up") == "420 rollup"