From ea5348e7317937db4c9fea2e1f2e777ce6ff7d40 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Thu, 18 Sep 2025 20:26:07 +0000 Subject: [PATCH 01/32] feat: constant fold f-strings --- mypy/constant_fold.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 4582b2a7396d..3294f9b8a782 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -17,6 +17,9 @@ StrExpr, UnaryExpr, Var, + CallExpr, + MemberExpr, + ListExpr, ) # All possible result types of constant folding @@ -73,6 +76,23 @@ 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) + # --- f-string constant folding --- + elif ( + isinstance(expr, CallExpr) + and isinstance(callee := expr.callee, MemberExpr) + and isinstance(callee.expr, StrExpr) + and callee.expr.value == "" + and callee.name == "join" + and len(args := expr.args) == 1 + and isinstance(arg := args[0], ListExpr) + ): + folded_items = [] + for item in arg.items: + val = constant_fold_expr(item, cur_mod_id) + if not isinstance(val, str): + return None + folded_items.append(val) + return "".join(folded_items) return None @@ -185,3 +205,7 @@ def constant_fold_unary_op(op: str, value: ConstantValue) -> int | float | None: elif op == "+" and isinstance(value, (int, float)): return value return None + + +def is_f_string_expr(expr: Expression) -> TypeGuard[CallExpr]: + \ No newline at end of file From e8d6c771e846ca6d3d6631ce7a213712e9d46f02 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 18 Sep 2025 20:46:16 +0000 Subject: [PATCH 02/32] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/constant_fold.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 3294f9b8a782..dd96c610f811 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -208,4 +208,3 @@ def constant_fold_unary_op(op: str, value: ConstantValue) -> int | float | None: def is_f_string_expr(expr: Expression) -> TypeGuard[CallExpr]: - \ No newline at end of file From 5f1162ee50680149e35f08cd6d7997edb48cd203 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Thu, 18 Sep 2025 20:49:01 +0000 Subject: [PATCH 03/32] support TupleExpr --- mypy/constant_fold.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 3294f9b8a782..b2e0d91ca62c 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -20,6 +20,7 @@ CallExpr, MemberExpr, ListExpr, + TupleExpr, ) # All possible result types of constant folding @@ -76,15 +77,14 @@ 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) - # --- f-string constant folding --- + # --- partial str.join support in preparation for f-string constant folding --- elif ( isinstance(expr, CallExpr) and isinstance(callee := expr.callee, MemberExpr) and isinstance(callee.expr, StrExpr) - and callee.expr.value == "" and callee.name == "join" and len(args := expr.args) == 1 - and isinstance(arg := args[0], ListExpr) + and isinstance(arg := args[0], (ListExpr, TupleExpr)) ): folded_items = [] for item in arg.items: @@ -92,7 +92,7 @@ def constant_fold_expr(expr: Expression, cur_mod_id: str) -> ConstantValue | Non if not isinstance(val, str): return None folded_items.append(val) - return "".join(folded_items) + return callee.expr.value.join(folded_items) return None From 91a19076e64f2a767ca1fdee9e57b8baaa22761e Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Thu, 18 Sep 2025 20:51:51 +0000 Subject: [PATCH 04/32] fix accident commit --- mypy/constant_fold.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index b2e0d91ca62c..1176d110260c 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -205,7 +205,3 @@ def constant_fold_unary_op(op: str, value: ConstantValue) -> int | float | None: elif op == "+" and isinstance(value, (int, float)): return value return None - - -def is_f_string_expr(expr: Expression) -> TypeGuard[CallExpr]: - \ No newline at end of file From 96c25748d3895f911ac0295d9c25b8a8d7325e43 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 18 Sep 2025 20:54:04 +0000 Subject: [PATCH 05/32] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/constant_fold.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 1176d110260c..621cc7b0df63 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -8,19 +8,19 @@ from typing import Final, Union from mypy.nodes import ( + CallExpr, ComplexExpr, Expression, FloatExpr, IntExpr, + ListExpr, + MemberExpr, NameExpr, OpExpr, StrExpr, + TupleExpr, UnaryExpr, Var, - CallExpr, - MemberExpr, - ListExpr, - TupleExpr, ) # All possible result types of constant folding From e84c21c10da6f6d31a1ddf2a9915203a453104bf Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Thu, 18 Sep 2025 21:12:17 +0000 Subject: [PATCH 06/32] feat: better folding --- mypy/constant_fold.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 621cc7b0df63..7a5f207bb820 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -81,7 +81,7 @@ def constant_fold_expr(expr: Expression, cur_mod_id: str) -> ConstantValue | Non elif ( isinstance(expr, CallExpr) and isinstance(callee := expr.callee, MemberExpr) - and isinstance(callee.expr, StrExpr) + and isinstance(folded_callee := constant_fold_expr(callee.expr, cur_mod_id), str) and callee.name == "join" and len(args := expr.args) == 1 and isinstance(arg := args[0], (ListExpr, TupleExpr)) @@ -92,7 +92,7 @@ def constant_fold_expr(expr: Expression, cur_mod_id: str) -> ConstantValue | Non if not isinstance(val, str): return None folded_items.append(val) - return callee.expr.value.join(folded_items) + return folded_callee.join(folded_items) return None From b67f10a858b33f3cadc00eb7e453ff72af366128 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Thu, 18 Sep 2025 21:15:03 +0000 Subject: [PATCH 07/32] feat: implement constant folding for str.format --- mypy/constant_fold.py | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 7a5f207bb820..0900886bb975 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -77,22 +77,34 @@ 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) - # --- partial str.join support in preparation for f-string constant folding --- + # --- f-string requires partial support for both str.join and str.format --- elif ( isinstance(expr, CallExpr) and isinstance(callee := expr.callee, MemberExpr) and isinstance(folded_callee := constant_fold_expr(callee.expr, cur_mod_id), str) - and callee.name == "join" - and len(args := expr.args) == 1 - and isinstance(arg := args[0], (ListExpr, TupleExpr)) ): - folded_items = [] - for item in arg.items: - val = constant_fold_expr(item, cur_mod_id) - if not isinstance(val, str): - return None - folded_items.append(val) - return folded_callee.join(folded_items) + # --- partial str.join constant folding --- + if ( + callee.name == "join" + and len(args := expr.args) == 1 + and isinstance(arg := args[0], (ListExpr, TupleExpr)) + ): + folded_items: list[str] = [] + for item in arg.items: + val = constant_fold_expr(item, cur_mod_id) + if not isinstance(val, str): + return None + folded_items.append(val) + return folded_callee.join(folded_items) + # --- str.format constant folding + elif callee.name == "format": + folded_args: list[str] = [] + for arg in expr.args: + arg_val = constant_fold_expr(arg, cur_mod_id) + if arg_val is None: + return None + folded_args.append(arg_val) + return folded_callee.format(*folded_args) return None From 201a683160efd9f3a9c62b1baacc68bf111635c2 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 18 Sep 2025 18:23:07 -0400 Subject: [PATCH 08/32] fix mypy err --- 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 0900886bb975..3b621c3b4129 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 return folded_callee.join(folded_items) # --- str.format constant folding elif callee.name == "format": - folded_args: list[str] = [] + folded_args: list[ConstantValue] = [] for arg in expr.args: arg_val = constant_fold_expr(arg, cur_mod_id) if arg_val is None: From 4420f83cf62b8fae09f1fbaee3620fc4c95dbd30 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Thu, 18 Sep 2025 22:01:31 +0000 Subject: [PATCH 09/32] feat: constant fold common builtin call exprs --- mypy/constant_fold.py | 115 ++++++++++++++++++++++++++++++++---------- 1 file changed, 87 insertions(+), 28 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 3b621c3b4129..55d6e9b87d8d 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -8,6 +8,7 @@ from typing import Final, Union from mypy.nodes import ( + ArgKind, CallExpr, ComplexExpr, Expression, @@ -77,34 +78,8 @@ 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) - # --- f-string requires partial support for both str.join and str.format --- - elif ( - isinstance(expr, CallExpr) - and isinstance(callee := expr.callee, MemberExpr) - and isinstance(folded_callee := constant_fold_expr(callee.expr, cur_mod_id), str) - ): - # --- partial str.join constant folding --- - if ( - callee.name == "join" - and len(args := expr.args) == 1 - and isinstance(arg := args[0], (ListExpr, TupleExpr)) - ): - folded_items: list[str] = [] - for item in arg.items: - val = constant_fold_expr(item, cur_mod_id) - if not isinstance(val, str): - return None - folded_items.append(val) - return folded_callee.join(folded_items) - # --- str.format constant folding - elif callee.name == "format": - folded_args: list[ConstantValue] = [] - for arg in expr.args: - arg_val = constant_fold_expr(arg, cur_mod_id) - if arg_val is None: - return None - folded_args.append(arg_val) - return folded_callee.format(*folded_args) + elif isinstance(expr, CallExpr): + return constant_fold_call_expr(expr, cur_mod_id) return None @@ -217,3 +192,87 @@ def constant_fold_unary_op(op: str, value: ConstantValue) -> int | float | None: elif op == "+" and isinstance(value, (int, float)): return value return None + + +foldable_builtins = { + "builtins.str": str, + "builtins.int": int, + "builtins.bool": bool, + "builtins.float": float, + "builtins.complex": complex, + "builtins.repr": repr, + "builtins.len": len, + "builtins.hasattr": hasattr, + "builtins.hex": hex, + "builtins.hash": hash, + "builtins.min": min, + "builtins.max": max, + "builtins.oct": oct, + "builtins.pow": pow, + "builtins.round": round, + "builtins.abs": abs, + "builtins.ascii": ascii, + "builtins.bin": bin, + "builtins.chr": chr, +} + +def constant_fold_call_expr(expr: CallExpr, cur_mod_id: str, foldable_builtins=foldable_builtins) -> ConstantValue | None: + callee = expr.callee + if isinstance(callee, NameExpr): + func = foldable_builtins.get(callee.fullname) + if func is None: + return None + + folded_args = [] + for arg in expr.args: + val = constant_fold_expr(arg, cur_mod_id) + if val is None: + return None + folded_args.append(arg) + + args = [] + kwargs = {} + for folded_arg, arg_kind, arg_name in zip(folded_args, expr.arg_kinds, expr.arg_names): + try: + if arg_kind == ArgKind.ARG_POS: + args.append(folded_arg) + elif arg_kind == ArgKind.ARG_NAMED: + kwargs[arg_name] = folded_arg + elif arg_kind == ArgKind.ARG_STAR: + args.extend(folded_arg) + elif arg_kind == ArgKind.ARG_STAR2: + kwargs.update(folded_arg) + except: + return None + + try: + return func(*args, **kwargs) + except: + return None + # --- f-string requires partial support for both str.join and str.format --- + elif ( + isinstance(callee, MemberExpr) + and isinstance(folded_callee := constant_fold_expr(callee.expr, cur_mod_id), str) + ): + # --- partial str.join constant folding --- + if ( + callee.name == "join" + and len(args := expr.args) == 1 + and isinstance(arg := args[0], (ListExpr, TupleExpr)) + ): + folded_items: list[str] = [] + for item in arg.items: + val = constant_fold_expr(item, cur_mod_id) + if not isinstance(val, str): + return None + folded_items.append(val) + return folded_callee.join(folded_items) + # --- str.format constant folding + elif callee.name == "format": + folded_args: list[str] = [] + for arg in expr.args: + arg_val = constant_fold_expr(arg, cur_mod_id) + if arg_val is None: + return None + folded_args.append(arg_val) + return folded_callee.format(*folded_args) \ No newline at end of file From 78dc09a3882e76d5cb5fe91aa1158bba79ff63b3 Mon Sep 17 00:00:00 2001 From: BobTheBuidler Date: Thu, 18 Sep 2025 22:17:07 +0000 Subject: [PATCH 10/32] fix type errs --- mypy/constant_fold.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 55d6e9b87d8d..40814abd5dc5 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -5,7 +5,7 @@ from __future__ import annotations -from typing import Final, Union +from typing import Any, Callable, Final, Union from mypy.nodes import ( ArgKind, @@ -216,7 +216,11 @@ def constant_fold_unary_op(op: str, value: ConstantValue) -> int | float | None: "builtins.chr": chr, } -def constant_fold_call_expr(expr: CallExpr, cur_mod_id: str, foldable_builtins=foldable_builtins) -> ConstantValue | None: +def constant_fold_call_expr( + expr: CallExpr, + cur_mod_id: str, + foldable_builtins: dict[str, Callable[..., Any]] = foldable_builtins, +) -> ConstantValue | None: callee = expr.callee if isinstance(callee, NameExpr): func = foldable_builtins.get(callee.fullname) From 04508a30ebd898f732c4218961b64082d02e4ae5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 18 Sep 2025 22:07:28 +0000 Subject: [PATCH 11/32] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/constant_fold.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 40814abd5dc5..f6523f843084 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -226,14 +226,14 @@ def constant_fold_call_expr( func = foldable_builtins.get(callee.fullname) if func is None: return None - + folded_args = [] for arg in expr.args: val = constant_fold_expr(arg, cur_mod_id) if val is None: return None folded_args.append(arg) - + args = [] kwargs = {} for folded_arg, arg_kind, arg_name in zip(folded_args, expr.arg_kinds, expr.arg_names): @@ -254,9 +254,8 @@ def constant_fold_call_expr( except: return None # --- f-string requires partial support for both str.join and str.format --- - elif ( - isinstance(callee, MemberExpr) - and isinstance(folded_callee := constant_fold_expr(callee.expr, cur_mod_id), str) + elif isinstance(callee, MemberExpr) and isinstance( + folded_callee := constant_fold_expr(callee.expr, cur_mod_id), str ): # --- partial str.join constant folding --- if ( @@ -279,4 +278,4 @@ def constant_fold_call_expr( if arg_val is None: return None folded_args.append(arg_val) - return folded_callee.format(*folded_args) \ No newline at end of file + return folded_callee.format(*folded_args) From 90b3234274463e632fea2d95476768d479c9b0b1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 18 Sep 2025 22:23:21 +0000 Subject: [PATCH 12/32] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/constant_fold.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index f6523f843084..d89f3b5c1dd0 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -216,6 +216,7 @@ def constant_fold_unary_op(op: str, value: ConstantValue) -> int | float | None: "builtins.chr": chr, } + def constant_fold_call_expr( expr: CallExpr, cur_mod_id: str, From 05191f3367cd233fb091dcaf33485e8c4733fe64 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 18 Sep 2025 19:17:40 -0400 Subject: [PATCH 13/32] fix mypy errs --- mypy/constant_fold.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index d89f3b5c1dd0..3dac6d006dcf 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -228,30 +228,30 @@ def constant_fold_call_expr( if func is None: return None - folded_args = [] + args = [] for arg in expr.args: val = constant_fold_expr(arg, cur_mod_id) if val is None: return None - folded_args.append(arg) + args.append(arg) - args = [] - kwargs = {} - for folded_arg, arg_kind, arg_name in zip(folded_args, expr.arg_kinds, expr.arg_names): + call_args = [] + call_kwargs = {} + for folded_arg, arg_kind, arg_name in zip(args, expr.arg_kinds, expr.arg_names): try: if arg_kind == ArgKind.ARG_POS: - args.append(folded_arg) + call_args.append(folded_arg) elif arg_kind == ArgKind.ARG_NAMED: - kwargs[arg_name] = folded_arg + call_kwargs[arg_name] = folded_arg elif arg_kind == ArgKind.ARG_STAR: - args.extend(folded_arg) + call_args.extend(folded_arg) elif arg_kind == ArgKind.ARG_STAR2: - kwargs.update(folded_arg) + call_kwargs.update(folded_arg) except: return None try: - return func(*args, **kwargs) + return func(*call_args, **call_kwargs) except: return None # --- f-string requires partial support for both str.join and str.format --- From 6feede67910c4fe5052df3645f7d6aa7ae2f4174 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 18 Sep 2025 19:25:57 -0400 Subject: [PATCH 14/32] fix mypy errs --- mypy/constant_fold.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 3dac6d006dcf..9b9e68e770b0 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -194,7 +194,7 @@ def constant_fold_unary_op(op: str, value: ConstantValue) -> int | float | None: return None -foldable_builtins = { +foldable_builtins: dict[str, Callable[..., Any] = { "builtins.str": str, "builtins.int": int, "builtins.bool": bool, @@ -235,8 +235,8 @@ def constant_fold_call_expr( return None args.append(arg) - call_args = [] - call_kwargs = {} + call_args: list[ConstantValue] = [] + call_kwargs: dict[str, ConstantValue] = {} for folded_arg, arg_kind, arg_name in zip(args, expr.arg_kinds, expr.arg_names): try: if arg_kind == ArgKind.ARG_POS: @@ -251,7 +251,7 @@ def constant_fold_call_expr( return None try: - return func(*call_args, **call_kwargs) + return func(*call_args, **call_kwargs) # type: ignore [return-value] except: return None # --- f-string requires partial support for both str.join and str.format --- @@ -280,3 +280,5 @@ def constant_fold_call_expr( return None folded_args.append(arg_val) return folded_callee.format(*folded_args) + return None + From 60642c531857967830ec6d4794d76e0f6c7c2b3e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 18 Sep 2025 23:27:20 +0000 Subject: [PATCH 15/32] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/constant_fold.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 9b9e68e770b0..dc4e125075a8 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -281,4 +281,3 @@ def constant_fold_call_expr( folded_args.append(arg_val) return folded_callee.format(*folded_args) return None - From 065d8db4cab1fc92b6d78ba41dec19ab984f1fad Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 19 Sep 2025 22:32:52 -0400 Subject: [PATCH 16/32] fix missing ] --- 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 dc4e125075a8..39be3d42a53a 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -194,7 +194,7 @@ def constant_fold_unary_op(op: str, value: ConstantValue) -> int | float | None: return None -foldable_builtins: dict[str, Callable[..., Any] = { +foldable_builtins: dict[str, Callable[..., Any]] = { "builtins.str": str, "builtins.int": int, "builtins.bool": bool, From dce4881001fa0c613da4d51ed3a2d2a1effc9b72 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sat, 20 Sep 2025 15:37:28 -0400 Subject: [PATCH 17/32] Update irbuild-basic.test --- mypyc/test-data/irbuild-basic.test | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/mypyc/test-data/irbuild-basic.test b/mypyc/test-data/irbuild-basic.test index 612f3266fd79..8fa352aa2715 100644 --- a/mypyc/test-data/irbuild-basic.test +++ b/mypyc/test-data/irbuild-basic.test @@ -3093,18 +3093,8 @@ def f() -> int: return x - 1 [out] def f(): - r0 :: int - r1 :: bool - r2 :: int L0: - r0 = __main__.x :: static - if is_error(r0) goto L1 else goto L2 -L1: - r1 = raise NameError('value for final name "x" was not set') - unreachable -L2: - r2 = CPyTagged_Subtract(r0, 2) - return r2 + return 0 [case testFinalRestrictedTypeVar] from typing import TypeVar From 7415fe9f76fffa5d870f866e8ee8fe3ad1fe95f6 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sat, 20 Sep 2025 15:40:59 -0400 Subject: [PATCH 18/32] Update exceptions-freq.test --- mypyc/test-data/exceptions-freq.test | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mypyc/test-data/exceptions-freq.test b/mypyc/test-data/exceptions-freq.test index b0e4cd6d35f7..1c78b3c73120 100644 --- a/mypyc/test-data/exceptions-freq.test +++ b/mypyc/test-data/exceptions-freq.test @@ -99,7 +99,11 @@ hot blocks: [0, 1] [case testRareBranch_freq] from typing import Final -x: Final = str() +def setter() -> str: + # we need this helper to ensure `x` cannot be constant folded + return "" + +x: Final = setter() def f() -> str: return x From 714aaa49c3eb07ff7a177561dfeab830823d4363 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sat, 20 Sep 2025 16:19:28 -0400 Subject: [PATCH 19/32] Update check-final.test --- test-data/unit/check-final.test | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test-data/unit/check-final.test b/test-data/unit/check-final.test index e3fc4614fc06..62e51286216d 100644 --- a/test-data/unit/check-final.test +++ b/test-data/unit/check-final.test @@ -11,7 +11,7 @@ y: Final[float] = int() z: Final[int] = int() bad: Final[str] = int() # E: Incompatible types in assignment (expression has type "int", variable has type "str") -reveal_type(x) # N: Revealed type is "builtins.int" +reveal_type(x) # N: Revealed type is "Literal[0]?" reveal_type(y) # N: Revealed type is "builtins.float" reveal_type(z) # N: Revealed type is "builtins.int" [out] @@ -26,10 +26,10 @@ class C: bad: Final[str] = int() # E: Incompatible types in assignment (expression has type "int", variable has type "str") class D(C): pass -reveal_type(D.x) # N: Revealed type is "builtins.int" +reveal_type(D.x) # N: Revealed type is "Literal[0]?" reveal_type(D.y) # N: Revealed type is "builtins.float" reveal_type(D.z) # N: Revealed type is "builtins.int" -reveal_type(D().x) # N: Revealed type is "builtins.int" +reveal_type(D().x) # N: Revealed type is "Literal[0]?" reveal_type(D().y) # N: Revealed type is "builtins.float" reveal_type(D().z) # N: Revealed type is "builtins.int" [out] From f42f9f663e16a235e2c662c205310313d62170b6 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sat, 20 Sep 2025 16:21:51 -0400 Subject: [PATCH 20/32] Update check-inference.test --- test-data/unit/check-inference.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-data/unit/check-inference.test b/test-data/unit/check-inference.test index 63278d6c4547..6730aac5620e 100644 --- a/test-data/unit/check-inference.test +++ b/test-data/unit/check-inference.test @@ -2174,8 +2174,8 @@ class A: [out] [case testMultipassAndTopLevelVariable] -y = x # E: Cannot determine type of "x" # E: Name "x" is used before definition -y() +y = x # E: Name "x" is used before definition +y() # E: "int" not callable x = 1+int() [out] From 75f00ff544d5a7b6102f81c43bee63e975484099 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sat, 20 Sep 2025 16:23:35 -0400 Subject: [PATCH 21/32] Update exceptions-freq.test --- mypyc/test-data/exceptions-freq.test | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mypyc/test-data/exceptions-freq.test b/mypyc/test-data/exceptions-freq.test index 1c78b3c73120..314bbe14a8b5 100644 --- a/mypyc/test-data/exceptions-freq.test +++ b/mypyc/test-data/exceptions-freq.test @@ -108,6 +108,13 @@ x: Final = setter() def f() -> str: return x [out] +def setter(): + r0 :: str +L0: + r0 = '' + inc_ref r0 + return r0 +hot blocks: [0] def f(): r0 :: str r1 :: bool From d22b4edb9f85b7ad080f62afa8688c057288dca6 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sat, 20 Sep 2025 16:30:44 -0400 Subject: [PATCH 22/32] Update constant_fold.py --- mypy/constant_fold.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 39be3d42a53a..d12b67e1b28a 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -235,12 +235,12 @@ def constant_fold_call_expr( return None args.append(arg) - call_args: list[ConstantValue] = [] - call_kwargs: dict[str, ConstantValue] = {} + call_args: list[Expression] = [] + call_kwargs: dict[str, Expression] = {} for folded_arg, arg_kind, arg_name in zip(args, expr.arg_kinds, expr.arg_names): try: if arg_kind == ArgKind.ARG_POS: - call_args.append(folded_arg) + call_args.append(folded_arg) # elif arg_kind == ArgKind.ARG_NAMED: call_kwargs[arg_name] = folded_arg elif arg_kind == ArgKind.ARG_STAR: @@ -251,7 +251,7 @@ def constant_fold_call_expr( return None try: - return func(*call_args, **call_kwargs) # type: ignore [return-value] + return func(*call_args, **call_kwargs) # type: ignore [no-any-return] except: return None # --- f-string requires partial support for both str.join and str.format --- From 06f3553c92e21373c0e9db9e2a15c6cb690a19e8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 20 Sep 2025 20:32:33 +0000 Subject: [PATCH 23/32] [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 d12b67e1b28a..35e38e5b7fb6 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -240,7 +240,7 @@ def constant_fold_call_expr( for folded_arg, arg_kind, arg_name in zip(args, expr.arg_kinds, expr.arg_names): try: if arg_kind == ArgKind.ARG_POS: - call_args.append(folded_arg) # + call_args.append(folded_arg) # elif arg_kind == ArgKind.ARG_NAMED: call_kwargs[arg_name] = folded_arg elif arg_kind == ArgKind.ARG_STAR: From 0160ca0290ab858a80af900c5f8145f5a28e3edd Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sat, 20 Sep 2025 17:15:32 -0400 Subject: [PATCH 24/32] Update exceptions-freq.test --- mypyc/test-data/exceptions-freq.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypyc/test-data/exceptions-freq.test b/mypyc/test-data/exceptions-freq.test index 314bbe14a8b5..fbfbd80ad923 100644 --- a/mypyc/test-data/exceptions-freq.test +++ b/mypyc/test-data/exceptions-freq.test @@ -124,7 +124,7 @@ L0: if is_error(r0) goto L1 else goto L3 L1: r1 = raise NameError('value for final name "x" was not set') - if not r1 goto L4 (error at f:6) else goto L2 :: bool + if not r1 goto L4 (error at f:10) else goto L2 :: bool L2: unreachable L3: From 8940ded30212a3383ebd4995e472205ac862385c Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sat, 20 Sep 2025 17:23:42 -0400 Subject: [PATCH 25/32] fix mypy errs --- mypy/constant_fold.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 35e38e5b7fb6..ed8cfba8de1f 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -228,25 +228,25 @@ def constant_fold_call_expr( if func is None: return None - args = [] + args: list[ConstantValue] = [] for arg in expr.args: val = constant_fold_expr(arg, cur_mod_id) if val is None: return None - args.append(arg) + args.append(val) - call_args: list[Expression] = [] - call_kwargs: dict[str, Expression] = {} + call_args: list[ConstantValue] = [] + call_kwargs: dict[str, ConstantValue] = {} for folded_arg, arg_kind, arg_name in zip(args, expr.arg_kinds, expr.arg_names): try: if arg_kind == ArgKind.ARG_POS: - call_args.append(folded_arg) # + call_args.append(folded_arg) elif arg_kind == ArgKind.ARG_NAMED: - call_kwargs[arg_name] = folded_arg + call_kwargs[arg_name] = folded_arg # type: ignore [index] elif arg_kind == ArgKind.ARG_STAR: call_args.extend(folded_arg) elif arg_kind == ArgKind.ARG_STAR2: - call_kwargs.update(folded_arg) + call_kwargs.update(folded_arg) # type: ignore [call-overload] except: return None @@ -271,7 +271,7 @@ def constant_fold_call_expr( return None folded_items.append(val) return folded_callee.join(folded_items) - # --- str.format constant folding + # --- str.format constant folding --- elif callee.name == "format": folded_args: list[str] = [] for arg in expr.args: From f449a1e9165e605ff53cbdbc01aa97375f5f26d0 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sat, 20 Sep 2025 18:37:46 -0400 Subject: [PATCH 26/32] Update constant_fold.py --- mypy/constant_fold.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index ed8cfba8de1f..2d9dde88b5de 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -228,25 +228,25 @@ def constant_fold_call_expr( if func is None: return None - args: list[ConstantValue] = [] + folded_args: list[ConstantValue] = [] for arg in expr.args: val = constant_fold_expr(arg, cur_mod_id) if val is None: return None - args.append(val) + folded_args.append(val) call_args: list[ConstantValue] = [] call_kwargs: dict[str, ConstantValue] = {} - for folded_arg, arg_kind, arg_name in zip(args, expr.arg_kinds, expr.arg_names): + for folded_arg, arg_kind, arg_name in zip(folded_args, expr.arg_kinds, expr.arg_names): try: if arg_kind == ArgKind.ARG_POS: call_args.append(folded_arg) elif arg_kind == ArgKind.ARG_NAMED: call_kwargs[arg_name] = folded_arg # type: ignore [index] elif arg_kind == ArgKind.ARG_STAR: - call_args.extend(folded_arg) + call_args.extend(folded_arg) # type: ignore [arg-type] elif arg_kind == ArgKind.ARG_STAR2: - call_kwargs.update(folded_arg) # type: ignore [call-overload] + call_kwargs.update(folded_arg) # type: ignore [arg-type, call-overload] except: return None @@ -273,11 +273,11 @@ def constant_fold_call_expr( return folded_callee.join(folded_items) # --- str.format constant folding --- elif callee.name == "format": - folded_args: list[str] = [] + folded_strings: list[str] = [] for arg in expr.args: arg_val = constant_fold_expr(arg, cur_mod_id) if arg_val is None: return None - folded_args.append(arg_val) - return folded_callee.format(*folded_args) + folded_strings.append(arg_val) + return folded_callee.format(*folded_strings) return None From a3b7577c86bd6ac597c86301e22331566c886d3a Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sat, 20 Sep 2025 18:55:50 -0400 Subject: [PATCH 27/32] Update constant_fold.py --- mypy/constant_fold.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 2d9dde88b5de..1184e44ec3cf 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -222,13 +222,15 @@ def constant_fold_call_expr( cur_mod_id: str, foldable_builtins: dict[str, Callable[..., Any]] = foldable_builtins, ) -> ConstantValue | None: + folded_args: list[ConstantValue] + callee = expr.callee if isinstance(callee, NameExpr): func = foldable_builtins.get(callee.fullname) if func is None: return None - folded_args: list[ConstantValue] = [] + folded_args = [] for arg in expr.args: val = constant_fold_expr(arg, cur_mod_id) if val is None: @@ -273,7 +275,7 @@ def constant_fold_call_expr( return folded_callee.join(folded_items) # --- str.format constant folding --- elif callee.name == "format": - folded_strings: list[str] = [] + folded_args = [] for arg in expr.args: arg_val = constant_fold_expr(arg, cur_mod_id) if arg_val is None: From 73c7d11a8432b0e2c5abf89f9cf3ce71c9b59ddf Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 20 Sep 2025 22:57:09 +0000 Subject: [PATCH 28/32] [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 1184e44ec3cf..d13c5d89be0b 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -223,7 +223,7 @@ def constant_fold_call_expr( foldable_builtins: dict[str, Callable[..., Any]] = foldable_builtins, ) -> ConstantValue | None: folded_args: list[ConstantValue] - + callee = expr.callee if isinstance(callee, NameExpr): func = foldable_builtins.get(callee.fullname) From 91560923cbf2f6ecf82e0fd4c76a3478f0151772 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sat, 20 Sep 2025 19:42:29 -0400 Subject: [PATCH 29/32] Update constant_fold.py --- mypy/constant_fold.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index d13c5d89be0b..ee3f1865cdc1 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -280,6 +280,6 @@ def constant_fold_call_expr( arg_val = constant_fold_expr(arg, cur_mod_id) if arg_val is None: return None - folded_strings.append(arg_val) - return folded_callee.format(*folded_strings) + folded_args.append(arg_val) + return folded_callee.format(*folded_args) return None From 58e51825012dff7a19cba382fadc625c574a83b6 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Sat, 20 Sep 2025 19:43:07 -0400 Subject: [PATCH 30/32] Update constant_fold.py --- mypy/constant_fold.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index ee3f1865cdc1..9bbdc7ac66fb 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -266,13 +266,13 @@ def constant_fold_call_expr( and len(args := expr.args) == 1 and isinstance(arg := args[0], (ListExpr, TupleExpr)) ): - folded_items: list[str] = [] + folded_strings: list[str] = [] for item in arg.items: val = constant_fold_expr(item, cur_mod_id) if not isinstance(val, str): return None - folded_items.append(val) - return folded_callee.join(folded_items) + folded_strings.append(val) + return folded_callee.join(folded_strings) # --- str.format constant folding --- elif callee.name == "format": folded_args = [] From 9a73bc5c2015109e13c9bac1a4195b7c8b167879 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 25 Sep 2025 21:58:46 -0400 Subject: [PATCH 31/32] fix mypy err --- 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 9bbdc7ac66fb..98e79db8792c 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -248,7 +248,7 @@ def constant_fold_call_expr( elif arg_kind == ArgKind.ARG_STAR: call_args.extend(folded_arg) # type: ignore [arg-type] elif arg_kind == ArgKind.ARG_STAR2: - call_kwargs.update(folded_arg) # type: ignore [arg-type, call-overload] + call_kwargs.update(folded_arg) # type: ignore [arg-type] except: return None From 148cc5fdc0afed0e594ebbe90e9222a184ea53f5 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Thu, 25 Sep 2025 22:43:13 -0400 Subject: [PATCH 32/32] lint --- mypy/constant_fold.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/mypy/constant_fold.py b/mypy/constant_fold.py index 98e79db8792c..315ba1f313b8 100644 --- a/mypy/constant_fold.py +++ b/mypy/constant_fold.py @@ -239,8 +239,8 @@ def constant_fold_call_expr( call_args: list[ConstantValue] = [] call_kwargs: dict[str, ConstantValue] = {} - for folded_arg, arg_kind, arg_name in zip(folded_args, expr.arg_kinds, expr.arg_names): - try: + try: + for folded_arg, arg_kind, arg_name in zip(folded_args, expr.arg_kinds, expr.arg_names): if arg_kind == ArgKind.ARG_POS: call_args.append(folded_arg) elif arg_kind == ArgKind.ARG_NAMED: @@ -249,12 +249,8 @@ def constant_fold_call_expr( call_args.extend(folded_arg) # type: ignore [arg-type] elif arg_kind == ArgKind.ARG_STAR2: call_kwargs.update(folded_arg) # type: ignore [arg-type] - except: - return None - - try: return func(*call_args, **call_kwargs) # type: ignore [no-any-return] - except: + except Exception: return None # --- f-string requires partial support for both str.join and str.format --- elif isinstance(callee, MemberExpr) and isinstance(