#!/usr/bin/env bash # # Regression tests for scripts/vc6-probe-pythoncore-frontier.sh. # # The no-toolchain checks exercise the source-list parser/normalizer and the # per-file define handling that make the frontier source-list-driven. When # Wine + MSVC600 are available, a bounded live run verifies that the harness # reports cl.exe's real first-blocker status. # # Usage: scripts/vc6-probe-pythoncore-frontier.test.sh set -u REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" PROBE="$REPO_ROOT/scripts/vc6-probe-pythoncore-frontier.sh" fail=0 pass() { echo "ok - $1"; } bad() { echo "not ok - $1"; fail=1; } out="$($PROBE --list-only 2>&1)" status=$? if [ "$status" -eq 0 ]; then pass "frontier --list-only exits 0" else bad "frontier --list-only exits 0 (got exit $status)" echo "$out" fi count="$(printf '%s\n' "$out" | awk 'NF { n++ } END { print n+0 }')" if [ "$count" -eq 198 ]; then pass "frontier parses all 198 pythoncore.vcxproj ClCompile entries" else bad "frontier parses all 198 pythoncore.vcxproj ClCompile entries (got $count)" fi first_fields="$(printf '%s\n' "$out" | awk -F '\t' 'NR == 1 { print $1 "|" $2 "|" $3 }')" if [ "$first_fields" = 'compile|cpython/Modules/getpath.c|..\Modules\getpath.c' ]; then pass "frontier normalizes the first Windows-style source path to cpython/Modules/getpath.c" else bad "frontier normalizes the first Windows-style source path to cpython/Modules/getpath.c (got: $first_fields)" fi getpath_defs="$(printf '%s\n' "$out" | awk -F '\t' '$2 == "cpython/Modules/getpath.c" { print $4; exit }')" case "$getpath_defs" in *'PREFIX=NULL'*'EXEC_PREFIX=NULL'*'VERSION=NULL'*'VPATH="..\\.."'*'PYDEBUGEXT=""'*'PLATLIBDIR="DLLs"'*) pass "frontier preserves getpath.c per-file defines with project-local substitutions" ;; *) bad "frontier preserves getpath.c per-file defines with project-local substitutions (got: $getpath_defs)" ;; esac sysmodule_defs="$(printf '%s\n' "$out" | awk -F '\t' '$2 == "cpython/Python/sysmodule.c" { print $4; exit }')" case "$sysmodule_defs" in *'VPATH="..\\.."'*) pass "frontier preserves sysmodule.c VPATH per-file define" ;; *) bad "frontier preserves sysmodule.c VPATH per-file define (got: $sysmodule_defs)" ;; esac binascii_defs="$(printf '%s\n' "$out" | awk -F '\t' '$2 == "cpython/Modules/binascii.c" { print $4; exit }')" case "$binascii_defs" in *USE_ZLIB_CRC32*) bad "frontier disables unavailable zlib CRC32 path for binascii.c (got: $binascii_defs)" ;; *) pass "frontier disables unavailable zlib CRC32 path for binascii.c" ;; esac # Source the probe after --list-only so the guarded helper definitions can be # tested without launching Wine or requiring MSVC600. # shellcheck source=scripts/vc6-probe-pythoncore-frontier.sh source "$PROBE" rendered_cmd="$(render_compile_command_for_test 'VPATH="..\\.."' 'PYDEBUGEXT=""' 'PLATLIBDIR="DLLs"')" case "$rendered_cmd" in *' /D VPATH=\"..\\\\..\"'*' /D PYDEBUGEXT=\"\"'*' /D PLATLIBDIR=\"DLLs\"'*) pass "frontier renders quoted per-file string defines safely for CL response files" ;; *) bad "frontier renders quoted per-file string defines safely for CL response files (got: $rendered_cmd)" ;; esac if printf '%s\n' "$out" | grep -q $'^skip-generated deepfreeze source\tcpython/Python/deepfreeze/deepfreeze.c\t'; then pass "frontier reports the generated deepfreeze source instead of compiling it blindly" else bad "frontier reports the generated deepfreeze source instead of compiling it blindly" fi if printf '%s\n' "$out" | grep -q $'^skip-external IncludeExternals-conditioned source\tcpython/Modules/zlibmodule.c\t'; then pass "frontier reports IncludeExternals-conditioned in-tree zlibmodule.c as external" else bad "frontier reports IncludeExternals-conditioned in-tree zlibmodule.c as external" fi if printf '%s\n' "$out" | grep -q $'^skip-external IncludeExternals-conditioned source\t$(zlibDir)/adler32.c\t'; then pass "frontier reports macro-based zlibDir sources as external without normalizing outside the repo" else bad "frontier reports macro-based zlibDir sources as external without normalizing outside the repo" fi if printf '%s\n' "$out" | cut -f2 | grep -q '\\'; then bad "frontier normalized source column contains no backslashes" else pass "frontier normalized source column contains no backslashes" fi # Keep the current cmath.c VC6 frontier explicit: the old compiler has no C99 # NAN macro, so pymath.h must provide Py_NAN without referring to NAN. if python3 - "$REPO_ROOT/cpython/Include/pymath.h" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() if "# if defined(_MSC_VER) && _MSC_VER < 1300" not in source: raise SystemExit(1) start = source.index("#if !defined(Py_NAN)") end = source.index("#endif\n\n#endif /* Py_PYMATH_H */", start) block = source[start:end] if "# define Py_NAN (Py_HUGE_VAL * 0.0)" not in block: raise SystemExit(1) if "# define Py_NAN ((double)NAN)" not in block: raise SystemExit(1) PY then pass "pymath.h provides a VC6 Py_NAN fallback without C99 NAN" else bad "pymath.h provides a VC6 Py_NAN fallback without C99 NAN" fi # Keep the first _pickle.c VC6/C89 slice explicit: batch_list_exact() must # declare its shared error local before executable statements. if python3 - "$REPO_ROOT/cpython/Modules/_pickle.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("batch_list_exact(PicklerObject *self, PyObject *obj)") end = source.index("\n}\n\nstatic int\nsave_list", start) + 2 function = source[start:end] if " int err;" not in function: raise SystemExit(1) if " int err = save(self, item, 0);" in function: raise SystemExit(1) if function.count(" err = save(self, item, 0);") != 2: raise SystemExit(1) PY then pass "_pickle batch_list_exact uses C89 declaration-order-safe error local" else bad "_pickle batch_list_exact uses C89 declaration-order-safe error local" fi # Keep the next _pickle.c VC6/C89 slice explicit: save_set() must declare its # shared error local before the loop's executable statements. if python3 - "$REPO_ROOT/cpython/Modules/_pickle.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("save_set(PicklerObject *self, PyObject *obj)") end = source.index("\n}\n\nstatic int\nsave_frozenset", start) + 2 function = source[start:end] if " int err;" not in function: raise SystemExit(1) if " int err = save(self, item, 0);" in function: raise SystemExit(1) if function.count(" err = save(self, item, 0);") != 1: raise SystemExit(1) PY then pass "_pickle save_set uses C89 declaration-order-safe error local" else bad "_pickle save_set uses C89 declaration-order-safe error local" fi # Keep the current _pickle.c VC6/C89 frontier explicit: Pickler_traverse() # must declare memo before the preceding Py_VISIT executable statements. if python3 - "$REPO_ROOT/cpython/Modules/_pickle.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("Pickler_traverse(PicklerObject *self, visitproc visit, void *arg)") end = source.index("\n}\n\nstatic int\nPickler_clear", start) + 2 function = source[start:end] if " PyMemoTable *memo;" not in function: raise SystemExit(1) if " PyMemoTable *memo = self->memo;" in function: raise SystemExit(1) if function.count(" memo = self->memo;") != 1: raise SystemExit(1) PY then pass "_pickle Pickler_traverse uses C89 declaration-order-safe memo local" else bad "_pickle Pickler_traverse uses C89 declaration-order-safe memo local" fi # Keep the current _pickle.c VC6/C89 frontier explicit: Unpickler_traverse() # must declare memo before the preceding Py_VISIT executable statements. if python3 - "$REPO_ROOT/cpython/Modules/_pickle.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("Unpickler_traverse(UnpicklerObject *self, visitproc visit, void *arg)") end = source.index("\n}\n\nstatic int\nUnpickler_clear", start) + 2 function = source[start:end] if " PyObject **memo;" not in function: raise SystemExit(1) if " PyObject **memo = self->memo;" in function: raise SystemExit(1) if function.count(" memo = self->memo;") != 1: raise SystemExit(1) PY then pass "_pickle Unpickler_traverse uses C89 declaration-order-safe memo local" else bad "_pickle Unpickler_traverse uses C89 declaration-order-safe memo local" fi # The next _pickle.c VC6/C89 slice is PicklerMemoProxy.copy(): its loop counter # must be declared before executable statements and assigned in the for init. if python3 - "$REPO_ROOT/cpython/Modules/_pickle.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("_pickle_PicklerMemoProxy_copy_impl(PicklerMemoProxyObject *self)") end = source.index("\n}\n\n/*[clinic input]", start) + 2 function = source[start:end] if " size_t i;" not in function: raise SystemExit(1) if "for (size_t i = 0;" in function: raise SystemExit(1) if function.count("for (i = 0;") != 1: raise SystemExit(1) PY then pass "_pickle PicklerMemoProxy.copy uses C89 declaration-order-safe loop counter" else bad "_pickle PicklerMemoProxy.copy uses C89 declaration-order-safe loop counter" fi # Keep the current _pickle.c frontier explicit: load_readonly_buffer() must # hoist its locals before the initial executable stack-underflow check. if python3 - "$REPO_ROOT/cpython/Modules/_pickle.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("load_readonly_buffer(UnpicklerObject *self)") end = source.index("\n}\n\nstatic int\nload_unicode", start) + 2 function = source[start:end] if " Py_ssize_t len;" not in function: raise SystemExit(1) if " PyObject *obj;" not in function: raise SystemExit(1) if " PyObject *view;" not in function: raise SystemExit(1) if " Py_ssize_t len = Py_SIZE(self->stack);" in function: raise SystemExit(1) if " PyObject *obj = self->stack->data[len - 1];" in function: raise SystemExit(1) if " PyObject *view = PyMemoryView_FromObject(obj);" in function: raise SystemExit(1) if function.count(" len = Py_SIZE(self->stack);") != 1: raise SystemExit(1) if function.count(" obj = self->stack->data[len - 1];") != 1: raise SystemExit(1) if function.count(" view = PyMemoryView_FromObject(obj);") != 1: raise SystemExit(1) PY then pass "_pickle load_readonly_buffer uses C89 declaration-order-safe locals" else bad "_pickle load_readonly_buffer uses C89 declaration-order-safe locals" fi # Keep the current _pickle.c frontier explicit: load_next_buffer() must hoist # its locals before the initial executable NULL check. if python3 - "$REPO_ROOT/cpython/Modules/_pickle.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("load_next_buffer(UnpicklerObject *self)") end = source.index("\n}\n\nstatic int\nload_readonly_buffer", start) + 2 function = source[start:end] if " PickleState *st;" not in function: raise SystemExit(1) if " PyObject *buf;" not in function: raise SystemExit(1) if " PickleState *st = _Pickle_GetGlobalState();" in function: raise SystemExit(1) if " PyObject *buf = PyIter_Next(self->buffers);" in function: raise SystemExit(1) if function.count(" st = _Pickle_GetGlobalState();") != 2: raise SystemExit(1) if " buf = PyIter_Next(self->buffers);" not in function: raise SystemExit(1) PY then pass "_pickle load_next_buffer uses C89 declaration-order-safe locals" else bad "_pickle load_next_buffer uses C89 declaration-order-safe locals" fi # Keep the current _pickle.c VC6/C89 frontier explicit: Unpickler_set_memo() # must hoist its loop counter before both executable loop sites. if python3 - "$REPO_ROOT/cpython/Modules/_pickle.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("Unpickler_set_memo(UnpicklerObject *self") end = source.index("\n}\n\nstatic PyObject *\nUnpickler_get_persload", start) + 2 function = source[start:end] if " size_t i;" not in function: raise SystemExit(1) if "for (size_t i = new_memo_size - 1;" in function: raise SystemExit(1) if "for (size_t i = 0;" in function: raise SystemExit(1) if function.count("for (i = 0;") != 1: raise SystemExit(1) if function.count("for (i = new_memo_size; i-- > 0;)") != 1: raise SystemExit(1) PY then pass "_pickle Unpickler_set_memo uses C89 declaration-order-safe loop counter" else bad "_pickle Unpickler_set_memo uses C89 declaration-order-safe loop counter" fi # The active _pickle.c frontier must not depend on SIZE_MAX, which VC6's # standard headers do not provide. The cleanup loop must retain the same # descending index sequence using a C89-safe post-decrement condition. if python3 - "$REPO_ROOT/cpython/Modules/_pickle.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("Unpickler_set_memo(UnpicklerObject *self") end = source.index("\n}\n\nstatic PyObject *\nUnpickler_get_persload", start) + 2 function = source[start:end] if "SIZE_MAX" in function: raise SystemExit(1) if "for (i--; i != 0; i--)" in function: raise SystemExit(1) if "for (i = new_memo_size; i-- > 0;)" not in function: raise SystemExit(1) PY then pass "_pickle Unpickler_set_memo cleanup avoids unavailable SIZE_MAX" else bad "_pickle Unpickler_set_memo cleanup avoids unavailable SIZE_MAX" fi # Keep the current _sha3 VC6/C89 frontier explicit: SHA3_dealloc() # must hoist tp before the preceding executable cleanup statements. if python3 - "$REPO_ROOT/cpython/Modules/_sha3/sha3module.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("SHA3_dealloc(SHA3object *self)") end = source.index("\n}\n", start) + 2 function = source[start:end] if " PyTypeObject *tp;" not in function: raise SystemExit(1) if " PyTypeObject *tp = Py_TYPE(self);" in function: raise SystemExit(1) if function.count(" tp = Py_TYPE(self);") != 1: raise SystemExit(1) sequence = ( "SHA3_dealloc(SHA3object *self) { PyTypeObject *tp; " "if (self->lock) { PyThread_free_lock(self->lock); } " "tp = Py_TYPE(self); PyObject_Free(self); Py_DECREF(tp); }" ) if sequence not in " ".join(function.split()): raise SystemExit(1) PY then pass "_sha3 SHA3_dealloc uses C89 declaration-order-safe tp local" else bad "_sha3 SHA3_dealloc uses C89 declaration-order-safe tp local" fi # Keep the current _randommodule.c VC6/C89 frontier explicit: _random_exec() # must hoist locals while preserving the allocation/error/refcount sequence. if python3 - "$REPO_ROOT/cpython/Modules/_randommodule.c" <<'PY' import re import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("_random_exec(PyObject *module)\n{") end = source.index("\n}\n", start) + 2 function = source[start:end] function = re.sub(r"/\\*.*?\\*/", "", function, flags=re.S) function = " ".join(function.split()) if not function.startswith( "_random_exec(PyObject *module) { " "_randomstate *state = get_random_state(module); " "PyObject *longval; PyObject *longtype; " "state->Random_Type = PyType_FromModuleAndSpec(" ): raise SystemExit(1) if "PyObject *longval =" in function or "PyObject *longtype =" in function: raise SystemExit(1) if function.count("longval = PyLong_FromLong(0);") != 1: raise SystemExit(1) if function.count("longtype = PyObject_Type(longval);") != 1: raise SystemExit(1) sequence = ( "if (PyModule_AddType(module, (PyTypeObject *)state->Random_Type) < 0) { " "return -1; } " "longval = PyLong_FromLong(0); " "if (longval == NULL) { return -1; } " "longtype = PyObject_Type(longval); " "Py_DECREF(longval); " "if (longtype == NULL) { return -1; } " 'state->Long___abs__ = PyObject_GetAttrString(longtype, "__abs__"); ' "Py_DECREF(longtype); " "if (state->Long___abs__ == NULL) { return -1; } " "return 0; }" ) if sequence not in function: raise SystemExit(1) PY then pass "_random_exec uses C89 locals and preserves initialization order" else bad "_random_exec uses C89 locals and preserves initialization order" fi # Keep the current _sha3 frontier explicit: its type and module definitions must # use positional initializers because VC6 has no C99 designated initializers. if python3 - "$REPO_ROOT/cpython/Modules/_sha3/sha3module.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("#define SHA3_TYPE_SPEC") end = source.index("\n\nPyDoc_STRVAR(sha3_224__doc__", start) macro = source[start:end] expected_macro = '''#define SHA3_TYPE_SPEC(type_spec_obj, type_name, type_slots) \\ static PyType_Spec type_spec_obj = { \\ "_sha3." type_name, \\ sizeof(SHA3object), \\ 0, \\ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE, \\ type_slots \\ }''' if macro != expected_macro: raise SystemExit(1) start = source.index("static struct PyModuleDef _sha3module = {") end = source.index("\n};", start) + 3 module = source[start:end] expected_module = '''static struct PyModuleDef _sha3module = { PyModuleDef_HEAD_INIT, "_sha3", NULL, sizeof(SHA3State), NULL, _sha3_slots, _sha3_traverse, _sha3_clear, _sha3_free, };''' if module != expected_module: raise SystemExit(1) PY then pass "_sha3 uses VC6-safe positional type and module initializers" else bad "_sha3 uses VC6-safe positional type and module initializers" fi # Keep the current _sre frontier explicit: SRE(match) must declare ptr before # executable statements, including the entrance label. if python3 - "$REPO_ROOT/cpython/Modules/_sre/sre_lib.h" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("SRE(match)(SRE_STATE* state, const SRE_CODE* pattern, int toplevel)") end = source.index("\n}\n\n/* need to reset capturing groups", start) + 2 function = source[start:end] if " const SRE_CHAR *ptr;" not in function: raise SystemExit(1) if " const SRE_CHAR *ptr = (SRE_CHAR *)state->ptr;" in function: raise SystemExit(1) if function.count(" ptr = (SRE_CHAR *)state->ptr;") != 1: raise SystemExit(1) PY then pass "_sre SRE(match) uses C89 declaration-order-safe ptr local" else bad "_sre SRE(match) uses C89 declaration-order-safe ptr local" fi # The active _sre VC6/C89 frontier must declare repeat_of_tail before the # executable DO_JUMP statement that assigns it. if python3 - "$REPO_ROOT/cpython/Modules/_sre/sre_lib.h" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("SRE(match)(SRE_STATE* state, const SRE_CODE* pattern, int toplevel)") end = source.index("\n}\n\n/* need to reset capturing groups", start) + 2 function = source[start:end] if " SRE_REPEAT *repeat_of_tail;" not in function: raise SystemExit(1) if " SRE_REPEAT *repeat_of_tail = state->repeat;" in function: raise SystemExit(1) if function.count(" repeat_of_tail = state->repeat;") != 1: raise SystemExit(1) PY then pass "_sre MIN_UNTIL uses C89 declaration-order-safe repeat local" else bad "_sre MIN_UNTIL uses C89 declaration-order-safe repeat local" fi # The active _sre VC6/C89 frontier must declare rc before the preceding # VTRACE executable statement in the GROUPREF_EXISTS validation case. if python3 - "$REPO_ROOT/cpython/Modules/_sre/sre.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("_validate_inner(SRE_CODE *code, SRE_CODE *end, Py_ssize_t groups)") end = source.index("\n}\n\nstatic int\n_validate_outer", start) + 2 function = source[start:end] if " int rc;" not in function: raise SystemExit(1) if " int rc = _validate_inner(code+1, code+skip-1, groups);" in function: raise SystemExit(1) if function.count(" rc = _validate_inner(code+1, code+skip-1, groups);") != 1: raise SystemExit(1) PY then pass "_sre GROUPREF_EXISTS uses C89 declaration-order-safe rc local" else bad "_sre GROUPREF_EXISTS uses C89 declaration-order-safe rc local" fi # The active _sre designated-initializer frontier must use C89-compatible # positional PyType_Spec initializers in declared field order. if python3 - "$REPO_ROOT/cpython/Modules/_sre/sre.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() expected = { "pattern_spec": '''static PyType_Spec pattern_spec = { "re.Pattern", sizeof(PatternObject), sizeof(SRE_CODE), (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_HAVE_GC), pattern_slots, };''', "match_spec": '''static PyType_Spec match_spec = { "re.Match", sizeof(MatchObject), sizeof(Py_ssize_t), (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_HAVE_GC), match_slots, };''', "scanner_spec": '''static PyType_Spec scanner_spec = { "_" SRE_MODULE ".SRE_Scanner", sizeof(ScannerObject), 0, (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_DISALLOW_INSTANTIATION | Py_TPFLAGS_HAVE_GC), scanner_slots, };''', } for name, block in expected.items(): if block not in source: raise SystemExit(name) if ".name = \"re.Pattern\"" in source or ".name = \"re.Match\"" in source: raise SystemExit("designated initializer remains") PY then pass "_sre PyType_Spec definitions use C89-compatible positional initializers" else bad "_sre PyType_Spec definitions use C89-compatible positional initializers" fi # The active _sre module definition must use C89-compatible positional # initializers in the declared PyModuleDef field order. if python3 - "$REPO_ROOT/cpython/Modules/_sre/sre.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() expected = '''static struct PyModuleDef sremodule = { PyModuleDef_HEAD_INIT, "_" SRE_MODULE, NULL, sizeof(_sremodulestate), _functions, sre_slots, sre_traverse, sre_clear, sre_free, };''' if expected not in source: raise SystemExit("sremodule positional initializer mismatch") body = source[source.index("static struct PyModuleDef sremodule = {"):] body = body[:body.index("\n};")] if ".m_" in body: raise SystemExit("sremodule designated initializer remains") PY then pass "_sre sremodule uses C89-compatible positional initializers" else bad "_sre sremodule uses C89-compatible positional initializers" fi # The active _sre VC6/C89 frontier must declare ADD_ULONG_CONSTANT's result # before the executable NULL check shared by both macro call sites. if python3 - "$REPO_ROOT/cpython/Modules/_sre/sre.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("#define ADD_ULONG_CONSTANT(") end = source.index("\n\n", start) actual = [ line.rstrip().removesuffix("\\").strip() for line in source[start:end].splitlines() ] expected = [ "#define ADD_ULONG_CONSTANT(module, name, value)", "do {", "PyObject *o = PyLong_FromUnsignedLong(value);", "int res;", "if (!o)", "goto error;", "res = PyModule_AddObjectRef(module, name, o);", "Py_DECREF(o);", "if (res < 0) {", "goto error;", "}", "} while (0)", ] if actual != expected: raise SystemExit("ADD_ULONG_CONSTANT declaration/order mismatch") PY then pass "_sre ADD_ULONG_CONSTANT uses C89 declaration-order-safe res local" else bad "_sre ADD_ULONG_CONSTANT uses C89 declaration-order-safe res local" fi # Keep the active arraymodule.c VC6/C89 frontier explicit: u_setitem() # must declare len and w before executable statements. if python3 - "$REPO_ROOT/cpython/Modules/arraymodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("u_setitem(arrayobject *ap, Py_ssize_t i, PyObject *v)") end = source.index("\n}\n\n\nstatic PyObject *\nh_getitem", start) + 2 function = source[start:end] if " Py_ssize_t len;" not in function: raise SystemExit(1) if " wchar_t w;" not in function: raise SystemExit(1) if " Py_ssize_t len = PyUnicode_AsWideChar(u, NULL, 0);" in function: raise SystemExit(1) if " wchar_t w;\n len =" in function: raise SystemExit(1) if function.count(" len = PyUnicode_AsWideChar(u, NULL, 0);") != 1: raise SystemExit(1) if " len = PyUnicode_AsWideChar(u, &w, 1);" not in function: raise SystemExit(1) PY then pass "arraymodule u_setitem uses C89 declaration-order-safe len and w locals" else bad "arraymodule u_setitem uses C89 declaration-order-safe len and w locals" fi # Keep the next arraymodule.c VC6/C89 frontier explicit: array_repeat() # must declare locals before executable statements and preserve assignment order. if python3 - "$REPO_ROOT/cpython/Modules/arraymodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("array_repeat(arrayobject *a, Py_ssize_t n)\n{") end = source.index("\n}\n\nstatic int\narray_del_slice", start) + 2 function = source[start:end] for declaration in (" Py_ssize_t array_length;", " Py_ssize_t size;", " arrayobject *np;", " Py_ssize_t oldbytes;", " Py_ssize_t newbytes;"): if declaration not in function: raise SystemExit("array_repeat declaration missing: " + declaration) for declaration in (" const Py_ssize_t array_length =", " Py_ssize_t size =", " arrayobject* np =", " const Py_ssize_t oldbytes =", " const Py_ssize_t newbytes ="): if declaration in function: raise SystemExit("array_repeat retains mixed declaration: " + declaration) expected = ( " array_length = Py_SIZE(a);\n" " if ((array_length != 0) && (n > PY_SSIZE_T_MAX / array_length))" ) if expected not in function: raise SystemExit("array_repeat array_length assignment sequence changed") if " size = array_length * n;" not in function: raise SystemExit("array_repeat size assignment missing") if " np = (arrayobject *) newarrayobject" not in function: raise SystemExit("array_repeat np assignment missing") if " oldbytes = array_length * a->ob_descr->itemsize;" not in function: raise SystemExit("array_repeat oldbytes assignment missing") if " newbytes = oldbytes * n;" not in function: raise SystemExit("array_repeat newbytes assignment missing") PY then pass "arraymodule array_repeat uses C89 declaration-order-safe locals" else bad "arraymodule array_repeat uses C89 declaration-order-safe locals" fi # The current arraymodule.c VC6/C89 frontier must declare array_inplace_repeat # locals before the executable conditional block. if python3 - "$REPO_ROOT/cpython/Modules/arraymodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("array_inplace_repeat(arrayobject *self, Py_ssize_t n)\n{") end = source.index("\n}\n\n\nstatic PyObject *\nins(", start) + 2 function = source[start:end] opening = function[:function.index(" if (array_size > 0")] if " Py_ssize_t size;\n" not in opening: raise SystemExit("array_inplace_repeat size declaration missing from opening group") if " Py_ssize_t size = array_size * self->ob_descr->itemsize;" in function: raise SystemExit("array_inplace_repeat retains mixed declaration") if function.count(" size = array_size * self->ob_descr->itemsize;") != 1: raise SystemExit("array_inplace_repeat size assignment missing") PY then pass "arraymodule array_inplace_repeat uses C89 declaration-order-safe size local" else bad "arraymodule array_inplace_repeat uses C89 declaration-order-safe size local" fi # The next arraymodule.c VC6/C89 frontier must declare the index loop counter # before the preceding executable start/stop normalization statements. if python3 - "$REPO_ROOT/cpython/Modules/arraymodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("array_array_index_impl(arrayobject *self, PyObject *v, Py_ssize_t start,") end = source.index("\n}\n\nstatic int\narray_contains", start) + 2 function = source[start:end] opening = function[:function.index(" if (start < 0)")] if " Py_ssize_t i;\n" not in opening: raise SystemExit("array_array_index_impl loop counter declaration missing from opening group") if "for (Py_ssize_t i = start;" in function: raise SystemExit("array_array_index_impl retains C99 loop declaration") if function.count(" for (i = start; i < stop && i < Py_SIZE(self); i++) {") != 1: raise SystemExit("array_array_index_impl loop assignment sequence changed") PY then pass "arraymodule array_array_index_impl uses C89 declaration-order-safe loop counter" else bad "arraymodule array_array_index_impl uses C89 declaration-order-safe loop counter" fi # The active arraymodule.c VC6/C89 frontier must hoist array_state declarations # while preserving the guarded lookup points in fromfile() and tofile(). if python3 - "$REPO_ROOT/cpython/Modules/arraymodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() checks = { "fromfile": ( "array_array_fromfile_impl(arrayobject *self, PyTypeObject *cls, PyObject *f,", "\n}\n\n/*[clinic input]\narray.array.tofile", " int not_enough_bytes;\n", " nbytes = n * itemsize;\n", ), "tofile": ( "array_array_tofile_impl(arrayobject *self, PyTypeObject *cls, PyObject *f)", "\n}\n\n/*[clinic input]\narray.array.fromlist", " Py_ssize_t i;\n", " for (i = 0; i < nblocks; i++) {\n", ), } for name, (signature, end_marker, declaration_anchor, after_lookup) in checks.items(): start = source.index(signature) end = source.index(end_marker, start) + 2 function = source[start:end] opening = function[:function.index(" if ")] if " array_state *state;\n" not in opening: raise SystemExit(name + ": array_state declaration missing from opening group") if " array_state *state = get_array_state_by_class(cls);" in function: raise SystemExit(name + ": array_state declaration retains initializer") if function.count(" state = get_array_state_by_class(cls);\n") != 1: raise SystemExit(name + ": state assignment missing or duplicated") if " state = get_array_state_by_class(cls);\n assert(state != NULL);\n" not in function: raise SystemExit(name + ": state lookup/assert sequence changed") if function.index(declaration_anchor) >= function.index(" if "): raise SystemExit(name + ": declaration anchor moved after executable code") lookup = function.index(" state = get_array_state_by_class(cls);") if function.index(after_lookup, lookup) <= lookup: raise SystemExit(name + ": post-lookup sequence changed") PY then pass "arraymodule fromfile/tofile use C89 declaration-order-safe state locals" else bad "arraymodule fromfile/tofile use C89 declaration-order-safe state locals" fi # The active arraymodule.c VC6/C89 frontier must hoist fromunicode() locals # without moving either initializer past its original guard/decrement point. if python3 - "$REPO_ROOT/cpython/Modules/arraymodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() signature = "array_array_fromunicode_impl(arrayobject *self, PyObject *ustr)" start = source.index(signature) end = source.index("\n}\n\n/*[clinic input]\narray.array.tounicode", start) + 2 function = source[start:end] opening = function[:function.index(" if ")] if " Py_ssize_t ustr_length;\n" not in opening: raise SystemExit("fromunicode: ustr_length declaration missing from opening group") if " Py_ssize_t old_size;\n" not in function[:function.index(" ustr_length--")]: raise SystemExit("fromunicode: old_size declaration missing before decrement") if " Py_ssize_t ustr_length = PyUnicode_AsWideChar(ustr, NULL, 0);" in function: raise SystemExit("fromunicode: ustr_length declaration retains initializer") if " Py_ssize_t old_size = Py_SIZE(self);" in function: raise SystemExit("fromunicode: old_size declaration retains initializer") if " ustr_length = PyUnicode_AsWideChar(ustr, NULL, 0);\n assert(ustr_length > 0);" not in function: raise SystemExit("fromunicode: Unicode conversion assignment/assert sequence changed") if " old_size = Py_SIZE(self);\n if (array_resize(self, old_size + ustr_length) == -1)" not in function: raise SystemExit("fromunicode: old_size assignment/resize sequence changed") PY then pass "arraymodule fromunicode uses C89 declaration-order-safe locals" else bad "arraymodule fromunicode uses C89 declaration-order-safe locals" fi # Keep the current arraymodule.c VC6/C89 frontier explicit: array_richcompare() # must declare cmp before the executable fast-path statements. if python3 - "$REPO_ROOT/cpython/Modules/arraymodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("array_richcompare(PyObject *v, PyObject *w, int op)") end = source.index("\n}\n\nstatic PyMethodDef", start) + 2 function = source[start:end] opening = function[:function.index(" if (!array_Check")] if " int cmp;\n" not in opening: raise SystemExit("array_richcompare cmp declaration missing from opening group") fast_path = function[function.index(" if (va->ob_descr"):function.index("\n }\n\n\n /* Search", function.index(" if (va->ob_descr"))] if " int cmp;\n" in fast_path: raise SystemExit("array_richcompare cmp declaration remains after executable statements") size_path = function[function.index(" compare_sizes: ;"):function.index("\n }\n\n /* We have an item", function.index(" compare_sizes: ;"))] if " int cmp;\n" in size_path: raise SystemExit("array_richcompare compare_sizes retains duplicate cmp declaration") if size_path.count(" case Py_LT: cmp = vs < ws; break;") != 1: raise SystemExit("array_richcompare compare_sizes cmp assignment sequence changed") if fast_path.count(" case Py_LT: cmp = result < 0; break;") != 1: raise SystemExit("array_richcompare cmp assignment sequence changed") PY then pass "arraymodule array_richcompare uses C89 declaration-order-safe cmp local" else bad "arraymodule array_richcompare uses C89 declaration-order-safe cmp local" fi # The next arraymodule.c VC6/C89 frontier must reuse the opening res local; # VC6 rejects the fast-path declaration after executable statements. if python3 - "$REPO_ROOT/cpython/Modules/arraymodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("array_richcompare(PyObject *v, PyObject *w, int op)\n{") end = source.index("\n}\n\nstatic Py_ssize_t\narray_length(", start) + 2 function = source[start:end] opening = function[:function.index(" if (!array_Check")] if opening.count(" PyObject *res;\n") != 1: raise SystemExit("array_richcompare opening res declaration missing") start = function.index(" if (va->ob_descr") fast_path = function[start:function.index("\n }\n\n\n /* Search", start)] if "PyObject *res" in fast_path: raise SystemExit("array_richcompare fast path retains res declaration") if fast_path.count( " }\n" " res = cmp ? Py_True : Py_False;\n" " Py_INCREF(res);\n" " return res;" ) != 1: raise SystemExit("array_richcompare fast-path res assignment sequence changed") PY then pass "arraymodule array_richcompare reuses C89-safe res local" else bad "arraymodule array_richcompare reuses C89-safe res local" fi # The next arraymodule.c VC6/C89 frontier must hoist the compare-size locals # before the label and executable statements in the shared-size path. if python3 - "$REPO_ROOT/cpython/Modules/arraymodule.c" <<'PY' import re import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("array_richcompare(PyObject *v, PyObject *w, int op)\n{") end = source.index("\n}\n\nstatic Py_ssize_t\narray_length(", start) + 2 function = source[start:end] opening = function[:function.index(" if (!array_Check")] if re.search(r" Py_ssize_t [^;]*\bvs\b[^;]*\bws\b[^;]*;\n", opening) is None: raise SystemExit("array_richcompare size locals missing from opening group") if " Py_ssize_t vs = Py_SIZE(va);" in function: raise SystemExit("array_richcompare retains post-label vs declaration") if " Py_ssize_t ws = Py_SIZE(wa);" in function: raise SystemExit("array_richcompare retains post-label ws declaration") if function.count(" vs = Py_SIZE(va);\n ws = Py_SIZE(wa);") != 1: raise SystemExit("array_richcompare size assignments changed") PY then pass "arraymodule array_richcompare uses C89 declaration-order-safe size locals" else bad "arraymodule array_richcompare uses C89 declaration-order-safe size locals" fi # Keep the current arraymodule.c VC6/C89 frontier explicit: getarrayitem() # must declare ap before the debug-only executable assertion. if python3 - "$REPO_ROOT/cpython/Modules/arraymodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("getarrayitem(PyObject *op, Py_ssize_t i)") end = source.index("\n}\n\nstatic int\nins1", start) + 2 function = source[start:end] if "{\n arrayobject *ap;\n#ifndef NDEBUG" not in function: raise SystemExit("getarrayitem ap declaration is not before debug block") if function.count(" arrayobject *ap;") != 1: raise SystemExit("getarrayitem ap declaration is duplicated") if " ap = (arrayobject *)op;" not in function: raise SystemExit("getarrayitem ap assignment missing") if " arrayobject *ap = (arrayobject *)op;" in function: raise SystemExit("getarrayitem retains declaration initializer") PY then pass "arraymodule getarrayitem uses C89 declaration-order-safe ap local" else bad "arraymodule getarrayitem uses C89 declaration-order-safe ap local" fi # Keep the active _struct.c VC6 frontier explicit: MSVC6 has no C99 _Bool, # so the module must provide a one-byte compatibility typedef locally. if python3 - "$REPO_ROOT/cpython/Modules/_struct.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() expected = '''#if defined(_MSC_VER) && _MSC_VER < 1300 /* MSVC6 has no C99 _Bool; preserve the one-byte layout used by struct. */ typedef unsigned char _Bool; #endif''' if expected not in source: raise SystemExit("_struct VC6 _Bool compatibility typedef missing") PY then pass "_struct provides a guarded one-byte VC6 _Bool compatibility typedef" else bad "_struct provides a guarded one-byte VC6 _Bool compatibility typedef" fi # The active _struct.c VC6 frontier must spell its 64-bit integer types through # CPython's PY_LONG_LONG abstraction because VC6 rejects the long long keyword. if python3 - "$REPO_ROOT/cpython/Modules/_struct.c" <<'PY' import re import sys from pathlib import Path source = Path(sys.argv[1]).read_text() code = re.sub(r"/\*.*?\*/", "", source, flags=re.S) doc_start = code.index("PyDoc_STRVAR(module_doc,") doc_end = code.index('\\n");', doc_start) + 3 code = code[:doc_start] + code[doc_end:] if re.search(r"\b(?:unsigned\s+)?long\s+long\b", code): raise SystemExit("_struct long long keyword remains in code") if code.count("PY_LONG_LONG") < 20: raise SystemExit("_struct PY_LONG_LONG replacements are incomplete") if code.count("unsigned PY_LONG_LONG") < 7: raise SystemExit("_struct unsigned PY_LONG_LONG replacements are incomplete") PY then pass "_struct routes signed and unsigned 64-bit types through PY_LONG_LONG" else bad "_struct routes signed and unsigned 64-bit types through PY_LONG_LONG" fi # Keep the active _stat.c VC6/C89 frontier explicit: stat_exec() must place # its constant table and loop counter before the initial executable macro call. if python3 - "$REPO_ROOT/cpython/Modules/_stat.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("stat_exec(PyObject *module)\n{") end = source.index("\n}\n\n\nstatic PyModuleDef_Slot stat_slots", start) + 2 function = source[start:end] first_statement = function.index(" ADD_INT_MACRO(module, S_IFDIR);") if function.index(" const char* st_constants[] = {") >= first_statement: raise SystemExit(1) if function.index(" };", function.index("st_constants[]")) >= first_statement: raise SystemExit(1) if function.index(" int i;") >= first_statement: raise SystemExit(1) if "for (int i" in function: raise SystemExit(1) if "for (i = 0; i < (int)Py_ARRAY_LENGTH(st_constants); i++)" not in function: raise SystemExit(1) PY then pass "_stat stat_exec uses C89 declaration-order-safe constants and loop counter" else bad "_stat stat_exec uses C89 declaration-order-safe constants and loop counter" fi # The active _stat.c frontier must provide guarded Win9x/VC6 fallbacks for # constants absent from the VC6 Windows headers, without overriding headers # that already define them. if python3 - "$REPO_ROOT/cpython/Modules/_stat.c" <<'PY' import re import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("#ifdef MS_WINDOWS") end = source.index("#endif /* MS_WINDOWS */", start) block = source[start:end] expected = { "FILE_ATTRIBUTE_DEVICE": "0x0040", "FILE_ATTRIBUTE_VIRTUAL": "0x10000", "IO_REPARSE_TAG_SYMLINK": "0xA000000CL", } for name, value in expected.items(): pattern = rf"#ifndef {name}\s+#\s*define {name}\s+{re.escape(value)}\s+#endif" if re.search(pattern, block) is None: raise SystemExit(name) PY then pass "_stat provides guarded VC6/Win9x file-attribute fallbacks" else bad "_stat provides guarded VC6/Win9x file-attribute fallbacks" fi # The active _stat.c frontier must use C89-compatible positional initializers # for statmodule because VC6 has no C99 designated-initializer support. if python3 - "$REPO_ROOT/cpython/Modules/_stat.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() expected = '''static struct PyModuleDef statmodule = { PyModuleDef_HEAD_INIT, "_stat", module_doc, 0, stat_methods, stat_slots, NULL, NULL, NULL, };''' if expected not in source: raise SystemExit("statmodule positional initializer mismatch") body = source[source.index("static struct PyModuleDef statmodule = {"):] body = body[:body.index("\n};")] if ".m_" in body: raise SystemExit("statmodule designated initializer remains") PY then pass "_stat statmodule uses C89-compatible positional initializers" else bad "_stat statmodule uses C89-compatible positional initializers" fi # The active _struct.c module definition must use C89-compatible positional # initializers in the declared PyModuleDef field order. if python3 - "$REPO_ROOT/cpython/Modules/_struct.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() expected = '''static struct PyModuleDef _structmodule = { PyModuleDef_HEAD_INIT, "_struct", module_doc, sizeof(_structmodulestate), module_functions, _structmodule_slots, _structmodule_traverse, _structmodule_clear, _structmodule_free, };''' if expected not in source: raise SystemExit("_structmodule positional initializer mismatch") body = source[source.index("static struct PyModuleDef _structmodule = {"):] body = body[:body.index("\n};")] if ".m_" in body: raise SystemExit("_structmodule designated initializer remains") PY then pass "_struct _structmodule uses C89-compatible positional initializers" else bad "_struct _structmodule uses C89-compatible positional initializers" fi # The active _struct.c frontier must hoist declarations before executable # statements for VC6/C89 in s_new(), s_dealloc(), and s_pack(). if python3 - "$REPO_ROOT/cpython/Modules/_struct.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() checks = { "s_new": ("s_new(PyTypeObject *type, PyObject *args, PyObject *kwds)", "\n}\n\n/*[clinic input]", [" allocfunc alloc_func;"], [" alloc_func = PyType_GetSlot(type, Py_tp_alloc);"]), "s_dealloc": ("s_dealloc(PyStructObject *s)", "\n}\n\nstatic PyObject *\ns_unpack_internal", [" PyTypeObject *tp;", " freefunc free_func;"], [" tp = Py_TYPE(s);", " free_func = PyType_GetSlot(Py_TYPE(s), Py_tp_free);"]), "s_pack": ("s_pack(PyObject *self, PyObject *const *args, Py_ssize_t nargs)", "\n}\n\nPyDoc_STRVAR(s_pack_into__doc__", [" _PyBytesWriter writer;"], [" _PyBytesWriter_Init(&writer);"]), } for name, (signature, end_marker, declarations, assignments) in checks.items(): start = source.index(signature) end = source.index(end_marker, start) + 2 function = source[start:end] for declaration in declarations: if declaration not in function or declaration[:-1] + " =" in function: raise SystemExit(f"{name}: declaration not hoisted") for assignment in assignments: if assignment not in function: raise SystemExit(f"{name}: assignment missing") PY then pass "_struct uses C89 declaration-order-safe locals at the active frontier" else bad "_struct uses C89 declaration-order-safe locals at the active frontier" fi # Keep the current arraymodule.c VC6 frontier explicit: the q/QQ handlers # must route 64-bit C types through CPython's VC6-compatible spelling. if python3 - "$REPO_ROOT/cpython/Modules/arraymodule.c" <<'PY' import re import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("q_getitem(arrayobject *ap, Py_ssize_t i)") end = source.index("\n}\n\nstatic PyObject *\nf_getitem", start) + 2 family = source[start:end] if re.search(r"(?ob_item") != 2: raise SystemExit(1) if family.count("(unsigned PY_LONG_LONG *)ap->ob_item") != 2: raise SystemExit(1) PY then pass "arraymodule q/QQ handlers use VC6-compatible 64-bit type spellings" else bad "arraymodule q/QQ handlers use VC6-compatible 64-bit type spellings" fi # The active arraymodule.c frontier must declare the compare-loop counter in # the function declaration group because VC6 has no C99 for-loop declarations. if python3 - "$REPO_ROOT/cpython/Modules/arraymodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("#define DEFINE_COMPAREITEMS") end = source.index("\n\nDEFINE_COMPAREITEMS(b", start) macro = source[start:end] if " Py_ssize_t i; \\\n" not in macro: raise SystemExit("compare macro loop counter declaration missing") if " for (i = 0; i < length; ++i) \\\n" not in macro: raise SystemExit("compare macro still uses C99 for initializer") if "for (Py_ssize_t i =" in macro: raise SystemExit("compare macro retains C99 for initializer") PY then pass "arraymodule compare macro uses C89 declaration-order-safe loop counter" else bad "arraymodule compare macro uses C89 declaration-order-safe loop counter" fi # The active arraymodule.c frontier must route the q/QQ compare macro type # arguments through CPython's VC6-compatible 64-bit spelling. if python3 - "$REPO_ROOT/cpython/Modules/arraymodule.c" <<'PY' import re import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("#define DEFINE_COMPAREITEMS") end = source.index("\n\nDEFINE_COMPAREITEMS(b", start) macro = source[start:end] if "DEFINE_COMPAREITEMS(q, PY_LONG_LONG)" not in source: raise SystemExit("signed q compare macro argument is not VC6-compatible") if "DEFINE_COMPAREITEMS(QQ, unsigned PY_LONG_LONG)" not in source: raise SystemExit("unsigned QQ compare macro argument is not VC6-compatible") if re.search(r"DEFINE_COMPAREITEMS\(q,\s*long\s+long\)", source): raise SystemExit("signed q compare macro retains long long") if re.search(r"DEFINE_COMPAREITEMS\(QQ,\s*unsigned\s+long\s+long\)", source): raise SystemExit("unsigned QQ compare macro retains long long") PY then pass "arraymodule compare macro uses VC6-compatible 64-bit type arguments" else bad "arraymodule compare macro uses VC6-compatible 64-bit type arguments" fi # Keep the active arraymodule.c frontier explicit: get_native_typecode() # must use CPython's VC6-compatible 64-bit type spelling, not long long. if python3 - "$REPO_ROOT/cpython/Modules/arraymodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("typecode_to_mformat_code(char typecode)") end = source.index("\n}\n\n/* Forward declaration. */", start) + 2 function = source[start:end] if "intsize = sizeof(PY_LONG_LONG);" not in function: raise SystemExit("get_native_typecode q/QQ width is not VC6-compatible") if function.count("intsize = sizeof(PY_LONG_LONG);") != 2: raise SystemExit("get_native_typecode q/QQ width count changed") if "intsize = sizeof(long long);" in function: raise SystemExit("get_native_typecode retains sizeof(long long)") PY then pass "arraymodule get_native_typecode uses VC6-compatible 64-bit widths" else bad "arraymodule get_native_typecode uses VC6-compatible 64-bit widths" fi # Keep the active arraymodule.c frontier explicit: the q/QQ descriptor widths # must use CPython's VC6-compatible 64-bit type spelling, not long long. if python3 - "$REPO_ROOT/cpython/Modules/arraymodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("static const struct arraydescr descriptors[]") end = source.index("\n};", start) + 3 descriptors = source[start:end] if "{'q', sizeof(PY_LONG_LONG)," not in descriptors: raise SystemExit("q descriptor does not use VC6-compatible 64-bit width") if "{'Q', sizeof(PY_LONG_LONG)," not in descriptors: raise SystemExit("Q descriptor does not use VC6-compatible 64-bit width") if "{'q', sizeof(long long)," in descriptors: raise SystemExit("q descriptor retains sizeof(long long)") if "{'Q', sizeof(long long)," in descriptors: raise SystemExit("Q descriptor retains sizeof(long long)") PY then pass "arraymodule q/QQ descriptors use VC6-compatible 64-bit widths" else bad "arraymodule q/QQ descriptors use VC6-compatible 64-bit widths" fi # VC6 C2026: each arraytype_doc literal must fit, while concatenation must # preserve every byte of the original documentation (including newlines). if python3 - "$REPO_ROOT/cpython/Modules/arraymodule.c" <<'PY' import ast import hashlib import re import sys from pathlib import Path source = Path(sys.argv[1]).read_text() match = re.search(r'PyDoc_STRVAR\(arraytype_doc,\s*(.*?)\);', source, re.S) assert match, "arraytype_doc must retain PyDoc_STRVAR behavior" body = match.group(1).replace("\\\n", "") literals = re.findall(r'"(?:[^"\\]|\\.)*"', body) assert literals and not re.sub(r'"(?:[^"\\]|\\.)*"', "", body).strip(), \ "arraytype_doc must consist of adjacent string literals" chunks = [ast.literal_eval(literal).encode("ascii") for literal in literals] assert all(len(chunk) <= 2048 for chunk in chunks), \ "arraytype_doc exceeds VC6's 2048-byte individual string literal limit" assert hashlib.sha256(b"".join(chunks)).hexdigest() == \ "36d98a6261ec374e1e5ed2da654bb1c47286a2658b86c28800af1543c40f6822", \ "arraytype_doc documentation bytes changed" assert "{Py_tp_doc, (void *)arraytype_doc}" in source, \ "array type must retain its documentation slot" PY then pass "arraymodule arraytype_doc uses VC6-sized literals with unchanged text" else bad "arraymodule arraytype_doc uses VC6-sized literals with unchanged text" fi # The active arraymodule.c VC6/C89 frontier must hoist arrayiter_next()'s # debug-only state declaration before executable statements. if python3 - "$REPO_ROOT/cpython/Modules/arraymodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("arrayiter_next(arrayiterobject *it)\n{") end = source.index("\n}\n\nstatic void\narrayiter_dealloc", start) + 2 function = source[start:end] opening = function[:function.index(" assert(it != NULL);")] if " array_state *state;\n" not in opening: raise SystemExit("arrayiter_next state declaration missing from opening group") if " array_state *state = find_array_state_by_type(Py_TYPE(it));" in function: raise SystemExit("arrayiter_next retains mixed declaration") if function.count(" state = find_array_state_by_type(Py_TYPE(it));") != 1: raise SystemExit("arrayiter_next state assignment missing") if "#ifndef NDEBUG\n state = find_array_state_by_type(Py_TYPE(it));\n assert(PyObject_TypeCheck(it, state->ArrayIterType));" not in function: raise SystemExit("arrayiter_next debug type-check sequence changed") PY then pass "arraymodule arrayiter_next uses C89 declaration-order-safe state local" else bad "arraymodule arrayiter_next uses C89 declaration-order-safe state local" fi # The active arraymodule.c VC6/C89 frontier must hoist array iterator reduce # locals before the executable state lookup and assertion. if python3 - "$REPO_ROOT/cpython/Modules/arraymodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("array_arrayiterator___reduce___impl(arrayiterobject *self, PyTypeObject *cls)") end = source.index("\n}\n\n/*[clinic input]\narray.arrayiterator.__setstate__", start) + 2 function = source[start:end] opening = function[:function.index(" assert(state != NULL);")] if " array_state *state;\n" not in opening: raise SystemExit("array iterator reduce state declaration missing from opening group") if " PyObject *func;\n" not in opening: raise SystemExit("array iterator reduce func declaration missing from opening group") if " array_state *state = get_array_state_by_class(cls);" in function: raise SystemExit("array iterator reduce retains mixed state declaration") if " PyObject *func = _PyEval_GetBuiltin(state->str_iter);" in function: raise SystemExit("array iterator reduce retains mixed func declaration") if " state = get_array_state_by_class(cls);\n assert(state != NULL);\n func = _PyEval_GetBuiltin(state->str_iter);\n" not in function: raise SystemExit("array iterator reduce lookup sequence changed") PY then pass "arraymodule array iterator reduce uses C89 declaration-order-safe locals" else bad "arraymodule array iterator reduce uses C89 declaration-order-safe locals" fi # The active arraymodule.c VC6/C89 frontier must hoist array_modexec()'s # locals before the ADD_INTERNED executable statements. if python3 - "$REPO_ROOT/cpython/Modules/arraymodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("array_modexec(PyObject *m)\n{") end = source.index("\n}\n\nstatic PyModuleDef_Slot arrayslots", start) + 2 function = source[start:end] opening = function[:function.index(" /* Add interned strings */")] for declaration in (" PyObject *abc_mod;\n", " PyObject *mutablesequence;\n", " PyObject *res;\n"): if declaration not in opening: raise SystemExit("array_modexec local missing from opening declaration group: " + declaration.strip()) for mixed in (" PyObject *abc_mod = PyImport_ImportModule", " PyObject *mutablesequence = PyObject_GetAttrString", " PyObject *res = PyObject_CallMethod"): if mixed in function: raise SystemExit("array_modexec retains mixed declaration: " + mixed) if " abc_mod = PyImport_ImportModule(\"collections.abc\");\n" not in function: raise SystemExit("array_modexec import assignment missing") if " mutablesequence = PyObject_GetAttrString(abc_mod, \"MutableSequence\");\n" not in function: raise SystemExit("array_modexec attribute assignment missing") if " res = PyObject_CallMethod(mutablesequence, \"register\", \"O\",\n" not in function: raise SystemExit("array_modexec register assignment missing") PY then pass "arraymodule array_modexec uses C89 declaration-order-safe locals" else bad "arraymodule array_modexec uses C89 declaration-order-safe locals" fi # The active arraymodule.c VC6 frontier must replace arrayiter_spec's C99 # designated initializer with declared-order positional PyType_Spec fields. if python3 - "$REPO_ROOT/cpython/Modules/arraymodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("static PyType_Spec arrayiter_spec = {") end = source.index("\n};", start) body = source[start:end] assert ".name" not in body and ".basicsize" not in body and ".flags" not in body and ".slots" not in body, \ "arrayiter_spec retains designated fields" expected = '\n "array.arrayiterator",\n sizeof(arrayiterobject),\n 0,\n (Py_TPFLAGS_DEFAULT |' assert expected in body, "arrayiter_spec positional field order mismatch" assert body.rstrip().endswith("arrayiter_slots,"), "arrayiter_spec slots field missing" PY then pass "arraymodule arrayiter_spec uses VC6-compatible positional PyType_Spec fields" else bad "arraymodule arrayiter_spec uses VC6-compatible positional PyType_Spec fields" fi # The active arraymodule.c VC6 frontier must replace array_spec's C99 # designated initializer with declared-order positional PyType_Spec fields. if python3 - "$REPO_ROOT/cpython/Modules/arraymodule.c" <<'PY' import re import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("static PyType_Spec array_spec = {") end = source.index("\n};", start) body = source[start:end] assert ".name" not in body and ".basicsize" not in body and ".flags" not in body and ".slots" not in body, \ "array_spec retains designated fields" expected = '\n "array.array",\n sizeof(arrayobject),\n 0,\n (Py_TPFLAGS_DEFAULT |' assert expected in body, "array_spec positional field order mismatch" assert body.rstrip().endswith("array_slots,"), "array_spec slots field missing" PY then pass "arraymodule array_spec uses VC6-compatible positional PyType_Spec fields" else bad "arraymodule array_spec uses VC6-compatible positional PyType_Spec fields" fi # The active arraymodule.c VC6 frontier must replace arraymodule's C99 # designated PyModuleDef initializer with declared-order positional fields. if python3 - "$REPO_ROOT/cpython/Modules/arraymodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("static struct PyModuleDef arraymodule = {") end = source.index("\n};", start) + 3 module = source[start:end] expected = '''static struct PyModuleDef arraymodule = { PyModuleDef_HEAD_INIT, "array", module_doc, sizeof(array_state), a_methods, arrayslots, array_traverse, array_clear, array_free, };''' if module != expected: raise SystemExit("arraymodule positional initializer mismatch") PY then pass "arraymodule uses C89-compatible positional PyModuleDef initializer" else bad "arraymodule uses C89-compatible positional PyModuleDef initializer" fi # The active atexitmodule.c VC6/C89 frontier must declare loop counters before # the executable callback cleanup/call statements. if python3 - "$REPO_ROOT/cpython/Modules/atexitmodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() checks = { "cleanup": ( "atexit_cleanup(struct atexit_state *state)", "\n}\n\n\nPyStatus\n_PyAtExit_Init", " int i;\n", " for (i = 0; i < state->ncallbacks; i++) {\n", ), "callfuncs": ( "atexit_callfuncs(struct atexit_state *state)", "\n}\n\n\nvoid\n_PyAtExit_Call", " int i;\n", " for (i = state->ncallbacks - 1; i >= 0; i--) {\n", ), } for name, (signature, end_marker, declaration, loop) in checks.items(): start = source.index(signature) end = source.index(end_marker, start) + 2 function = source[start:end] if declaration not in function: raise SystemExit(name + ": loop counter declaration missing") if "for (int i =" in function: raise SystemExit(name + ": C99 loop declaration remains") if loop not in function: raise SystemExit(name + ": loop assignment sequence changed") PY then pass "atexit cleanup/callfuncs use C89 declaration-order-safe loop counters" else bad "atexit cleanup/callfuncs use C89 declaration-order-safe loop counters" fi # The active atexit_callfuncs() frontier must hoist callback locals before the # loop's executable statements for MSVC6's C89 dialect. if python3 - "$REPO_ROOT/cpython/Modules/atexitmodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("atexit_callfuncs(struct atexit_state *state)") end = source.index("\n}\n\n\nvoid\n_PyAtExit_Call", start) + 2 function = source[start:end] if " PyObject *the_func;\n" not in function: raise SystemExit("atexit_callfuncs: the_func declaration missing") if " PyObject *res;\n" not in function: raise SystemExit("atexit_callfuncs: res declaration missing") if " PyObject* the_func = Py_NewRef(cb->func);" in function: raise SystemExit("atexit_callfuncs: the_func remains a mixed declaration") if " PyObject *res = PyObject_Call(cb->func, cb->args, cb->kwargs);" in function: raise SystemExit("atexit_callfuncs: res remains a mixed declaration") if function.count(" the_func = Py_NewRef(cb->func);\n") != 1: raise SystemExit("atexit_callfuncs: the_func assignment sequence changed") if function.count(" res = PyObject_Call(cb->func, cb->args, cb->kwargs);\n") != 1: raise SystemExit("atexit_callfuncs: res assignment sequence changed") PY then pass "atexit_callfuncs uses C89 declaration-order-safe callback locals" else bad "atexit_callfuncs uses C89 declaration-order-safe callback locals" fi # The current atexitmodule.c VC6/C89 frontier must hoist every local in # atexit_register() before its initial executable argument check. if python3 - "$REPO_ROOT/cpython/Modules/atexitmodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("atexit_register(PyObject *module, PyObject *args, PyObject *kwargs)") end = source.index("\n}\n\nPyDoc_STRVAR(atexit_run_exitfuncs__doc__", start) + 2 function = source[start:end] opening = function[:function.index(" if (PyTuple_GET_SIZE(args)")] for declaration in ( " PyObject *func;\n", " struct atexit_state *state;\n", " atexit_callback **r;\n", " size_t size;\n", " atexit_callback *callback;\n", ): if declaration not in opening: raise SystemExit("atexit_register declaration missing from opening group: " + declaration.strip()) for mixed in ( " PyObject *func = PyTuple_GET_ITEM(args, 0);", " struct atexit_state *state = get_atexit_state();", " atexit_callback **r;\n", " size_t size = sizeof(atexit_callback*)", " atexit_callback *callback = PyMem_Malloc", ): if mixed in function: raise SystemExit("atexit_register retains mixed declaration: " + mixed) for assignment in ( " func = PyTuple_GET_ITEM(args, 0);\n", " state = get_atexit_state();\n", " size = sizeof(atexit_callback*) * (size_t)state->callback_len;\n", " callback = PyMem_Malloc(sizeof(atexit_callback));\n", ): if function.count(assignment) != 1: raise SystemExit("atexit_register assignment missing or duplicated: " + assignment.strip()) if " r = (atexit_callback**)PyMem_Realloc(state->callbacks, size);\n" not in function: raise SystemExit("atexit_register realloc assignment missing") PY then pass "atexit_register uses C89 declaration-order-safe locals" else bad "atexit_register uses C89 declaration-order-safe locals" fi # The active atexitmodule.c VC6/C89 frontier must hoist atexit_unregister() # locals before the initial executable loop statement. if python3 - "$REPO_ROOT/cpython/Modules/atexitmodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("atexit_unregister(PyObject *module, PyObject *func)") end = source.index("\n}\n\n\nstatic PyMethodDef atexit_methods", start) + 2 function = source[start:end] opening = function[:function.index(" for (")] for declaration in ( " int i;\n", " atexit_callback *cb;\n", " int eq;\n", ): if declaration not in opening: raise SystemExit("atexit_unregister declaration missing from opening group: " + declaration.strip()) for mixed in ( "for (int i = 0;", " atexit_callback *cb = state->callbacks[i];", " int eq = PyObject_RichCompareBool(cb->func, func, Py_EQ);", ): if mixed in function: raise SystemExit("atexit_unregister retains mixed declaration: " + mixed) if " struct atexit_state *state = get_atexit_state();\n int i;\n" not in function: raise SystemExit("atexit_unregister state/local declaration order changed") if function.count(" for (i = 0; i < state->ncallbacks; i++)") != 1: raise SystemExit("atexit_unregister loop assignment changed") if function.count(" cb = state->callbacks[i];\n") != 1: raise SystemExit("atexit_unregister callback assignment missing") if function.count(" eq = PyObject_RichCompareBool(cb->func, func, Py_EQ);\n") != 1: raise SystemExit("atexit_unregister comparison assignment missing") PY then pass "atexit_unregister uses C89 declaration-order-safe locals" else bad "atexit_unregister uses C89 declaration-order-safe locals" fi # The next atexitmodule.c VC6 frontier must replace atexitmodule's C99 # designated PyModuleDef initializer with declared-order positional fields. if python3 - "$REPO_ROOT/cpython/Modules/atexitmodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("static struct PyModuleDef atexitmodule = {") end = source.index("\n};", start) + 3 module = source[start:end] expected = '''static struct PyModuleDef atexitmodule = { PyModuleDef_HEAD_INIT, "atexit", atexit__doc__, 0, atexit_methods, };''' if module != expected: raise SystemExit("atexitmodule positional initializer mismatch") PY then pass "atexitmodule uses C89-compatible positional PyModuleDef initializer" else bad "atexitmodule uses C89-compatible positional PyModuleDef initializer" fi # The active audioop.c VC6 frontier must avoid SIZE_MAX, which VC6's standard # headers do not provide, while preserving ratecv's allocation-overflow guard. if python3 - "$REPO_ROOT/cpython/Modules/audioop.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("audioop_ratecv_impl(PyObject *module") end = source.index("\n}\n\n/*[clinic input]\naudioop.lin2ulaw", start) + 2 function = source[start:end] if function.count("if ((size_t)nchannels > ((size_t)-1)/sizeof(int)) {") != 1: raise SystemExit("audioop ratecv SIZE_MAX guard replacement missing") if "SIZE_MAX" in function: raise SystemExit("audioop ratecv retains unavailable SIZE_MAX") sequence = ( "if ((size_t)nchannels > ((size_t)-1)/sizeof(int)) {\n" " PyErr_SetString(PyExc_MemoryError,\n" " \"not enough memory for output buffer\");\n" " return NULL;\n" " }\n" " prev_i = (int *) PyMem_Malloc(nchannels * sizeof(int));\n" " cur_i = (int *) PyMem_Malloc(nchannels * sizeof(int));" ) if sequence not in function: raise SystemExit("audioop ratecv allocation guard sequence changed") PY then pass "audioop ratecv allocation guard avoids unavailable SIZE_MAX" else bad "audioop ratecv allocation guard avoids unavailable SIZE_MAX" fi # The next binascii.c VC6/C89 frontier must hoist a2b_base64 locals before # its initial executable assertion and loop. if python3 - "$REPO_ROOT/cpython/Modules/binascii.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("binascii_a2b_base64_impl(PyObject *module") end = source.index("\n}\n\n\n/*[clinic input]\nbinascii.b2a_base64", start) + 2 function = source[start:end] opening = function[:function.index(" /* Allocate the buffer */")] for declaration in ( " const unsigned char *ascii_data;\n", " size_t ascii_len;\n", " binascii_state *state;\n", " char padding_started;\n", " Py_ssize_t bin_len;\n", " _PyBytesWriter writer;\n", " unsigned char *bin_data;\n", " unsigned char *bin_data_start;\n", " int quad_pos;\n", " unsigned char leftchar;\n", " int pads;\n", " size_t i;\n", " unsigned char this_ch;\n", ): if declaration not in opening: raise SystemExit("a2b_base64 declaration missing from opening group: " + declaration.strip()) for mixed in ( "const unsigned char *ascii_data =", "size_t ascii_len =", "binascii_state *state =", "char padding_started =", "Py_ssize_t bin_len =", "unsigned char *bin_data =", "unsigned char *bin_data_start =", "int quad_pos =", "unsigned char leftchar =", "int pads =", "for (size_t i =", "unsigned char this_ch =", ): if mixed in function: raise SystemExit("a2b_base64 retains mixed declaration: " + mixed) for assignment in ( " ascii_data = data->buf;\n", " ascii_len = data->len;\n", " state = NULL;\n", " padding_started = 0;\n", " bin_len = ((ascii_len+3)/4)*3; /* Upper bound, corrected later */\n", " bin_data = _PyBytesWriter_Alloc(&writer, bin_len);\n", " bin_data_start = bin_data;\n", " quad_pos = 0;\n", " leftchar = 0;\n", " pads = 0;\n", " for (i = 0; i < ascii_len; i++) {\n", " this_ch = ascii_data[i];\n", ): if assignment not in function: raise SystemExit("a2b_base64 assignment sequence changed: " + assignment.strip()) PY then pass "binascii a2b_base64 uses C89 declaration-order-safe locals" else bad "binascii a2b_base64 uses C89 declaration-order-safe locals" fi # The active _datetimemodule.c VC6/C89 frontier must hoist iso_to_ymd() # locals before its initial executable validation statements. if python3 - "$REPO_ROOT/cpython/Modules/_datetimemodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("iso_to_ymd(const int iso_year") end = source.index("\n}\n\n\n/* ---------------------------------------------------------------------------", start) + 2 function = source[start:end] opening = function[:function.index(" if (iso_week <= 0")] for declaration in (" int day_1;\n", " int day_offset;\n"): if declaration not in opening: raise SystemExit("iso_to_ymd declaration missing from opening group: " + declaration.strip()) if " int day_1 = iso_week1_monday(iso_year);" in function: raise SystemExit("iso_to_ymd retains mixed day_1 declaration") if " int day_offset = (iso_week - 1)*7 + iso_day - 1;" in function: raise SystemExit("iso_to_ymd retains mixed day_offset declaration") if " day_1 = iso_week1_monday(iso_year);\n" not in function: raise SystemExit("iso_to_ymd day_1 assignment missing") if " day_offset = (iso_week - 1)*7 + iso_day - 1;\n" not in function: raise SystemExit("iso_to_ymd day_offset assignment missing") PY then pass "_datetimemodule iso_to_ymd uses C89 declaration-order-safe locals" else bad "_datetimemodule iso_to_ymd uses C89 declaration-order-safe locals" fi # The active _datetimemodule.c VC6 frontier must use CPython's compiler- # conditional 64-bit spelling and literal suffix macros; VC6 rejects the # portable long long keyword and C99 LL suffixes. if python3 - "$REPO_ROOT/cpython/Modules/_datetimemodule.c" <<'PY' import re import sys from pathlib import Path source = Path(sys.argv[1]).read_text() code = re.sub(r"/\*.*?\*/", "", source, flags=re.S) code = re.sub(r"//.*", "", code) if re.search(r"\b(?:unsigned\s+)?long\s+long\b", code): raise SystemExit("_datetimemodule long long keyword remains") if "719163LL" in code: raise SystemExit("_datetimemodule C99 LL suffix remains") if "PY_LONG_LONG max_fold_seconds" not in code: raise SystemExit("_datetimemodule max_fold_seconds spelling missing") if "PY_LONG_LONG epoch" not in code: raise SystemExit("_datetimemodule epoch spelling missing") if "#define EPOCH_SECONDS (PY_LL(719163) * 24 * 60 * 60)" not in source: raise SystemExit("_datetimemodule epoch literal macro missing") PY then pass "_datetimemodule uses compiler-compatible 64-bit spellings" else bad "_datetimemodule uses compiler-compatible 64-bit spellings" fi # The current cmath VC6 frontier must replace its C99 designated PyModuleDef # initializer with declared-order positional fields. if python3 - "$REPO_ROOT/cpython/Modules/cmathmodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("static struct PyModuleDef cmathmodule = {") end = source.index("\n};", start) + 3 module = source[start:end] expected = '''static struct PyModuleDef cmathmodule = { PyModuleDef_HEAD_INIT, "cmath", module_doc, 0, cmath_methods, cmath_slots };''' if module != expected: raise SystemExit("cmathmodule positional initializer mismatch") PY then pass "cmathmodule uses C89-compatible positional PyModuleDef initializer" else bad "cmathmodule uses C89-compatible positional PyModuleDef initializer" fi # The current _datetimemodule.c VC6 frontier must declare parse_digits()'s # loop counter before executable statements and assign it in the for init. if python3 - "$REPO_ROOT/cpython/Modules/_datetimemodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("parse_digits(const char *ptr, int *var, size_t num_digits)") end = source.index("\n}\n\nstatic int\nparse_isoformat_date", start) + 2 function = source[start:end] if " size_t i;" not in function: raise SystemExit("parse_digits loop counter declaration missing") if "for (size_t i = 0;" in function: raise SystemExit("parse_digits retains C99 loop declaration") if function.count("for (i = 0;") != 1: raise SystemExit("parse_digits loop assignment missing") PY then pass "_datetimemodule parse_digits uses C89-compatible loop counter" else bad "_datetimemodule parse_digits uses C89-compatible loop counter" fi # The current _datetimemodule.c VC6 frontier must hoist parse_isoformat_date()'s # separator flag before executable statements and assign it at the original point. if python3 - "$REPO_ROOT/cpython/Modules/_datetimemodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("parse_isoformat_date(const char *dtstr") end = source.index("\n}\n\nstatic int\nparse_isoformat_time", start) + 2 function = source[start:end] if " unsigned char uses_separator;" not in function: raise SystemExit("parse_isoformat_date separator declaration missing") if " const unsigned char uses_separator = (*p == '-');" in function: raise SystemExit("parse_isoformat_date retains post-statement separator declaration") if function.count(" uses_separator = (*p == '-');") != 1: raise SystemExit("parse_isoformat_date separator assignment missing") PY then pass "_datetimemodule parse_isoformat_date uses C89-compatible separator declaration" else bad "_datetimemodule parse_isoformat_date uses C89-compatible separator declaration" fi # The active _datetimemodule.c VC6/C89 frontier must hoist the ISO-week locals # before the executable parse_digits() call and preserve their assignments. if python3 - "$REPO_ROOT/cpython/Modules/_datetimemodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("parse_isoformat_date(const char *dtstr") end = source.index("\n}\n\nstatic int\nparse_isoformat_time", start) + 2 function = source[start:end] opening = function[:function.index(" if(*p == 'W')")] for declaration in (" int iso_week;\n", " int iso_day;\n"): if declaration not in opening: raise SystemExit("parse_isoformat_date ISO-week declaration missing from opening group: " + declaration.strip()) for mixed in (" int iso_week = 0;", " int iso_day = 0;"): if mixed in function: raise SystemExit("parse_isoformat_date retains post-statement ISO-week declaration: " + mixed) if function.count(" iso_week = 0;\n") != 1: raise SystemExit("parse_isoformat_date ISO-week assignment missing") if function.count(" iso_day = 0;\n") != 1: raise SystemExit("parse_isoformat_date ISO-day assignment missing") PY then pass "_datetimemodule parse_isoformat_date uses C89-compatible ISO-week locals" else bad "_datetimemodule parse_isoformat_date uses C89-compatible ISO-week locals" fi # The active _datetimemodule.c VC6/C89 frontier must hoist parse_isoformat_date()'s # rv local before the executable ISO-week conversion call. if python3 - "$REPO_ROOT/cpython/Modules/_datetimemodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("parse_isoformat_date(const char *dtstr") end = source.index("\n}\n\nstatic int\nparse_isoformat_time", start) + 2 function = source[start:end] opening = function[:function.index(" if(*p == 'W')")] if " int rv;\n" not in opening: raise SystemExit("parse_isoformat_date rv declaration missing from opening group") if " int rv = iso_to_ymd(" in function: raise SystemExit("parse_isoformat_date retains post-statement rv declaration") if function.count(" rv = iso_to_ymd(*year, iso_week, iso_day, year, month, day);\n") != 1: raise SystemExit("parse_isoformat_date rv assignment missing") PY then pass "_datetimemodule parse_isoformat_date uses C89-compatible rv local" else bad "_datetimemodule parse_isoformat_date uses C89-compatible rv local" fi # The active _datetimemodule.c VC6/C89 frontier must hoist # date_fromisocalendar()'s locals before executable statements and preserve the # original conversion call position. if python3 - "$REPO_ROOT/cpython/Modules/_datetimemodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("date_fromisocalendar(PyObject *cls") end = source.index("\n}\n\n\n/*\n * Date arithmetic.", start) + 2 function = source[start:end] opening = function[:function.index(" if (PyArg_ParseTupleAndKeywords")] for declaration in (" int month;\n", " int rv;\n"): if declaration not in opening: raise SystemExit("date_fromisocalendar declaration missing from opening group: " + declaration.strip()) for mixed in (" int month;", " int rv = iso_to_ymd("): if mixed in function[function.index(" // Year is bounded"):]: raise SystemExit("date_fromisocalendar retains post-statement declaration: " + mixed) if function.count(" rv = iso_to_ymd(year, week, day, &year, &month, &day);\n") != 1: raise SystemExit("date_fromisocalendar conversion assignment missing") PY then pass "_datetimemodule date_fromisocalendar uses C89-compatible locals" else bad "_datetimemodule date_fromisocalendar uses C89-compatible locals" fi # The current _datetimemodule.c VC6/C89 frontier must hoist date_isocalendar()'s # result local before executable statements and preserve the constructor call. if python3 - "$REPO_ROOT/cpython/Modules/_datetimemodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("date_isocalendar(PyDateTime_Date *self") end = source.index("\n}\n\n/* Miscellaneous methods. */", start) + 2 function = source[start:end] opening = function[:function.index(" v = iso_calendar_date_new_impl(")] if " PyObject* v;\n" not in opening: raise SystemExit("date_isocalendar v declaration missing from opening group") if " PyObject* v = iso_calendar_date_new_impl(" in function: raise SystemExit("date_isocalendar retains post-statement v declaration") if function.count(" v = iso_calendar_date_new_impl(&PyDateTime_IsoCalendarDateType,\n") != 1: raise SystemExit("date_isocalendar constructor assignment missing") PY then pass "_datetimemodule date_isocalendar uses C89-compatible result local" else bad "_datetimemodule date_isocalendar uses C89-compatible result local" fi # The active _datetimemodule.c VC6/C89 frontier must hoist # iso_calendar_date_repr()'s tuple locals before the executable NULL check. if python3 - "$REPO_ROOT/cpython/Modules/_datetimemodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("iso_calendar_date_repr(PyDateTime_IsoCalendarDate *self)") end = source.index("\n}\n\nstatic PyObject *\niso_calendar_date_reduce", start) + 2 function = source[start:end] opening = function[:function.index(" if (year == NULL)")] for declaration in (" PyObject* week;\n", " PyObject* weekday;\n"): if declaration not in opening: raise SystemExit("iso_calendar_date_repr declaration missing from opening group: " + declaration.strip()) for mixed in (" PyObject* week = PyTuple_GetItem", " PyObject* weekday = PyTuple_GetItem"): if mixed in function: raise SystemExit("iso_calendar_date_repr retains post-statement declaration: " + mixed) if function.count(" week = PyTuple_GetItem((PyObject *)self, 1);\n") != 1: raise SystemExit("iso_calendar_date_repr week assignment missing") if function.count(" weekday = PyTuple_GetItem((PyObject *)self, 2);\n") != 1: raise SystemExit("iso_calendar_date_repr weekday assignment missing") PY then pass "_datetimemodule iso_calendar_date_repr uses C89-compatible tuple locals" else bad "_datetimemodule iso_calendar_date_repr uses C89-compatible tuple locals" fi # The active _datetimemodule.c VC6/C89 frontier must hoist # tzinfo_from_isoformat_results()'s delta local before executable statements. if python3 - "$REPO_ROOT/cpython/Modules/_datetimemodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("tzinfo_from_isoformat_results(int rv, int tzoffset, int tz_useconds)") end = source.index("\n}\n\n/* ---------------------------------------------------------------------------", start) + 2 function = source[start:end] opening = function[:function.index(" if (rv == 1)")] if " PyObject *delta;\n" not in opening: raise SystemExit("tzinfo_from_isoformat_results delta declaration missing from opening group") if " PyObject *delta = new_delta(" in function: raise SystemExit("tzinfo_from_isoformat_results retains post-statement delta declaration") if function.count(" delta = new_delta(0, tzoffset, tz_useconds, 1);\n") != 1: raise SystemExit("tzinfo_from_isoformat_results delta assignment missing") PY then pass "_datetimemodule tzinfo_from_isoformat_results uses C89-compatible delta local" else bad "_datetimemodule tzinfo_from_isoformat_results uses C89-compatible delta local" fi # The active _datetimemodule.c VC6/C89 frontier must hoist delta_repr()'s # separator and repr locals before executable statements. if python3 - "$REPO_ROOT/cpython/Modules/_datetimemodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("delta_repr(PyDateTime_Delta *self)") end = source.index("\n}\n\nstatic PyObject *\ndelta_str", start) + 2 function = source[start:end] opening = function[:function.index(" if (GET_TD_DAYS(self)")] for declaration in (" const char *sep;\n", " PyObject *repr;\n"): if declaration not in opening: raise SystemExit("delta_repr declaration missing from opening group: " + declaration.strip()) for mixed in (" const char *sep = \"\";", " PyObject *repr = PyUnicode_FromFormat"): if mixed in function: raise SystemExit("delta_repr retains mixed declaration: " + mixed) if function.count(" sep = \"\";\n") != 1: raise SystemExit("delta_repr separator assignment missing") if function.count(" repr = PyUnicode_FromFormat(\"%s(%S)\", Py_TYPE(self)->tp_name,\n") != 1: raise SystemExit("delta_repr repr assignment missing") PY then pass "_datetimemodule delta_repr uses C89 declaration-order-safe locals" else bad "_datetimemodule delta_repr uses C89 declaration-order-safe locals" fi # The active _datetimemodule.c VC6/C89 frontier must hoist parse_isoformat_time() # locals before executable statements and preserve the original assignments. if python3 - "$REPO_ROOT/cpython/Modules/_datetimemodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("parse_isoformat_time(const char *dtstr") end = source.index("\n}\n\n/* ---------------------------------------------------------------------------", start) + 2 function = source[start:end] opening = function[:function.index(" do {")] required = [ " int rv;\n", " int tzsign;\n", " int tzhour;\n", " int tzminute;\n", " int tzsecond;\n", ] for declaration in required: if declaration not in opening: raise SystemExit("parse_isoformat_time declaration missing from opening group: " + declaration.strip()) for forbidden in ( " int rv = parse_hh_mm_ss_ff(", " int tzsign =", " int tzhour = 0, tzminute = 0, tzsecond = 0;", ): if forbidden in function: raise SystemExit("parse_isoformat_time retains post-statement declaration: " + forbidden) if function.count(" rv = parse_hh_mm_ss_ff(dtstr, tzinfo_pos, hour, minute, second,\n") != 1: raise SystemExit("parse_isoformat_time initial rv assignment missing") if function.count(" tzsign = (*tzinfo_pos == '-') ? -1 : 1;\n") != 1: raise SystemExit("parse_isoformat_time timezone sign assignment missing") if function.count(" tzhour = 0;\n") != 1 or function.count(" tzminute = 0;\n") != 1 or function.count(" tzsecond = 0;\n") != 1: raise SystemExit("parse_isoformat_time timezone local assignments missing") PY then pass "_datetimemodule parse_isoformat_time uses C89-compatible locals" else bad "_datetimemodule parse_isoformat_time uses C89-compatible locals" fi # The active _datetimemodule.c VC6/C89 frontier must hoist all # parse_hh_mm_ss_ff() locals before executable statements and avoid C99 # aggregate/loop/declaration forms. if python3 - "$REPO_ROOT/cpython/Modules/_datetimemodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("parse_hh_mm_ss_ff(const char *tstr") end = source.index("\n}\n\nstatic int\nparse_isoformat_time", start) + 2 function = source[start:end] opening = function[:function.index(" *hour = *minute")] required = [ " int *vals[3];\n", " unsigned char has_separator = 1;\n", " size_t i;\n", " size_t len_remains;\n", " size_t to_parse;\n", " static int correction[] = {\n", ] for declaration in required: if declaration not in opening: raise SystemExit("parse_hh_mm_ss_ff declaration missing from opening group: " + declaration.strip()) for forbidden in ("int *vals[3] = {hour, minute, second};", "for (size_t i = 0;", "char c = *(p++);", "size_t len_remains =", "size_t to_parse ="): if forbidden in function: raise SystemExit("parse_hh_mm_ss_ff retains C99/post-statement form: " + forbidden) if function.count(" vals[0] = hour;\n") != 1 or function.count(" vals[1] = minute;\n") != 1 or function.count(" vals[2] = second;\n") != 1: raise SystemExit("parse_hh_mm_ss_ff vals assignments missing") if function.count("for (i = 0; i < 3; ++i) {") != 1: raise SystemExit("parse_hh_mm_ss_ff C89 loop form missing") if function.count(" char c;\n") != 1 or function.count(" c = *(p++);\n") != 1: raise SystemExit("parse_hh_mm_ss_ff C89 character local form missing") if function.count(" len_remains = p_end - p;\n") != 1 or function.count(" to_parse = len_remains;\n") != 1: raise SystemExit("parse_hh_mm_ss_ff fractional assignments missing") PY then pass "_datetimemodule parse_hh_mm_ss_ff uses C89-compatible locals" else bad "_datetimemodule parse_hh_mm_ss_ff uses C89-compatible locals" fi # The current _datetimemodule.c VC6/C89 frontier must hoist # date_fromisoformat() locals before its initial executable type check. if python3 - "$REPO_ROOT/cpython/Modules/_datetimemodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("date_fromisoformat(PyObject *cls, PyObject *dtstr)") end = source.index("\n}\n\n\nstatic PyObject *\ndate_fromisocalendar", start) + 2 function = source[start:end] opening = function[:function.index(" if (!PyUnicode_Check(dtstr))")] for declaration in ( " Py_ssize_t len;\n", " const char *dt_ptr;\n", " int year, month, day;\n", " int rv;\n", ): if declaration not in opening: raise SystemExit("date_fromisoformat declaration missing from opening group: " + declaration.strip()) for mixed in ( " Py_ssize_t len;\n\n const char *dt_ptr =", " const char *dt_ptr = PyUnicode_AsUTF8AndSize", " int year = 0, month = 0, day = 0;", ): if mixed in function: raise SystemExit("date_fromisoformat retains post-statement declaration: " + mixed) if function.count(" dt_ptr = PyUnicode_AsUTF8AndSize(dtstr, &len);\n") != 1: raise SystemExit("date_fromisoformat dt_ptr assignment missing") if function.count(" year = 0;\n month = 0;\n day = 0;\n") != 1: raise SystemExit("date_fromisoformat date local assignments missing") PY then pass "_datetimemodule date_fromisoformat uses C89-compatible locals" else bad "_datetimemodule date_fromisoformat uses C89-compatible locals" fi # The current _datetimemodule.c VC6/C89 frontier must hoist # time_fromisoformat() locals before its initial executable assertion. if python3 - "$REPO_ROOT/cpython/Modules/_datetimemodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("time_fromisoformat(PyObject *cls, PyObject *tstr) {") end = source.index("\n}\n\n\n/* Pickle support", start) + 2 function = source[start:end] opening = function[:function.index(" assert(tstr != NULL);")] for declaration in ( " Py_ssize_t len;\n", " const char *p;\n", " int hour, minute, second, microsecond;\n", " int tzoffset, tzimicrosecond;\n", " int rv;\n", " PyObject *tzinfo;\n", " PyObject *t;\n", ): if declaration not in opening: raise SystemExit("time_fromisoformat declaration missing from opening group: " + declaration.strip()) for mixed in ( " const char *p = PyUnicode_AsUTF8AndSize", " int hour = 0, minute = 0, second = 0, microsecond = 0;", " int tzoffset = 0, tzimicrosecond = 0;", " int rv = parse_isoformat_time(", " PyObject *tzinfo = tzinfo_from_isoformat_results(", " PyObject *t;\n", ): if mixed in function[function.index(" assert(tstr != NULL);"):]: raise SystemExit("time_fromisoformat retains mixed declaration: " + mixed) for assignment in ( " p = PyUnicode_AsUTF8AndSize(tstr, &len);\n", " hour = minute = second = microsecond = 0;\n", " tzoffset = tzimicrosecond = 0;\n", " rv = parse_isoformat_time(p, len,\n", " PyObject *tzinfo =", ): if assignment == " PyObject *tzinfo =": continue if assignment not in function: raise SystemExit("time_fromisoformat assignment missing: " + assignment.strip()) if " tzinfo = tzinfo_from_isoformat_results(rv, tzoffset,\n" not in function: raise SystemExit("time_fromisoformat tzinfo assignment missing") PY then pass "_datetimemodule time_fromisoformat uses C89-compatible locals" else bad "_datetimemodule time_fromisoformat uses C89-compatible locals" fi MSVC600_ROOT="${MSVC600_ROOT:-$REPO_ROOT/MSVC600}" if command -v wine >/dev/null 2>&1 && [ -e "$MSVC600_ROOT/VC98/Bin/CL.EXE" ]; then live_out="$($PROBE --limit 1 2>&1)" live_status=$? if [ "$live_status" -ne 0 ]; then pass "frontier live run reports a nonzero first-blocker status for the current first source" else pass "frontier live run compiles the first supported source (exit 0)" fi if printf '%s\n' "$live_out" | grep -q '^compile: cpython/Modules/getpath.c'; then pass "frontier live run starts from pythoncore.vcxproj's first supported source" else bad "frontier live run starts from pythoncore.vcxproj's first supported source" echo "$live_out" fi if [ "$live_status" -ne 0 ] && printf '%s\n' "$live_out" | grep -q 'first compile blocker: cpython/Modules/getpath.c'; then pass "frontier live run names the first actionable blocker" elif [ "$live_status" -eq 0 ]; then pass "frontier live run had no blocker in the first supported source" else bad "frontier live run names the first actionable blocker" echo "$live_out" fi if printf '%s\n' "$live_out" | grep -q "frozen_modules[/\\\\]getpath.h.*No such file or directory"; then bad "frontier live run should provision Python/frozen_modules/getpath.h before compiling getpath.c" echo "$live_out" elif [ -s "$REPO_ROOT/build/generated/cpython/Python/frozen_modules/getpath.h" ]; then pass "frontier live run provisions the generated getpath.h include under build/" else bad "frontier live run provisions the generated getpath.h include under build/" echo "$live_out" fi else echo "skip - wine/MSVC600 not available; skipping live frontier integration check" fi # The current _datetimemodule.c VC6 frontier must replace the ISO calendar # date type's C99 designated PyTypeObject initializer with declared-order # positional fields, matching the already-portable datetime type definitions. if python3 - "$REPO_ROOT/cpython/Modules/_datetimemodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("static PyTypeObject PyDateTime_IsoCalendarDateType = {") end = source.index("\n};", start) + 3 actual = source[start:end] expected = '''static PyTypeObject PyDateTime_IsoCalendarDateType = { PyVarObject_HEAD_INIT(NULL, 0) "datetime.IsoCalendarDate", sizeof(PyDateTime_IsoCalendarDate), 0, 0, 0, 0, 0, 0, (reprfunc) iso_calendar_date_repr, 0, 0, 0, 0, 0, 0, PyObject_GenericGetAttr, 0, 0, Py_TPFLAGS_DEFAULT, iso_calendar_date__doc__, 0, 0, 0, 0, 0, 0, iso_calendar_date_methods, 0, iso_calendar_date_getset, 0, 0, 0, 0, 0, 0, 0, iso_calendar_date_new, 0, };''' if actual != expected: raise SystemExit("iso calendar date type positional initializer mismatch") PY then pass "_datetimemodule ISO calendar date type uses C89-compatible positional initializer" else bad "_datetimemodule ISO calendar date type uses C89-compatible positional initializer" fi # The current _datetimemodule.c VC6 frontier must hoist _sanitize_isoformat_str() # locals before executable statements while preserving initializer order. if python3 - "$REPO_ROOT/cpython/Modules/_datetimemodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("_sanitize_isoformat_str(PyObject *dtstr)") end = source.index("\n}\n\n\nstatic Py_ssize_t\n_find_isoformat_datetime_separator", start) + 2 function = source[start:end] opening = function[:function.index(" Py_ssize_t len = PyUnicode_GetLength(dtstr);")] for declaration in ( " static const size_t potential_separators[3] = {7, 8, 10};\n", " const void *unicode_data;\n", " unsigned int kind;\n", " size_t surrogate_separator;\n", " size_t idx;\n", " PyObject *str_out;\n", ): if declaration not in opening: raise SystemExit("_sanitize_isoformat_str declaration missing from opening group: " + declaration.strip()) for forbidden in ( " const void* const unicode_data =", " const unsigned int kind =", " static const size_t potential_separators[3] = {7, 8, 10};\n size_t surrogate_separator", " for(size_t idx = 0;", " PyObject *str_out = _PyUnicode_Copy(dtstr);", ): if forbidden in function: raise SystemExit("_sanitize_isoformat_str retains post-statement declaration: " + forbidden) for assignment in ( " unicode_data = PyUnicode_DATA(dtstr);\n", " kind = PyUnicode_KIND(dtstr);\n", " surrogate_separator = 0;\n", " for (idx = 0;\n", " str_out = _PyUnicode_Copy(dtstr);\n", ): if assignment not in function: raise SystemExit("_sanitize_isoformat_str assignment missing: " + assignment.strip()) PY then pass "_datetimemodule _sanitize_isoformat_str uses C89-compatible locals" else bad "_datetimemodule _sanitize_isoformat_str uses C89-compatible locals" fi # The active _datetimemodule.c VC6 frontier must hoist datetime_fromisoformat() # locals before executable statements while preserving initializer order. if python3 - "$REPO_ROOT/cpython/Modules/_datetimemodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("datetime_fromisoformat(PyObject *cls, PyObject *dtstr)") end = source.index("\n}\n\n/*\n * Destructor.", start) + 2 function = source[start:end] opening = function[:function.index(" assert(dtstr != NULL);")] for declaration in ( " PyObject *dtstr_clean;\n", " Py_ssize_t len;\n", " const char *dt_ptr;\n", " Py_ssize_t separator_location;\n", " const char *p;\n", " int year, month, day;\n", " int hour, minute, second, microsecond;\n", " int tzoffset, tzusec;\n", " int rv;\n", " PyObject *tzinfo;\n", " PyObject *dt;\n", ): if declaration not in opening: raise SystemExit("datetime_fromisoformat declaration missing from opening group: " + declaration.strip()) for forbidden in ( " PyObject *dtstr_clean =", " const char *dt_ptr =", " const Py_ssize_t separator_location =", " const char *p =", " int year = 0, month = 0, day = 0;", " int hour = 0, minute = 0, second = 0, microsecond = 0;", " int tzoffset = 0, tzusec = 0;", " int rv = parse_isoformat_date(", " PyObject *tzinfo =", " PyObject *dt =", ): if forbidden in function: raise SystemExit("datetime_fromisoformat retains mixed declaration: " + forbidden) for assignment in ( " dtstr_clean = _sanitize_isoformat_str(dtstr);\n", " dt_ptr = PyUnicode_AsUTF8AndSize(dtstr_clean, &len);\n", " separator_location = _find_isoformat_datetime_separator(\n", " p = dt_ptr;\n", " year = 0; month = 0; day = 0;\n", " hour = 0; minute = 0; second = 0; microsecond = 0;\n", " tzoffset = 0; tzusec = 0;\n", " rv = parse_isoformat_date(p, separator_location, &year, &month, &day);\n", " tzinfo = tzinfo_from_isoformat_results(rv, tzoffset, tzusec);\n", " dt = new_datetime_subclass_ex(year, month, day, hour, minute,\n", ): if assignment not in function: raise SystemExit("datetime_fromisoformat assignment missing: " + assignment.strip()) PY then pass "_datetimemodule datetime_fromisoformat uses C89-compatible locals" else bad "_datetimemodule datetime_fromisoformat uses C89-compatible locals" fi # The active _datetimemodule.c VC6 frontier must hoist all _datetime_exec() # locals before executable statements while preserving later assignments. if python3 - "$REPO_ROOT/cpython/Modules/_datetimemodule.c" <<'PY' import sys from pathlib import Path source = Path(sys.argv[1]).read_text() start = source.index("_datetime_exec(PyObject *module)") end = source.index("\n}\n\nstatic struct PyModuleDef", start) + 2 function = source[start:end] opening = function[:function.index(" // `&...`")] body = function[len(opening):] for declaration in ( " PyTypeObject *types[] = {\n", " size_t i;\n", " PyObject *d;\n", " PyObject *delta;\n", " PyObject *x;\n", " PyDateTime_CAPI *capi;\n", ): if declaration not in opening: raise SystemExit("_datetime_exec declaration missing from opening group: " + declaration.strip()) for forbidden in ( " PyTypeObject *types[] = {\n &PyDateTime_DateType,", " for (size_t i = 0;", " PyObject *d = PyDateTime_DeltaType.tp_dict;", " PyObject *delta = new_delta(0, 0, 0, 0);", " PyObject *x = create_timezone(delta, NULL);", " PyDateTime_CAPI *capi = get_datetime_capi();", ): if forbidden in body: raise SystemExit("_datetime_exec retains post-statement declaration: " + forbidden.strip()) for assignment in ( " d = PyDateTime_DeltaType.tp_dict;\n", " delta = new_delta(0, 0, 0, 0);\n", " x = create_timezone(delta, NULL);\n", " capi = get_datetime_capi();\n", ): if assignment not in function: raise SystemExit("_datetime_exec assignment missing: " + assignment.strip()) PY then pass "_datetimemodule _datetime_exec uses C89-compatible locals" else bad "_datetimemodule _datetime_exec uses C89-compatible locals" fi if [ "$fail" -ne 0 ]; then echo "FAILED" exit 1 fi echo "ALL TESTS PASSED"