49 lines
1.8 KiB
Python
49 lines
1.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Keep subtype_traverse's managed-dict err C89-safe without moving evaluation."""
|
|
import re
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
source = Path(sys.argv[1] if len(sys.argv) > 1 else "cpython/Objects/typeobject.c").read_text()
|
|
match = re.search(
|
|
r"\nstatic int\nsubtype_traverse\(PyObject \*self, visitproc visit, void \*arg\)\n\{(.*?)\n\}",
|
|
source,
|
|
re.S,
|
|
)
|
|
assert match, "subtype_traverse definition missing"
|
|
body = match.group(1)
|
|
assert re.match(
|
|
r"\s*PyTypeObject \*type, \*base;\s*traverseproc basetraverse;\s*int err;",
|
|
body,
|
|
), "subtype_traverse: managed-dict err must be declared at function entry (C89)"
|
|
assert len(re.findall(r"\bint\s+err\b", body)) == 2, (
|
|
"subtype_traverse: retain the separate slot-local err and one function-local err"
|
|
)
|
|
expected = """
|
|
if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
|
|
assert(type->tp_dictoffset);
|
|
err = _PyObject_VisitInstanceAttributes(self, visit, arg);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
"""
|
|
managed_dict = body[body.index(" if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT)"):body.index(" if (type->tp_dictoffset != base->tp_dictoffset)")]
|
|
assert managed_dict.split() == expected.split(), (
|
|
"subtype_traverse: preserve guarded evaluation after assertion and error propagation"
|
|
)
|
|
assert body.count("_PyObject_VisitInstanceAttributes(") == 1, (
|
|
"subtype_traverse: visit instance attributes exactly once"
|
|
)
|
|
slots = """
|
|
if (Py_SIZE(base)) {
|
|
int err = traverse_slots(base, self, visit, arg);
|
|
if (err)
|
|
return err;
|
|
}
|
|
"""
|
|
assert " ".join(slots.split()) in " ".join(body.split()), (
|
|
"subtype_traverse: preserve the slot-local err declaration and control flow"
|
|
)
|
|
print("subtype_traverse managed-dict err C89 declaration and unchanged evaluation/control flow: PASS")
|