Files
Python3-Win9x/scripts/vc6-probe-pythoncore-frontier.test.sh
T

2808 lines
114 KiB
Bash
Executable File

#!/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 current mathmodule.c VC6 frontier explicit: MSVC6 has no CRT
# atanh(), so the guarded fallback must preserve signed zero, NaN, and domain
# error behavior while native compilers retain the CRT path.
if python3 - "$REPO_ROOT/cpython/Modules/mathmodule.c" <<'PY'
import sys
from pathlib import Path
source = Path(sys.argv[1]).read_text()
start = source.index("#if defined(_MSC_VER) && _MSC_VER < 1300")
end = source.index("#else\n# define PY_MATH_ULONG_LONG", start)
block = source[start:end]
if "m_atanh(double x)" not in block:
raise SystemExit("VC6 m_atanh fallback missing")
if "if (Py_IS_NAN(x))" not in block:
raise SystemExit("m_atanh NaN handling missing")
if "if (a >= 1.0)" not in block or "errno = EDOM;" not in block:
raise SystemExit("m_atanh domain handling missing")
if "return x;" not in block:
raise SystemExit("m_atanh signed-zero/small-input preservation missing")
if "FUNC1(atanh, m_atanh, 0," not in source:
raise SystemExit("atanh is not routed through m_atanh")
if "# define m_atanh atanh" not in source:
raise SystemExit("native m_atanh mapping missing")
PY
then
pass "mathmodule provides a guarded VC6 atanh fallback"
else
bad "mathmodule provides a guarded VC6 atanh fallback"
fi
# Keep the current mathmodule.c VC6 frontier explicit: MSVC6 has no CRT
# cbrt(), so the guarded fallback must preserve zero/NaN and native mapping.
if python3 - "$REPO_ROOT/cpython/Modules/mathmodule.c" <<'PY'
import sys
from pathlib import Path
source = Path(sys.argv[1]).read_text()
start = source.index("#if defined(_MSC_VER) && _MSC_VER < 1300")
end = source.index("#else\n# define PY_MATH_ULONG_LONG", start)
block = source[start:end]
if "m_cbrt(double x)" not in block:
raise SystemExit("VC6 m_cbrt fallback missing")
if "if (Py_IS_NAN(x))" not in block or "if (x == 0.0)" not in block:
raise SystemExit("m_cbrt special-value handling missing")
if "FUNC1(cbrt, m_cbrt, 0," not in source:
raise SystemExit("cbrt is not routed through m_cbrt")
if "# define m_cbrt cbrt" not in source:
raise SystemExit("native m_cbrt mapping missing")
PY
then
pass "mathmodule provides a guarded VC6 cbrt fallback"
else
bad "mathmodule provides a guarded VC6 cbrt fallback"
fi
# Keep the current mathmodule.c VC6 frontier explicit: MSVC6 has no CRT
# exp2(), so the guarded fallback must preserve NaN handling and native mapping.
if python3 - "$REPO_ROOT/cpython/Modules/mathmodule.c" <<'PY'
import sys
from pathlib import Path
source = Path(sys.argv[1]).read_text()
start = source.index("#if defined(_MSC_VER) && _MSC_VER < 1300")
end = source.index("#else\n# define PY_MATH_ULONG_LONG", start)
block = source[start:end]
if "m_exp2(double x)" not in block:
raise SystemExit("VC6 m_exp2 fallback missing")
if "if (Py_IS_NAN(x))" not in block:
raise SystemExit("m_exp2 NaN handling missing")
if "FUNC1(exp2, m_exp2, 1," not in source:
raise SystemExit("exp2 is not routed through m_exp2")
if "# define m_exp2 exp2" not in source:
raise SystemExit("native m_exp2 mapping missing")
PY
then
pass "mathmodule provides a guarded VC6 exp2 fallback"
else
bad "mathmodule provides a guarded VC6 exp2 fallback"
fi
# Keep the current mathmodule.c VC6 frontier explicit: MSVC6 has no CRT
# expm1(), so the guarded fallback must preserve small inputs and native mapping.
if python3 - "$REPO_ROOT/cpython/Modules/mathmodule.c" <<'PY'
import sys
from pathlib import Path
source = Path(sys.argv[1]).read_text()
start = source.index("#if defined(_MSC_VER) && _MSC_VER < 1300")
end = source.index("#else\n# define PY_MATH_ULONG_LONG", start)
block = source[start:end]
if "m_expm1(double x)" not in block:
raise SystemExit("VC6 m_expm1 fallback missing")
if "if (fabs(x) < 1e-5)" not in block:
raise SystemExit("m_expm1 small-input handling missing")
if "FUNC1(expm1, m_expm1, 1," not in source:
raise SystemExit("expm1 is not routed through m_expm1")
if "# define m_expm1 expm1" not in source:
raise SystemExit("native m_expm1 mapping missing")
PY
then
pass "mathmodule provides a guarded VC6 expm1 fallback"
else
bad "mathmodule provides a guarded VC6 expm1 fallback"
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"(?<![A-Za-z_])unsigned long long(?![A-Za-z_])", family):
raise SystemExit(1)
if re.search(r"(?<![A-Za-z_])long long(?![A-Za-z_])", family):
raise SystemExit(1)
for spelling in ("PY_LONG_LONG", "unsigned PY_LONG_LONG"):
if spelling not in family:
raise SystemExit(1)
if family.count("(PY_LONG_LONG *)ap->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
# The active mathmodule.c VC6 frontier must provide a local asinh fallback for
# MSVC6 while leaving newer CRTs on the native implementation.
if python3 - "$REPO_ROOT/cpython/Modules/mathmodule.c" <<'PY'
import sys
from pathlib import Path
source = Path(sys.argv[1]).read_text()
start = source.index("#if defined(_MSC_VER) && _MSC_VER < 1300")
end = source.index("#endif\n\n/*[clinic input]", start)
block = source[start:end]
if "static double\nm_asinh(double x)" not in block:
raise SystemExit("VC6 m_asinh fallback missing")
if "FUNC1(asinh, asinh, 0," in source:
raise SystemExit("mathmodule still routes asinh through the unavailable CRT symbol")
if "FUNC1(asinh, m_asinh, 0," not in source:
raise SystemExit("mathmodule does not route asinh through the fallback")
PY
then
pass "mathmodule provides a guarded VC6 asinh fallback"
else
bad "mathmodule provides a guarded VC6 asinh fallback"
fi
# The active mathmodule.c VC6 frontier must provide a local acosh fallback for
# MSVC6 while leaving newer CRTs on the native implementation.
if python3 - "$REPO_ROOT/cpython/Modules/mathmodule.c" <<'PY'
import sys
from pathlib import Path
source = Path(sys.argv[1]).read_text()
start = source.index("#if defined(_MSC_VER) && _MSC_VER < 1300")
end = source.index("#endif\n\n/*[clinic input]", start)
block = source[start:end]
if "static double\nm_acosh(double x)" not in block:
raise SystemExit("VC6 m_acosh fallback missing")
if "FUNC1(acosh, acosh, 0," in source:
raise SystemExit("mathmodule still routes acosh through the unavailable CRT symbol")
if "FUNC1(acosh, m_acosh, 0," not in source:
raise SystemExit("mathmodule does not route acosh through the fallback")
PY
then
pass "mathmodule provides a guarded VC6 acosh fallback"
else
bad "mathmodule provides a guarded VC6 acosh fallback"
fi
# The active mathmodule.c VC6/C89 frontier must hoist math_gcd()'s borrowed
# singleton local before executable statements while preserving its assignment point.
if python3 - "$REPO_ROOT/cpython/Modules/mathmodule.c" <<'PY'
import sys
from pathlib import Path
source = Path(sys.argv[1]).read_text()
start = source.index("math_gcd(PyObject *module")
end = source.index("\n}\n\nPyDoc_STRVAR(math_gcd_doc", start) + 2
function = source[start:end]
opening = function[:function.index(" if (nargs == 0)")]
if " PyObject *res, *x, *one;\n" not in opening:
raise SystemExit("math_gcd one declaration missing from opening group")
if " PyObject *one = _PyLong_GetOne();" in function:
raise SystemExit("math_gcd retains mixed one declaration")
if function.count(" one = _PyLong_GetOne(); // borrowed ref\n") != 1:
raise SystemExit("math_gcd one assignment missing")
PY
then
pass "math_gcd uses C89 declaration-order-safe borrowed singleton local"
else
bad "math_gcd uses C89 declaration-order-safe borrowed singleton local"
fi
# The active mathmodule.c VC6/C89 frontier must hoist math_lcm()'s borrowed
# zero local before executable statements while preserving its assignment point.
if python3 - "$REPO_ROOT/cpython/Modules/mathmodule.c" <<'PY'
import sys
from pathlib import Path
source = Path(sys.argv[1]).read_text()
start = source.index("math_lcm(PyObject *module")
end = source.index("\n}\n\n\nPyDoc_STRVAR(math_lcm_doc", start) + 2
function = source[start:end]
opening = function[:function.index(" if (nargs == 0)")]
if " PyObject *res, *x, *zero;\n" not in opening:
raise SystemExit("math_lcm zero declaration missing from opening group")
if " PyObject *zero = _PyLong_GetZero();" in function:
raise SystemExit("math_lcm retains mixed zero declaration")
if function.count(" zero = _PyLong_GetZero(); // borrowed ref\n") != 1:
raise SystemExit("math_lcm zero assignment missing or duplicated")
if " Py_INCREF(zero);" in function or " Py_DECREF(zero);" in function:
raise SystemExit("math_lcm borrowed zero reference ownership changed")
PY
then
pass "math_lcm uses C89 declaration-order-safe borrowed zero local"
else
bad "math_lcm uses C89 declaration-order-safe borrowed zero local"
fi
# The active gcmodule.c VC6/C89 frontier must not redeclare clear_unreachable_mask()
# locals after the initial declaration group.
if python3 - "$REPO_ROOT/cpython/Modules/gcmodule.c" <<'PY'
import sys
from pathlib import Path
source = Path(sys.argv[1]).read_text()
start = source.index("clear_unreachable_mask(PyGC_Head *unreachable)")
end = source.index("\n}\n\n/* A traversal callback", start) + 2
function = source[start:end]
if function.count(" PyGC_Head *gc, *next;\n") != 1:
raise SystemExit("clear_unreachable_mask duplicate local declaration")
PY
then
pass "gcmodule clear_unreachable_mask uses one C89 declaration group"
else
bad "gcmodule clear_unreachable_mask uses one C89 declaration group"
fi
# math_ceil() must declare x before statements and convert at the original point.
if python3 - "$REPO_ROOT/cpython/Modules/mathmodule.c" <<'PY'
import sys
from pathlib import Path
source = Path(sys.argv[1]).read_text()
start = source.index("math_ceil(PyObject *module, PyObject *number)")
end = source.index("\n}\n", start) + 2
function = source[start:end]
opening = function[:function.index(" if (!PyFloat_CheckExact(number))")]
assert (
" double x;\n" in opening
and "double x =" not in function
and function.count(" x = PyFloat_AsDouble(number);\n") == 1
and " if (PyErr_Occurred())\n return NULL;\n }\n"
" x = PyFloat_AsDouble(number);\n"
" if (x == -1.0 && PyErr_Occurred())" in function
), "math_ceil needs an opening x declaration and original-point assignment"
PY
then
pass "math_ceil uses an opening C89 declaration and original-point assignment"
else
bad "math_ceil uses an opening C89 declaration and original-point assignment"
fi
# math_isqrt() must declare its slow-path loop counter in the opening group.
if python3 - "$REPO_ROOT/cpython/Modules/mathmodule.c" <<'PY'
import sys
from pathlib import Path
source = Path(sys.argv[1]).read_text()
start = source.index("math_isqrt(PyObject *module, PyObject *n)")
end = source.index("\n}\n", start) + 2
function = source[start:end]
opening = function[:function.index(" n = _PyNumber_Index(n);")]
assert (
" int s;\n" in opening
and "for (int s" not in function
and function.count(" for (s = c_bit_length - 6; s >= 0; --s) {\n") == 1
), "math_isqrt needs an opening int s declaration and assignment-only loop initializer"
PY
then
pass "math_isqrt uses an opening C89 loop counter declaration"
else
bad "math_isqrt uses an opening C89 loop counter declaration"
fi
# _check_long_mult_overflow() must declare temporaries before its early return
# while evaluating their expressions only at the original point after it.
if python3 - "$REPO_ROOT/cpython/Modules/mathmodule.c" <<'PY'
import sys
from pathlib import Path
source = Path(sys.argv[1]).read_text()
start = source.index("_check_long_mult_overflow(long a, long b)")
end = source.index("\n}\n", start) + 2
function = source[start:end]
opening, statements = function.split(" if (doubled_longprod == doubleprod)", 1)
for name in ("diff", "absdiff", "absprod"):
assert f" double {name};\n" in opening, (
f"_check_long_mult_overflow needs an opening {name} declaration"
)
assert f"double {name}" not in statements, (
f"_check_long_mult_overflow retains a mixed {name} declaration"
)
assert statements == """ {
return 0;
}
diff = doubled_longprod - doubleprod;
absdiff = diff >= 0.0 ? diff : -diff;
absprod = doubleprod >= 0.0 ? doubleprod : -doubleprod;
if (32.0 * absdiff <= absprod) {
return 0;
}
return 1;
}""", "_check_long_mult_overflow must preserve evaluation order and return logic"
PY
then
pass "_check_long_mult_overflow uses opening C89 declarations and original-point assignments"
else
bad "_check_long_mult_overflow uses opening C89 declarations and original-point assignments"
fi
# fast_comb_limits2 must be declared before perm_comb_small() executes statements.
if python3 - "$REPO_ROOT/cpython/Modules/mathmodule.c" <<'PY'
import sys
from pathlib import Path
source = Path(sys.argv[1]).read_text()
start = source.index("perm_comb_small(PY_MATH_ULONG_LONG n,")
end = source.index("\n}\n", start) + 2
function = source[start:end]
opening, statements = function.split(" if (k == 0) {", 1)
declaration = """ static const PY_MATH_ULONG_LONG fast_comb_limits2[] = {
0, PY_MATH_ULLONG_MAX, (PY_MATH_ULONG_LONG)4294967296I64, 3329022, 102570, 13467, 3612, 1449, // 0-7
746, 453, 308, 227, 178, 147, // 8-13
};"""
assert (
declaration in opening
and function.count("static const PY_MATH_ULONG_LONG fast_comb_limits2[]") == 1
and " if (k < Py_ARRAY_LENGTH(fast_comb_limits2) && n <= fast_comb_limits2[k]) {" in statements
), "perm_comb_small opening fast_comb_limits2 declaration missing or changed"
PY
then
pass "perm_comb_small uses a C89 declaration-order-safe fast_comb_limits2 array"
else
bad "perm_comb_small uses a C89 declaration-order-safe fast_comb_limits2 array"
fi
if [ "$fail" -ne 0 ]; then
echo "FAILED"
exit 1
fi
echo "ALL TESTS PASSED"