#!/usr/bin/env python3 """Guard C89 local ordering in the float32 packing fallback.""" import sys from pathlib import Path from typing import NoReturn def fail(message: str) -> NoReturn: print(message, file=sys.stderr) raise SystemExit(1) def extract_function(source: str, signature: str) -> str: start = source.find(signature) if start < 0: fail(f"missing function signature: {signature}") brace = source.find("{", start + len(signature)) if brace < 0: fail("missing function body") depth = 0 for end in range(brace, len(source)): if source[end] == "{": depth += 1 elif source[end] == "}": depth -= 1 if depth == 0: return source[brace + 1:end] fail("unterminated function body") source = Path(sys.argv[1]).read_text(encoding="utf-8") body = extract_function(source, "PyFloat_Pack4(double x, char *data, int le)") expected = """ else { unsigned char s[sizeof(float)]; float y = (float)x; int i, incr = 1;""" if expected not in body: fail("float32 fallback locals must be declared at the start of its block") copy = body.find("memcpy(s, &y, sizeof(float));") infinity_check = body.find("if (Py_IS_INFINITY(y) && !Py_IS_INFINITY(x))") pack_loop = body.find("for (i = 0; i < 4; i++)") if min(copy, infinity_check, pack_loop) < 0 or not infinity_check < copy < pack_loop: fail("float32 fallback must preserve overflow-check/copy/packing order") dealloc = extract_function(source, "_PyFloat_ExactDealloc(PyObject *obj)") if " PyFloatObject *op;" not in dealloc or " PyFloatObject *op =" in dealloc: fail("float exact deallocator op must be declared without initializer at entry") if " struct _Py_float_state *state;" not in dealloc or " struct _Py_float_state *state =" in dealloc: fail("float exact deallocator state must be declared without initializer at entry") if not dealloc.find("assert(PyFloat_CheckExact(obj));") < dealloc.find("op = (PyFloatObject *)obj;") < dealloc.find("state = get_float_state();"): fail("float exact deallocator must preserve assert/op/state initialization order") as_double = extract_function(source, "PyFloat_AsDouble(PyObject *op)") if " PyObject *res;" not in as_double or " double val;" not in as_double: fail("PyFloat_AsDouble temporaries must be declared at function entry") if "PyObject *res = _PyNumber_Index(op);" in as_double or "double val = PyLong_AsDouble(res);" in as_double: fail("PyFloat_AsDouble must not redeclare its entry temporaries") if not as_double.find("res = _PyNumber_Index(op);") < as_double.find("val = PyLong_AsDouble(res);") < as_double.find("Py_DECREF(res);"): fail("PyFloat_AsDouble must preserve index-convert/decref order") vectorcall = extract_function(source, "float_vectorcall(PyObject *type, PyObject * const*args,") for declaration in (" Py_ssize_t nargs;", " PyObject *x;"): if declaration not in vectorcall: fail("float_vectorcall locals must be declared at function entry") if "Py_ssize_t nargs =" in vectorcall or "PyObject *x =" in vectorcall: fail("float_vectorcall locals must not be initialized at declaration") if not vectorcall.find("nargs = PyVectorcall_NARGS(nargsf);") < vectorcall.find("x = nargs >= 1 ? args[0] : NULL;"): fail("float_vectorcall must preserve nargs-before-argument selection order") type_init = extract_function(source, "PyTypeObject PyFloat_Type =") expected_slots = """ float_new, /* tp_new */ 0, /* tp_free */ 0, /* tp_is_gc */ 0, /* tp_bases */ 0, /* tp_mro */ 0, /* tp_cache */ 0, /* tp_subclasses */ 0, /* tp_weaklist */ 0, /* tp_del */ 0, /* tp_version_tag */ 0, /* tp_finalize */ (vectorcallfunc)float_vectorcall, /* tp_vectorcall */""" if expected_slots not in type_init or ".tp_vectorcall =" in type_init: fail("PyFloat_Type vectorcall must use C89 positional trailing slots") state_init = extract_function(source, "_PyFloat_InitState(PyInterpreterState *interp)") format_declaration = " float_format_type detected_double_format, detected_float_format;" if format_declaration not in state_init: fail("float format detection locals must be declared at function entry") if format_declaration in state_init[state_init.find("if (!_Py_IsMainInterpreter(interp))"):]: fail("float format detection locals must not be declared after a statement") clinic = Path(sys.argv[2]).read_text(encoding="utf-8") wrapper = extract_function(clinic, "float___getformat__(PyTypeObject *type, PyObject *arg)") declaration = " Py_ssize_t typestr_length;" if declaration not in wrapper: fail("float.__getformat__ typestr_length must be declared at wrapper entry") validation = wrapper.find("if (!PyUnicode_Check(arg))") assignment = wrapper.find("typestr = PyUnicode_AsUTF8AndSize(arg, &typestr_length);") if validation < 0 or assignment < validation or "Py_ssize_t typestr_length;" in wrapper[validation:]: fail("Clinic wrapper must preserve type validation before string conversion") print("floatobject C89 declarations and generated Clinic validation order: PASS")