Skip to content

Commit 20e8e68

Browse files
authored
[mypyc] Box initializers of final variables when required (#10229)
Fix a compile error when an unboxed initializer is used for a boxed final variable. I had to tweak how enums are compiled to avoid breaking them as a side effect. Fixes mypyc/mypyc#815.
1 parent 8aa385e commit 20e8e68

File tree

3 files changed

+27
-12
lines changed

3 files changed

+27
-12
lines changed

mypyc/irbuild/builder.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -345,8 +345,12 @@ def non_function_scope(self) -> bool:
345345
# Currently the stack always has at least two items: dummy and top-level.
346346
return len(self.fn_infos) <= 2
347347

348-
def init_final_static(self, lvalue: Lvalue, rvalue_reg: Value,
349-
class_name: Optional[str] = None) -> None:
348+
def init_final_static(self,
349+
lvalue: Lvalue,
350+
rvalue_reg: Value,
351+
class_name: Optional[str] = None,
352+
*,
353+
type_override: Optional[RType] = None) -> None:
350354
assert isinstance(lvalue, NameExpr)
351355
assert isinstance(lvalue.node, Var)
352356
if lvalue.node.final_value is None:
@@ -355,8 +359,9 @@ def init_final_static(self, lvalue: Lvalue, rvalue_reg: Value,
355359
else:
356360
name = '{}.{}'.format(class_name, lvalue.name)
357361
assert name is not None, "Full name not set for variable"
358-
self.final_names.append((name, rvalue_reg.type))
359-
self.add(InitStatic(rvalue_reg, name, self.module_name))
362+
coerced = self.coerce(rvalue_reg, type_override or self.node_type(lvalue), lvalue.line)
363+
self.final_names.append((name, coerced.type))
364+
self.add(InitStatic(coerced, name, self.module_name))
360365

361366
def load_final_static(self, fullname: str, typ: RType, line: int,
362367
error_name: Optional[str] = None) -> Value:

mypyc/irbuild/classdef.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Transform class definitions from the mypy AST form to IR."""
22

3-
from typing import List, Optional
3+
from typing import List, Optional, Tuple
44

55
from mypy.nodes import (
66
ClassDef, FuncDef, OverloadedFuncDef, PassStmt, AssignmentStmt, NameExpr, StrExpr,
@@ -11,7 +11,7 @@
1111
BasicBlock, Branch, MethodCall, NAMESPACE_TYPE, LoadAddress
1212
)
1313
from mypyc.ir.rtypes import (
14-
object_rprimitive, bool_rprimitive, dict_rprimitive, is_optional_type,
14+
RType, object_rprimitive, bool_rprimitive, dict_rprimitive, is_optional_type,
1515
is_object_rprimitive, is_none_rprimitive
1616
)
1717
from mypyc.ir.func_ir import FuncDecl, FuncSignature
@@ -76,7 +76,7 @@ def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None:
7676
dataclass_non_ext = None
7777
type_obj = None
7878

79-
attrs_to_cache = [] # type: List[Lvalue]
79+
attrs_to_cache = [] # type: List[Tuple[Lvalue, RType]]
8080

8181
for stmt in cdef.defs.body:
8282
if isinstance(stmt, OverloadedFuncDef) and stmt.is_property:
@@ -269,7 +269,7 @@ def add_non_ext_class_attr(builder: IRBuilder,
269269
lvalue: NameExpr,
270270
stmt: AssignmentStmt,
271271
cdef: ClassDef,
272-
attr_to_cache: List[Lvalue]) -> None:
272+
attr_to_cache: List[Tuple[Lvalue, RType]]) -> None:
273273
"""Add a class attribute to __annotations__ of a non-extension class.
274274
275275
If the attribute is initialized with a value, also add it to __dict__.
@@ -294,7 +294,8 @@ def add_non_ext_class_attr(builder: IRBuilder,
294294
# Skip "_order_" and "__order__", since Enum will remove it
295295
and lvalue.name not in ('_order_', '__order__')
296296
):
297-
attr_to_cache.append(lvalue)
297+
# Enum values are always boxed, so use object_rprimitive.
298+
attr_to_cache.append((lvalue, object_rprimitive))
298299

299300

300301
def generate_attr_defaults(builder: IRBuilder, cdef: ClassDef) -> None:
@@ -419,13 +420,15 @@ def load_decorated_class(builder: IRBuilder, cdef: ClassDef, type_obj: Value) ->
419420
return dec_class
420421

421422

422-
def cache_class_attrs(builder: IRBuilder, attrs_to_cache: List[Lvalue], cdef: ClassDef) -> None:
423+
def cache_class_attrs(builder: IRBuilder,
424+
attrs_to_cache: List[Tuple[Lvalue, RType]],
425+
cdef: ClassDef) -> None:
423426
"""Add class attributes to be cached to the global cache."""
424427
typ = builder.load_native_type_object(cdef.fullname)
425-
for lval in attrs_to_cache:
428+
for lval, rtype in attrs_to_cache:
426429
assert isinstance(lval, NameExpr)
427430
rval = builder.py_get_attr(typ, lval.name, cdef.line)
428-
builder.init_final_static(lval, rval, cdef.name)
431+
builder.init_final_static(lval, rval, cdef.name, type_override=rtype)
429432

430433

431434
def create_mypyc_attrs_tuple(builder: IRBuilder, ir: ClassIR, line: int) -> Value:

mypyc/test-data/run-tuples.test

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ assert f(Sub(3, 2)) == 3
9797

9898
[case testTupleOps]
9999
from typing import Tuple, List, Any, Optional
100+
from typing_extensions import Final
100101

101102
def f() -> Tuple[()]:
102103
return ()
@@ -178,3 +179,9 @@ def test_slicing() -> None:
178179
assert s[1:long_int] == ("o", "o", "b", "a", "r")
179180
assert s[long_int:] == ()
180181
assert s[-long_int:-1] == ("f", "o", "o", "b", "a")
182+
183+
TUPLE: Final[Tuple[str, ...]] = ('x', 'y')
184+
185+
def test_final_boxed_tuple() -> None:
186+
t = TUPLE
187+
assert t == ('x', 'y')

0 commit comments

Comments
 (0)