port: add MSVC600 CPython compatibility layer

This commit is contained in:
RayPals
2026-08-17 17:35:32 +00:00
parent 3f15bfa5f7
commit a7a9560e5c
13 changed files with 2652 additions and 35 deletions
+36
View File
@@ -0,0 +1,36 @@
/* Focused, isolated compile smoke test for Include/internal/pycore_atomic.h
* under VC6/_M_IX86.
*
* Deliberately does NOT go through Python.h or any other pycore_*.h header:
* pycore_interp.h (reached transitively from a real core translation unit
* like Modules/main.c) additionally requires <stdbool.h>, a separate,
* unrelated VC6 gap tracked in PORT_STATUS.md. Isolating pycore_atomic.h
* here keeps this probe's result attributable to the atomics compatibility
* work specifically.
*
* Py_BUILD_CORE is supplied by the probe script (/D Py_BUILD_CORE), the
* same way it is for a real core translation unit. <inttypes.h> (resolved
* to compat/msvc600/inttypes.h for VC6 via the include path) provides
* uintptr_t, which pycore_atomic.h needs but does not itself include; in
* a real build this comes from Python.h/pyport.h, included ahead of any
* pycore_*.h header.
*/
#include <inttypes.h>
#include "pycore_atomic.h"
static _Py_atomic_int atomic_int_value;
static _Py_atomic_address atomic_addr_value;
int
vc6_atomic_smoke(void)
{
_Py_atomic_store(&atomic_int_value, 1);
_Py_atomic_store_relaxed(&atomic_int_value, 2);
_Py_atomic_store(&atomic_addr_value, (uintptr_t)0);
_Py_atomic_store_relaxed(&atomic_addr_value, (uintptr_t)1);
return _Py_atomic_load(&atomic_int_value)
+ _Py_atomic_load_relaxed(&atomic_int_value)
+ (int)_Py_atomic_load(&atomic_addr_value)
+ (int)_Py_atomic_load_relaxed(&atomic_addr_value);
}
@@ -0,0 +1,64 @@
/* Focused compile smoke test for the VC6 C99 compound-literal /
* designated-initializer fixes applied to cpython/Modules/main.c.
*
* VC6 (a pre-C99 compiler) supports neither C99 compound literals
* (`(PyStatus){...}`) nor C99 designated initializers (`{ .field = value,
* ... }`), at any position in a block. Modules/main.c relied on both via
* three distinct patterns:
*
* 1. `status = _PyStatus_OK();` -- a compound-literal macro used as a
* plain assignment's right-hand side. Fixed by calling the existing
* public API function PyStatus_Ok() instead, which
* Python/initconfig.c defines as `return _PyStatus_OK();` -- the
* identical value, no header change needed.
* 2. `cf = _PyCompilerFlags_INIT;` -- likewise a compound-literal macro
* used as a plain assignment. Fixed by assigning PyCompilerFlags's
* two fields explicitly (cf_flags, cf_feature_version), matching the
* macro's own definition field-for-field.
* 3. `_PyArgv args = { .argc = argc, ... };` -- a designated-initializer
* aggregate initializer with no macro involved. Fixed by declaring
* the variable bare and assigning each field explicitly; field order
* does not matter for plain assignment, unlike an initializer list.
*
* See PORT_STATUS.md ("Compound-literal/designated-initializer slice") for
* full detail. Like vc6_mixed_decls_smoke.c, this fixture includes the
* real "Python.h" (Py_BUILD_CORE is supplied by the probe script) because
* PyStatus, PyCompilerFlags, and _PyArgv are real CPython types pulled in
* through the normal public/internal header chain, the same way
* Modules/main.c itself reaches them.
*/
#include "Python.h"
#include "pycore_initconfig.h" // _PyArgv, _PyStatus_OK()
PyStatus
vc6_status_ok_smoke(void)
{
PyStatus status;
/* pattern 1: was `status = _PyStatus_OK();` */
status = PyStatus_Ok();
return status;
}
PyCompilerFlags
vc6_compiler_flags_smoke(void)
{
PyCompilerFlags cf;
/* pattern 2: was `cf = _PyCompilerFlags_INIT;` */
cf.cf_flags = 0;
cf.cf_feature_version = PY_MINOR_VERSION;
return cf;
}
Py_ssize_t
vc6_argv_designated_init_smoke(int argc, wchar_t **argv)
{
/* pattern 3: was `_PyArgv args = { .argc = argc, ... };` */
_PyArgv args;
args.argc = argc;
args.use_bytes_argv = 0;
args.bytes_argv = NULL;
args.wchar_argv = argv;
return args.argc;
}
+162
View File
@@ -0,0 +1,162 @@
/* Focused compile smoke test for the VC6 C89-mixed-declarations fixes
* applied to cpython/Modules/main.c.
*
* VC6 is a C89-only compiler (plus Microsoft extensions): block-scope
* declarations must precede all statements in that block. Modules/main.c
* declared many locals mid-function (typically right before first use,
* C99 style); the fix hoists each declaration to the top of its enclosing
* block, without initializer, and turns the original `TYPE name = expr;`
* into a plain `name = expr;` assignment at its original position. This
* changes no evaluation order and no semantics. See PORT_STATUS.md
* ("Modules/main.c mixed-declarations slice") for full detail.
*
* This fixture is deliberately self-contained (no CPython headers) so it
* exercises the *shape* of every hoist pattern used in main.c, independent
* of main.c's own evolution:
*
* 1. a bare declaration hoisted above an early-return statement
* (pymain_init's `preconfig`/`config`);
* 2. a declaration whose initializer is a side-effecting call, hoisted
* bare and assigned at its original site so the call itself still
* happens after the same preceding statements
* (pymain_import_readline's `mod`, pymain_run_file_obj's `fp`);
* 3. two declarations in a row where hoisting the first turns its site
* into an assignment statement, which would otherwise make the
* second declaration (immediately following) a new violation too
* (pymain_run_file_obj's `cf`/`run`);
* 4. a declaration inside a nested block (its own scope), after
* statements already executed in that block (pymain_run_stdin's
* nested `exitcode`);
* 5. a platform-conditional declaration with the same name but a
* different type per branch, hoisted under the same #ifdef structure
* at the top of the function (pymain_run_startup's `env`).
*/
#define TARGET_WINDOWS 1
/* --- pattern 1: bare declaration hoisted above an early return --- */
static int
decl_after_early_return(int flag)
{
int status;
int extra;
status = flag ? 0 : -1;
if (status != 0) {
return status;
}
extra = 7;
return extra;
}
/* --- pattern 2: side-effecting-initializer declaration hoisted bare,
* assigned at its original position so the call still happens after the
* same preceding statements --- */
static int
side_effect(int *counter)
{
(*counter)++;
return *counter;
}
static int
decl_with_side_effecting_init(int *counter)
{
int guard;
int value;
guard = (*counter < 0);
if (guard) {
return -1;
}
value = side_effect(counter);
return value;
}
/* --- pattern 3: two declarations in a row, first one hoisted becomes an
* assignment, so the second (immediately following) must hoist too --- */
static int
decl_pair_after_hoist(int *counter)
{
int guard;
int a;
int b;
guard = (*counter < 0);
if (guard) {
return -1;
}
a = side_effect(counter);
b = side_effect(counter);
return a + b;
}
/* --- pattern 4: nested-block declaration after statements already
* executed within that same nested block --- */
static int
decl_after_statement_in_nested_block(int interactive)
{
int outer_result = 0;
if (interactive) {
int inner_value;
outer_result += 1;
outer_result += 2;
inner_value = outer_result * 10;
return inner_value;
}
return outer_result;
}
/* --- pattern 5: platform-conditional declaration, same name, different
* type per branch, hoisted under the same #ifdef at the top --- */
static long
decl_platform_conditional(void)
{
int ret;
#ifdef TARGET_WINDOWS
const char *env;
#else
const long *env;
#endif
ret = 1;
if (!ret) {
return 0;
}
#ifdef TARGET_WINDOWS
env = "PYTHONSTARTUP";
if (env == 0) {
return 0;
}
return (long)env[0];
#else
env = 0;
if (env == 0) {
return 0;
}
return *env;
#endif
}
int
main(void)
{
int counter = 0;
int total = 0;
total += decl_after_early_return(1);
total += decl_with_side_effecting_init(&counter);
total += decl_pair_after_hoist(&counter);
total += decl_after_statement_in_nested_block(1);
total += (int)decl_platform_conditional();
return (total >= 0) ? 0 : 1;
}
+54
View File
@@ -0,0 +1,54 @@
/* Focused compile smoke test for the VC6 C89-mixed-declarations fixes in
* four Include/internal/pycore_*.h headers: pycore_code.h, pycore_dict.h,
* pycore_list.h, and pycore_call.h.
*
* VC6 is a C89-only compiler (plus Microsoft extensions): block-scope
* declarations must precede all statements in that block. CPython 3.11's
* source freely uses C99-style declarations after statements (e.g. after
* an assert()); the fix hoists each such declaration to the top of its
* enclosing block without changing evaluation order or behavior. See
* PORT_STATUS.md ("Mixed-declarations header slice") for full detail.
*
* Unlike scripts/fixtures/vc6_atomic_smoke.c and vc6_stdbool_smoke.c, this
* fixture includes the real "Python.h" (Py_BUILD_CORE is supplied by the
* probe script) rather than isolating the pycore_*.h headers from it:
* these four headers assume types (PyObject, PyCodeObject, PyListObject,
* PyDictValues, vectorcallfunc, ...) that come from the normal public
* header chain, the same way real CPython .c sources reach them.
*/
#include "Python.h"
#include "pycore_code.h"
#include "pycore_dict.h"
#include "pycore_list.h"
#include "pycore_call.h"
int
vc6_locals_kind_smoke(PyObject *kinds, int i, _PyLocals_Kind kind)
{
_PyLocals_SetKind(kinds, i, kind);
return (int)_PyLocals_GetKind(kinds, i);
}
uint16_t
vc6_adaptive_counter_smoke(uint16_t counter)
{
return adaptive_counter_backoff(counter);
}
void
vc6_dict_values_smoke(PyDictValues *values, Py_ssize_t ix)
{
_PyDictValues_AddToInsertionOrder(values, ix);
}
int
vc6_list_append_smoke(PyListObject *self, PyObject *newitem)
{
return _PyList_AppendTakeRef(self, newitem);
}
vectorcallfunc
vc6_vectorcall_smoke(PyObject *callable)
{
return _PyVectorcall_FunctionInline(callable);
}
+31
View File
@@ -0,0 +1,31 @@
/* Focused, isolated compile smoke test for compat/msvc600/stdbool.h under
* VC6.
*
* Deliberately does NOT go through pycore_interp.h or any other pycore_*.h
* header: this isolates the result to the <stdbool.h> shim itself, the
* same way scripts/fixtures/vc6_atomic_smoke.c isolates pycore_atomic.h.
* Modules/main.c (see scripts/vc6-probe-core.test.sh) is the real-header
* reference case that proves compat/msvc600/stdbool.h is actually what
* pycore_interp.h's `#include <stdbool.h>` resolves to via the VC6 probe
* scripts' include path.
*/
#include <stdbool.h>
static bool vc6_stdbool_flag = true;
int
vc6_stdbool_smoke(void)
{
bool a = true;
bool b = false;
bool c = a && !b;
vc6_stdbool_flag = c;
if (!__bool_true_false_are_defined) {
return -1;
}
return (int)(a == true) + (int)(b == false) + (int)(c == true)
+ (int)vc6_stdbool_flag;
}