#!/usr/bin/env python3 """Keep only non-GC subtype_dealloc's type_needs_decref C89-safe.""" 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 void\nsubtype_dealloc\(PyObject \*self\)\n\{(.*?)\n\}", source, re.S, ) assert match, "subtype_dealloc definition missing" body = match.group(1) start = body.index(" if (!_PyType_IS_GC(type)) {") end = body.index(" /* We get here only if the type has GC */", start) branch = re.sub(r"/\*.*?\*/|//[^\n]*", "", body[start:end], flags=re.S) assert re.match(r"\s*if \(!_PyType_IS_GC\(type\)\) \{\s*int type_needs_decref;", branch), ( "non-GC subtype_dealloc: type_needs_decref must be declared at block entry (C89)" ) expected = """ if (!_PyType_IS_GC(type)) { int type_needs_decref; if (type->tp_finalize) { if (PyObject_CallFinalizerFromDealloc(self) < 0) return; } if (type->tp_del) { type->tp_del(self); if (Py_REFCNT(self) > 0) { return; } } base = type; while ((basedealloc = base->tp_dealloc) == subtype_dealloc) { base = base->tp_base; assert(base); } type = Py_TYPE(self); type_needs_decref = (type->tp_flags & Py_TPFLAGS_HEAPTYPE && !(base->tp_flags & Py_TPFLAGS_HEAPTYPE)); assert((type->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0); assert(basedealloc); basedealloc(self); if (type_needs_decref) { Py_DECREF(type); } return; } """ assert branch.split() == expected.split(), ( "non-GC subtype_dealloc: preserve assignment evaluation point and all control flow" ) print("non-GC subtype_dealloc type_needs_decref C89 declaration and unchanged control flow: PASS")