Skip to content

Commit 376e2ad

Browse files
authored
[mypyc] Fix is_native_ref_expr for class attrs (#18031)
This addresses mypyc/mypyc#932 in which in some cases before we were incorrectly not determining that the symbol was native. Because the problematic implementation of `is_native_ref_expr` is present in multiple places, the one in `builder.py` is rewritten to use the one in `mapper.py`. Additional run tests are added to cover the case. Use of a dictionary here prevents falling under a secondary, more optimized codepath, that avoided the issue entirely.
1 parent 776d01d commit 376e2ad

File tree

4 files changed

+20
-8
lines changed

4 files changed

+20
-8
lines changed

mypyc/irbuild/builder.py

+3-7
Original file line numberDiff line numberDiff line change
@@ -979,17 +979,13 @@ def _analyze_iterable_item_type(self, expr: Expression) -> Type:
979979

980980
def is_native_module(self, module: str) -> bool:
981981
"""Is the given module one compiled by mypyc?"""
982-
return module in self.mapper.group_map
982+
return self.mapper.is_native_module(module)
983983

984984
def is_native_ref_expr(self, expr: RefExpr) -> bool:
985-
if expr.node is None:
986-
return False
987-
if "." in expr.node.fullname:
988-
return self.is_native_module(expr.node.fullname.rpartition(".")[0])
989-
return True
985+
return self.mapper.is_native_ref_expr(expr)
990986

991987
def is_native_module_ref_expr(self, expr: RefExpr) -> bool:
992-
return self.is_native_ref_expr(expr) and expr.kind == GDEF
988+
return self.mapper.is_native_module_ref_expr(expr)
993989

994990
def is_synthetic_type(self, typ: TypeInfo) -> bool:
995991
"""Is a type something other than just a class we've created?"""

mypyc/irbuild/mapper.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ def __init__(self, group_map: dict[str, str | None]) -> None:
6262
self.group_map = group_map
6363
self.type_to_ir: dict[TypeInfo, ClassIR] = {}
6464
self.func_to_decl: dict[SymbolNode, FuncDecl] = {}
65+
self.symbol_fullnames: set[str] = set()
6566

6667
def type_to_rtype(self, typ: Type | None) -> RType:
6768
if typ is None:
@@ -217,7 +218,8 @@ def is_native_ref_expr(self, expr: RefExpr) -> bool:
217218
if expr.node is None:
218219
return False
219220
if "." in expr.node.fullname:
220-
return self.is_native_module(expr.node.fullname.rpartition(".")[0])
221+
name = expr.node.fullname.rpartition(".")[0]
222+
return self.is_native_module(name) or name in self.symbol_fullnames
221223
return True
222224

223225
def is_native_module_ref_expr(self, expr: RefExpr) -> bool:

mypyc/irbuild/prepare.py

+2
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ def build_type_map(
9494
if not options.global_opts:
9595
class_ir.children = None
9696
mapper.type_to_ir[cdef.info] = class_ir
97+
mapper.symbol_fullnames.add(class_ir.fullname)
9798

9899
# Populate structural information in class IR for extension classes.
99100
for module, cdef in classes:
@@ -149,6 +150,7 @@ def load_type_map(mapper: Mapper, modules: list[MypyFile], deser_ctx: DeserMaps)
149150
if isinstance(node.node, TypeInfo) and is_from_module(node.node, module):
150151
ir = deser_ctx.classes[node.node.fullname]
151152
mapper.type_to_ir[node.node] = ir
153+
mapper.symbol_fullnames.add(node.node.fullname)
152154
mapper.func_to_decl[node.node] = ir.ctor
153155

154156
for module in modules:

mypyc/test-data/run-classes.test

+12
Original file line numberDiff line numberDiff line change
@@ -2594,3 +2594,15 @@ def test_class_final_attribute_inherited() -> None:
25942594
assert C().b == 2
25952595
assert B().c == 3
25962596
assert C().c == 3
2597+
2598+
[case testClassWithFinalAttributeAccess]
2599+
from typing import Final
2600+
2601+
class C:
2602+
a: Final = {'x': 'y'}
2603+
b: Final = C.a
2604+
2605+
def test_final_attribute() -> None:
2606+
assert C.a['x'] == 'y'
2607+
assert C.b['x'] == 'y'
2608+
assert C.a is C.b

0 commit comments

Comments
 (0)