From f7640fc9d8e03cef7a494a375d3d85b97d5a9844 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Mon, 20 Oct 2025 11:32:15 -0400 Subject: [PATCH 1/6] [mypyc] feat: try constant fold 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..dbbb864d8804 100644 --- a/mypyc/irbuild/format_str_tokenizer.py +++ b/mypyc/irbuild/format_str_tokenizer.py @@ -143,16 +143,19 @@ 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 is_str_rprimitive(node_type) or isinstance(x, StrExpr): + # NOTE: why does mypyc think our fake StrExprs are not str rprimitives? var_str = builder.accept(x) + elif (folded := constant_fold_expr(builder, x)) is not None: + var_str = builder.accept(StrExpr(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: 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.accept(StrExpr(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 d63eafc0de765a99149a5a6c0193cb966383beb4 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Mon, 20 Oct 2025 11:33:19 -0400 Subject: [PATCH 2/6] Update format_str_tokenizer.py --- mypyc/irbuild/format_str_tokenizer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mypyc/irbuild/format_str_tokenizer.py b/mypyc/irbuild/format_str_tokenizer.py index dbbb864d8804..1284c99f5c74 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 From 58682c40655a781cb710ffda72d77d948cbb397a Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Mon, 20 Oct 2025 18:31:10 -0400 Subject: [PATCH 3/6] Update irbuild-str.test --- mypyc/test-data/irbuild-str.test | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mypyc/test-data/irbuild-str.test b/mypyc/test-data/irbuild-str.test index 056f120c7bac..471c81cc666f 100644 --- a/mypyc/test-data/irbuild-str.test +++ b/mypyc/test-data/irbuild-str.test @@ -638,11 +638,11 @@ floating: Final = 3.14 boolean: Final = True def test(x: str) -> str: - return f"{string}{integer}{floating}{boolean}{x}{boolean}{floating}{integer}{string}{x}{string}{integer}{floating}{boolean}" + return f"{string[:3]}{integer}{floating}{boolean}{x}{boolean}{floating}{integer}{string}{x}{string}{integer}{floating}{boolean}" def test2(x: str) -> str: - return f"{string}{integer}{floating}{boolean}{x}{boolean}{floating}{integer}{string}{x}{string}{integer}{floating}{boolean}{x}" + return f"{string[-3:]}{integer}{floating}{boolean}{x}{boolean}{floating}{integer}{string}{x}{string}{integer}{floating}{boolean}{x}" def test3(x: str) -> str: - return f"{x}{string}{integer}{floating}{boolean}{x}{boolean}{floating}{integer}{string}{x}{string}{integer}{floating}{boolean}{x}" + return f"{x}{string[:]}{integer}{floating}{boolean}{x}{boolean}{floating}{integer}{string}{x}{string}{integer}{floating}{boolean}{x}" [out] def test(x): From 8c6166a2611e633b7622519820a611593d32bbaf Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Tue, 6 Jan 2026 06:01:24 -0400 Subject: [PATCH 4/6] Update format_str_tokenizer.py --- mypyc/irbuild/format_str_tokenizer.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mypyc/irbuild/format_str_tokenizer.py b/mypyc/irbuild/format_str_tokenizer.py index 1284c99f5c74..3a36eeb10f76 100644 --- a/mypyc/irbuild/format_str_tokenizer.py +++ b/mypyc/irbuild/format_str_tokenizer.py @@ -144,11 +144,10 @@ 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? - var_str = builder.accept(x) - elif (folded := constant_fold_expr(builder, x)) is not None: + if (folded := constant_fold_expr(builder, x)) is not None: var_str = builder.accept(StrExpr(str(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: From 7345e200eafda69c9e3526e6a8aa7f6c636b7e3f Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Tue, 6 Jan 2026 09:56:16 +0000 Subject: [PATCH 5/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 9aae4988b2f18b2a15d6a6e9c3b5eed6db7b1579 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Tue, 6 Jan 2026 10:17:45 +0000 Subject: [PATCH 6/6] Revert "irtest" This reverts commit 7345e200eafda69c9e3526e6a8aa7f6c636b7e3f. --- mypyc/test-data/irbuild-constant-fold.test | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/mypyc/test-data/irbuild-constant-fold.test b/mypyc/test-data/irbuild-constant-fold.test index 06831043ff10..cd953c84c541 100644 --- a/mypyc/test-data/irbuild-constant-fold.test +++ b/mypyc/test-data/irbuild-constant-fold.test @@ -186,25 +186,6 @@ 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