Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions mypyc/analysis/attrdefined.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,14 +424,20 @@ def detect_undefined_bitmap(cl: ClassIR, seen: set[ClassIR]) -> None:
for base in cl.base_mro[1:]:
detect_undefined_bitmap(base, seen)

# Build fresh and assign once. This function is called per SCC and `seen`
# only dedupes within a single call, so appending in place to a shared base
# would accumulate duplicates across SCCs and produce non-deterministic
# struct layouts under separate=True.
new_attrs: list[str] = []
if len(cl.base_mro) > 1:
cl.bitmap_attrs.extend(cl.base_mro[1].bitmap_attrs)
new_attrs.extend(cl.base_mro[1].bitmap_attrs)
for n, t in cl.attributes.items():
if t.error_overlap and not cl.is_always_defined(n):
cl.bitmap_attrs.append(n)
new_attrs.append(n)

for base in cl.mro[1:]:
if base.is_trait:
for n, t in base.attributes.items():
if t.error_overlap and not cl.is_always_defined(n) and n not in cl.bitmap_attrs:
cl.bitmap_attrs.append(n)
if t.error_overlap and not cl.is_always_defined(n) and n not in new_attrs:
new_attrs.append(n)
cl.bitmap_attrs = new_attrs
21 changes: 21 additions & 0 deletions mypyc/test/test_emitclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import unittest

from mypyc.analysis.attrdefined import detect_undefined_bitmap
from mypyc.codegen.emitclass import getter_name, setter_name, slot_key
from mypyc.ir.class_ir import ClassIR
from mypyc.ir.rtypes import int32_rprimitive
from mypyc.namegen import NameGenerator


Expand Down Expand Up @@ -33,3 +35,22 @@ def test_getter_name(self) -> None:
generator = NameGenerator([["mod"]])

assert getter_name(cls, "down", generator) == "testing___SomeClass_get_down"

def test_bitmap_attrs_stable_across_repeat_analysis(self) -> None:
# Regression: detect_undefined_bitmap used to mutate cl.bitmap_attrs
# in place, so under separate=True (one SCC per group) a shared base
# class would accumulate duplicate entries as each subclass's SCC
# walked into it, growing the emitted struct between builds.
base = ClassIR("Base", "mod")
base.attributes = {"i": int32_rprimitive}
sub = ClassIR("Sub", "mod")
sub.attributes = {"j": int32_rprimitive}
base.mro = base.base_mro = [base]
sub.mro = sub.base_mro = [sub, base]
base.children = [sub]

detect_undefined_bitmap(sub, seen=set())
for _ in range(10):
detect_undefined_bitmap(sub, seen=set())
assert base.bitmap_attrs == ["i"]
assert sub.bitmap_attrs == ["i", "j"]
Loading