Skip to content

Commit b03b6b6

Browse files
committed
fix: nested
1 parent ab47db9 commit b03b6b6

File tree

2 files changed

+49
-15
lines changed

2 files changed

+49
-15
lines changed

mypyc/irbuild/expression.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,11 +1012,11 @@ def _visit_tuple_display(builder: IRBuilder, expr: TupleExpr) -> Value:
10121012
return builder.primitive_op(list_tuple_op, [val_as_list], expr.line)
10131013

10141014

1015-
def dict_literal_values(builder: IRBuilder, items: Sequence[tuple[Expression | None, Expression]], line: int) -> "Value | None":
1016-
"""Try to extract a constant dict from a dict literal.
1015+
def dict_literal_values(builder: IRBuilder, items: Sequence[tuple[Expression | None, Expression]], line: int) -> "dict | None":
1016+
"""Try to extract a constant dict from a dict literal, recursively staticizing nested dicts.
10171017
1018-
If all keys and values are deeply immutable and constant, register it as a static
1019-
and return a LoadLiteral for it. Otherwise, return None.
1018+
If all keys and values are deeply immutable and constant (including nested dicts as values),
1019+
return the Python dict value. Otherwise, return None.
10201020
"""
10211021
result = {}
10221022
for key_expr, value_expr in items:
@@ -1027,12 +1027,21 @@ def dict_literal_values(builder: IRBuilder, items: Sequence[tuple[Expression | N
10271027
key = constant_fold_expr(builder, key_expr)
10281028
if key is None:
10291029
return None
1030+
# Recursively staticize dict values
10301031
value = constant_fold_expr(builder, value_expr)
10311032
if value is None:
1032-
return None
1033+
# Try to staticize nested dicts
1034+
if isinstance(value_expr, DictExpr):
1035+
nested = dict_literal_values(builder, value_expr.items, value_expr.line)
1036+
if nested is not None:
1037+
value = nested
1038+
else:
1039+
return None
1040+
else:
1041+
return None
10331042
result[key] = value
10341043

1035-
return builder.add(LoadLiteral(result, dict_rprimitive)) if result else None
1044+
return result or None
10361045

10371046
def transform_dict_expr(builder: IRBuilder, expr: DictExpr) -> Value:
10381047
"""First accepts all keys and values, then makes a dict out of them.
@@ -1041,10 +1050,11 @@ def transform_dict_expr(builder: IRBuilder, expr: DictExpr) -> Value:
10411050
and at runtime use PyDict_Copy to return a fresh dict.
10421051
"""
10431052
# Try to constant fold the dict and get a static Value
1044-
template = dict_literal_values(builder, expr.items, expr.line)
1045-
if template is not None:
1046-
# At runtime, return PyDict_Copy(template)
1047-
return builder.call_c(dict_template_copy_op, [template], expr.line)
1053+
static_dict = dict_literal_values(builder, expr.items, expr.line)
1054+
if static_dict is not None:
1055+
# Register the static dict and return a copy at runtime
1056+
static_val = builder.add(LoadLiteral(static_dict, dict_rprimitive))
1057+
return builder.call_c(dict_template_copy_op, [static_val], expr.line)
10481058

10491059
# Fallback: build dict at runtime as before
10501060
key_value_pairs = []

mypyc/test-data/irbuild-dict.test

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -569,14 +569,38 @@ L3:
569569
[case testNestedDictLiteral]
570570
def f() -> None:
571571
d = {"a": {2: 3.4}}
572+
def g() -> None:
573+
d = {"outer": {"inner": {"deep": 42, "wow": {"x": {"a": {2: 3.4}}}}}}
574+
def h() -> None:
575+
d = {"tuple": ({1: 2},), "frozenset": frozenset([3, 4])}
572576
[out]
573577
def f():
578+
r0, r1, d :: dict
579+
L0:
580+
r0 = {'a': {2: 3.4}}
581+
r1 = PyDict_Copy(r0)
582+
d = r1
583+
return 1
584+
def g():
585+
r0, r1, d :: dict
586+
L0:
587+
r0 = {'outer': {'inner': {'deep': 42, 'wow': {'x': {'a': {2: 3.4}}}}}}
588+
r1 = PyDict_Copy(r0)
589+
d = r1
590+
return 1
591+
def h():
574592
r0 :: str
575-
r1, r2, r3, d :: dict
593+
r1, r2 :: dict
594+
r3 :: tuple[dict]
595+
r4 :: object
596+
r5, d :: dict
576597
L0:
577-
r0 = 'a'
578-
r1 = {2: 3.4}
598+
r0 = 'tuple'
599+
r1 = {1: 2}
579600
r2 = PyDict_Copy(r1)
580-
r3 = CPyDict_Build(1, r0, r2)
581-
d = r3
601+
r3 = (r2)
602+
r4 = box(tuple[dict], r3)
603+
r5 = CPyDict_Build(1, r0, r4)
604+
d = r5
582605
return 1
606+

0 commit comments

Comments
 (0)