927 lines
49 KiB
Markdown
927 lines
49 KiB
Markdown
# Port Status — CPython 3.11.16 to Windows 98 SE
|
|
|
|
Date: 2026-08-17
|
|
Baseline: CPython `v3.11.16`
|
|
Compiler: MSVC 6.0 (`cl.exe` 12.00.8804), from external `MSVC600`
|
|
Host test environment: Linux + Wine
|
|
|
|
## Files changed in this revision (`pylifecycle.c` VC6 PyRuntime section slice)
|
|
|
|
All still uncommitted; nothing in this list has been pushed anywhere.
|
|
|
|
- `cpython/Python/pylifecycle.c` — narrow VC6-only compatibility edit for
|
|
the first `Python/pylifecycle.c` blocker: keep the modern Windows
|
|
`#pragma section("PyRuntime", read, write)` +
|
|
`__declspec(allocate("PyRuntime"))` path unchanged for non-VC6 compilers,
|
|
but use VC6's supported `#pragma data_seg("PyRuntime")` /
|
|
`#pragma data_seg()` spelling around the initialized `_PyRuntime` object
|
|
when `_MSC_VER < 1300`. The `_PyRuntimeState_INIT` initializer remains
|
|
exactly at the definition site.
|
|
- `compat/msvc600/cpython-3.11.16-vc6-headers.patch` — regenerated in full
|
|
from `git -C cpython diff`; now 767 lines and includes the
|
|
`Python/pylifecycle.c` hunk alongside previous uncommitted compatibility
|
|
edits.
|
|
- `PORT_STATUS.md` — this revision.
|
|
|
|
**Command results (all reproduced in this revision):**
|
|
|
|
| Command | Result |
|
|
|---|---|
|
|
| `bash scripts/vc6-probe.test.sh` | exit 0, `ALL TESTS PASSED` — 6/6 checks `ok` |
|
|
| `bash scripts/vc6-probe-core.test.sh` | exit 0, `ALL TESTS PASSED` — 27/27 checks `ok`; includes `Modules/main.c` compile-only clean check |
|
|
| `bash scripts/vc6-probe-core.sh cpython/Python/pylifecycle.c` | exit 2 — gets past the prior VC6-incompatible `PyRuntime` section pragma/declaration pair; no `unknown pragma` warning and no `C2341` segment error remain. New first blocker is `cpython\Python\pylifecycle.c(112) : error C2059: syntax error : '.'` in `_PyRuntimeState_INIT`, followed by more C99 compound-literal/designated-initializer and C89 mixed-declaration errors; the run later hits `fatal error C1003` at line 1232. |
|
|
|
|
**Caveat:** all probe results here are compile-only (`/c`) results. No link,
|
|
no `pythoncore`/`python.exe` build, no Wine launch, and no Windows 98 runtime
|
|
execution was attempted or claimed.
|
|
|
|
## Verified
|
|
|
|
- CPython source checkout is at the requested `v3.11.16` baseline.
|
|
- The MSVC600 tool bundle is present locally and its `VC98/bin/VCVARS32.BAT`
|
|
environment script runs under Wine.
|
|
- MSVC6 reports:
|
|
|
|
```text
|
|
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
|
|
```
|
|
|
|
- A direct compile probe reaches CPython 3.11 headers. The first unmodified
|
|
probe fails before linking, proving the compiler is being invoked against the
|
|
intended baseline.
|
|
- Project-local compatibility headers have been added under
|
|
`compat/msvc600/`: `inttypes.h` (first isolated shim) and, as of this
|
|
revision, `stdbool.h` (see "stdbool slice" below). Neither is declared
|
|
sufficient for the port on its own.
|
|
- **`scripts/vc6-probe.sh` was fixed and is now truthful.** The script piped
|
|
`cl.exe` output through `grep`/`sed` to drop Wine's `:err:` debug noise and
|
|
to shorten absolute host paths to repo-relative ones. The path-shortening
|
|
step was silently broken: it built a `sed` pattern directly from a
|
|
Windows-style path (e.g. `Z:\home\ubuntu\...\`), and both `sed` BRE and bash
|
|
glob patterns treat a bare `\` in a *pattern* as an escape character, so
|
|
`\h`, `\u`, `\p`, etc. were consumed as (mostly no-op) escapes instead of
|
|
literal characters and the substitution never matched anything — diagnostics
|
|
always showed full absolute Wine paths instead of the intended
|
|
repo-relative ones. Fixed by matching the prefix as a literal string via a
|
|
quoted bash `${line//"$prefix"/}` expansion (quoting forces literal, not
|
|
glob, matching) instead of an unescaped regex. The pipeline's exit-status
|
|
capture via `PIPESTATUS[0]` was already correct (verified with a
|
|
deliberately broken translation unit: `cl.exe`'s real nonzero exit code
|
|
propagated correctly both before and after this fix); it is now covered by
|
|
a regression test so it cannot regress silently.
|
|
A new regression test, `scripts/vc6-probe.test.sh`, exercises both the
|
|
path-stripping helper directly and — when wine + MSVC600 are available, as
|
|
they are in this environment — the real script against a deliberately
|
|
invalid `.c` file, asserting a nonzero exit code, a surfaced `error C`
|
|
diagnostic, and no leaked absolute Wine path. Run: `bash
|
|
scripts/vc6-probe.test.sh`. Last run: all 6 checks `ok`.
|
|
- **`Include/internal/pycore_atomic.h`'s VC6/`intrin.h` blocker is now
|
|
resolved for 32-bit x86 (`_M_IX86`) and verified; see "Atomics slice"
|
|
below for full detail and honesty notes on what is and is not covered.
|
|
- **`Include/internal/pycore_interp.h`'s VC6/`<stdbool.h>` blocker is now
|
|
resolved and verified; see "stdbool slice" below for full detail.
|
|
- **The VC6 C89-vs-C99 mixed-declarations blocker in four
|
|
`Include/internal/pycore_*.h` headers (`pycore_code.h`, `pycore_dict.h`,
|
|
`pycore_list.h`, `pycore_call.h`) reachable from `Modules/main.c` is now
|
|
resolved and verified; see "Mixed-declarations header slice" below for
|
|
full detail.
|
|
- **The same C89-vs-C99 mixed-declarations pattern, pervasive within
|
|
`Modules/main.c`'s own body (29 error sites across roughly a dozen
|
|
functions), is now also resolved and verified; see "Modules/main.c
|
|
declaration-order slice" below for full detail.** With that fix applied,
|
|
`Modules/main.c` hits a new, unrelated, and out-of-scope-for-that-slice
|
|
blocker: several C99 compound-literal/designated-initializer macros
|
|
(`_PyStatus_OK()`, `_PyCompilerFlags_INIT`, and the `_PyArgv`-style struct
|
|
literals in `Py_Main()`/`Py_BytesMain()`) that VC6 does not support at
|
|
all, independent of declaration position.
|
|
- **That compound-literal/designated-initializer blocker is now also
|
|
resolved and verified for all 8 sites in `Modules/main.c`; see
|
|
"Compound-literal / designated-initializer slice" below for full
|
|
detail.** With that fix applied, `cpython/Modules/main.c` compiles with
|
|
`cl.exe` exit status 0 under `scripts/vc6-probe-core.sh` — no errors, no
|
|
warnings. **This is a compile-only (`/c`) result for one translation
|
|
unit; it is not a link, not a `pythoncore`/`python.exe` build, and not a
|
|
runtime claim** (`vc6-probe-core.sh` never attempts to link). Probing
|
|
the next core translation unit in `Modules/main.c`'s own call chain,
|
|
`Python/pylifecycle.c`, hits the same two already-solved blocker classes
|
|
again (recurring, not novel) plus enough additional errors to exceed
|
|
the compiler's 100-error cap before the whole file is even seen; see
|
|
"Next blocker: `Python/pylifecycle.c`" below. No link or runtime success
|
|
is claimed anywhere in this document.
|
|
|
|
## `pycore_object.h` VC6 `__func__` / GC helper declaration-order slice
|
|
|
|
**Scope: one internal header only, as requested.** The real
|
|
`Python/pylifecycle.c` core probe previously stopped first in
|
|
`Include/internal/pycore_object.h`: VC6 reported `__func__` undeclared at
|
|
the `_PyObject_ASSERT_FROM(..., __func__)` calls in `_PyObject_GC_TRACK()`,
|
|
then cascaded into C89 mixed-declaration errors for the local `PyGC_Head *`
|
|
and `PyInterpreterState *` declarations in `_PyObject_GC_TRACK()` and
|
|
`_PyObject_GC_UNTRACK()`.
|
|
|
|
The header now has a VC6-only fallback:
|
|
|
|
```c
|
|
#if defined(_MSC_VER) && _MSC_VER < 1300 && !defined(__func__)
|
|
# define __func__ "<unknown>"
|
|
#endif
|
|
```
|
|
|
|
A quick VC6 smoke probe showed `__FUNCTION__` is also unavailable in VC6, so
|
|
there was no exact function-name extension to preserve. This mapping only
|
|
degrades debug/fatal diagnostic function-name text on VC6; C99 compilers and
|
|
newer MSVC keep their normal behavior. The only declaration-order edits are
|
|
the mechanical C89 transform in `_PyObject_GC_TRACK()` and
|
|
`_PyObject_GC_UNTRACK()`: declarations at block start, assignments left at
|
|
the original evaluation points.
|
|
|
|
Verification: `bash scripts/vc6-probe-core.sh cpython/Python/pylifecycle.c`
|
|
now gets past `pycore_object.h`; the new first blocker is the Windows
|
|
section-allocation pragma pair in `Python/pylifecycle.c` (see "Next
|
|
blocker" below).
|
|
|
|
## Atomics slice (`pycore_atomic.h`, VC6 / `_M_IX86`)
|
|
|
|
**Scope: 32-bit x86 only, matching this port's only target (Windows 9x has
|
|
no x64 edition).** `Include/internal/pycore_atomic.h` previously included
|
|
`<intrin.h>` and used `_Interlocked*` compiler intrinsics unconditionally
|
|
for any `_MSC_VER`-defined compiler; VC6 (`_MSC_VER` 1200) has neither. The
|
|
now-applied, still-uncommitted `compat/msvc600/cpython-3.11.16-vc6-headers.patch`
|
|
adds two things to this header, both gated strictly to
|
|
`defined(_MSC_VER) && _MSC_VER < 1300 && defined(_M_IX86)` (a new
|
|
`_Py_ATOMIC_VC6_X86` macro) so every other compiler/version — including
|
|
modern MSVC on `_M_X64`/ARM and the GCC/clang/C11 paths — keeps its exact
|
|
prior behavior, byte-for-byte (verified: `git diff` on the non-VC6 branches
|
|
shows only their line numbers moving under the new `#elif`, no content
|
|
changes):
|
|
|
|
1. The top-of-file `<intrin.h>`/`<immintrin.h>` include is skipped for
|
|
VC6/`_M_IX86`; `<windows.h>` is included instead, to declare
|
|
`InterlockedExchange`/`InterlockedCompareExchange`.
|
|
2. A new `#if defined(_Py_ATOMIC_VC6_X86)` branch (ahead of the existing
|
|
`_M_IX86 || _M_X64` branch, now `#elif`) implements
|
|
`_Py_atomic_store_explicit`/`_Py_atomic_load_explicit` for
|
|
`_Py_atomic_int`/`_Py_atomic_address` using the Win32
|
|
`InterlockedExchange`/`InterlockedCompareExchange` API — present in
|
|
`KERNEL32.DLL` since Windows 98 (confirmed against this project's actual
|
|
MSVC600 VC98 SDK headers, `MSVC600/VC98/Include/WINBASE.H`, which declare
|
|
both for `_M_IX86` outside the MRx000/Alpha-only intrinsic path). Both
|
|
give a full memory barrier on x86, same as the intrinsics they replace,
|
|
so — as the original comment already notes for the HLE-unavailable case
|
|
— every `_Py_memory_order` maps to the same fully-fenced operation; VC6
|
|
has no HLE (`_HLEAcquire`/`_HLERelease`, VS2013+) intrinsics or hardware
|
|
support to preserve here regardless.
|
|
|
|
**64-bit atomics are explicitly NOT implemented, and this is not a gap in
|
|
practice for this port.** `_Py_atomic_address` (`uintptr_t`) and
|
|
`_Py_atomic_int` (`int`) are both 4 bytes on 32-bit x86, so the existing
|
|
`sizeof(...) == 8` branches that select the 64-bit store/load paths in the
|
|
pre-existing `_M_IX86 || _M_X64` code are already unreachable dead code on
|
|
`_M_IX86` upstream, before this patch — VC6 never defines `_M_X64`. The VC6
|
|
branch added here therefore has no 64-bit path to implement, and a
|
|
`#error` guards the (unreachable-under-VC6) case where `_M_X64` were ever
|
|
also defined, so a future misconfiguration fails loudly at compile time
|
|
instead of silently truncating a 64-bit atomic. No `CMPXCHG8B` inline
|
|
assembly was written, and none is required for this header; if some other
|
|
part of the port needs genuine 64-bit interlocked ops on VC6 later, that is
|
|
a separate, not-yet-encountered problem, not something papered over here.
|
|
|
|
**Verified with two independent probes**, both under
|
|
`scripts/vc6-probe-core.sh` (new; extends `scripts/vc6-probe.sh`'s approach
|
|
to `Py_BUILD_CORE` translation units — `/D Py_BUILD_CORE` plus
|
|
`Include/internal` on the include path):
|
|
|
|
- `scripts/fixtures/vc6_atomic_smoke.c` (new) — a minimal, isolated
|
|
translation unit that includes only `pycore_atomic.h` (via `<inttypes.h>`
|
|
for `uintptr_t`, which real callers get from `Python.h`/`pyport.h`) and
|
|
exercises `_Py_atomic_store`/`_Py_atomic_load` and their `_relaxed`
|
|
variants on both `_Py_atomic_int` and `_Py_atomic_address`:
|
|
|
|
```text
|
|
$ bash scripts/vc6-probe-core.sh scripts/fixtures/vc6_atomic_smoke.c
|
|
== MSVC600 Py_BUILD_CORE compile probe ==
|
|
source : scripts/fixtures/vc6_atomic_smoke.c
|
|
...
|
|
vc6_atomic_smoke.c
|
|
|
|
== cl.exe exit status: 0 ==
|
|
```
|
|
|
|
No errors, no warnings.
|
|
|
|
- `cpython/Modules/main.c`, the same real core translation unit identified
|
|
as the next blocker previously. It now compiles *past*
|
|
`pycore_atomic.h` (reached transitively via `pycore_runtime.h` before
|
|
`pycore_interp.h`) and hits a new, different, and unrelated blocker:
|
|
|
|
```text
|
|
$ bash scripts/vc6-probe-core.sh
|
|
== MSVC600 Py_BUILD_CORE compile probe ==
|
|
source : cpython/Modules/main.c
|
|
...
|
|
cpython\Include\internal\pycore_interp.h(11) : fatal error C1083:
|
|
Cannot open include file: 'stdbool.h': No such file or directory
|
|
|
|
== cl.exe exit status: 2 ==
|
|
```
|
|
|
|
`Include/internal/pycore_interp.h` line 11 does `#include <stdbool.h>`
|
|
before its own `#include "pycore_atomic.h"` on line 13 — a C99 standard
|
|
header VC6 does not ship, unrelated to atomics. This is the next blocker
|
|
for this port and is left unaddressed here, out of scope for this slice.
|
|
|
|
Both probes are covered by a new regression test,
|
|
`scripts/vc6-probe-core.test.sh`, which additionally asserts (so this
|
|
result cannot regress silently) that a deliberately broken translation
|
|
unit still fails with a surfaced `error C` diagnostic, and that
|
|
`Modules/main.c`'s current failure is specifically *not* at
|
|
`pycore_atomic.h`/`intrin.h` (i.e. that blocker has not resurfaced). Run:
|
|
`bash scripts/vc6-probe-core.test.sh`. At the time this atomics slice was
|
|
written, this was all 6 checks `ok`; the stdbool slice below added 5 more
|
|
checks to the same file (now 11; see that section for the current count
|
|
and last-run result). `scripts/vc6-probe.test.sh` was re-run after the
|
|
atomics change and still passed (all 6 checks `ok`), confirming the
|
|
public-header probe was unaffected.
|
|
|
|
## stdbool slice (`pycore_interp.h`, VC6 / C99 `<stdbool.h>`)
|
|
|
|
**`Include/internal/pycore_interp.h` line 11 does `#include <stdbool.h>`**
|
|
before its own `#include "pycore_atomic.h"` on line 13 — a C99 standard
|
|
header VC6 (`_MSC_VER` 1200, which predates C99 entirely) does not ship.
|
|
In VC6's C mode, `bool`/`true`/`false` are not keywords at all (they are
|
|
C++-only there), and there is no standard-library `<stdbool.h>` to fall
|
|
back on.
|
|
|
|
The now-added, still-uncommitted `compat/msvc600/stdbool.h` is a
|
|
project-local fallback for this single header, **not a patch to CPython
|
|
source.** It is picked up purely through include-path ordering: both
|
|
`scripts/vc6-probe.sh` and `scripts/vc6-probe-core.sh` already put
|
|
`compat/msvc600` first on the VC6 `/I` include path (needed earlier for
|
|
`compat/msvc600/inttypes.h`), so VC6's `#include <stdbool.h>` resolves to
|
|
this project-local header before it would ever reach a (nonexistent)
|
|
system one. Nothing under `cpython/` changes for this slice, so
|
|
`compat/msvc600/cpython-3.11.16-vc6-headers.patch` is unchanged. Every
|
|
other compiler is unaffected for the same reason a browser's `/I` search
|
|
order doesn't affect other browsers: `compat/msvc600` is never added to
|
|
any other compiler's include path, so a conforming C99/C11 compiler still
|
|
gets its own real `<stdbool.h>` (or built-in `_Bool`/`bool`), untouched.
|
|
As defence in depth (not because it is expected to be reachable), the
|
|
header itself also `#error`s out if included under anything but
|
|
`_MSC_VER < 1300`.
|
|
|
|
The shim defines the same four names as the standard C99 header —
|
|
`bool` (as `int`, since VC6 has no `_Bool` built-in type to alias),
|
|
`true`, `false`, and `__bool_true_false_are_defined` — as plain
|
|
`#define` macros rather than a typedef, matching the standard's
|
|
requirement that a conforming program be able to `#undef`/redefine
|
|
`bool`/`true`/`false`.
|
|
|
|
**Verified with the same two-probe approach used for the atomics slice:**
|
|
|
|
- `scripts/fixtures/vc6_stdbool_smoke.c` (new) — a minimal, isolated
|
|
translation unit that includes only `<stdbool.h>` and exercises
|
|
`bool`/`true`/`false`/`__bool_true_false_are_defined`:
|
|
|
|
```text
|
|
$ bash scripts/vc6-probe-core.sh scripts/fixtures/vc6_stdbool_smoke.c
|
|
== MSVC600 Py_BUILD_CORE compile probe ==
|
|
source : scripts/fixtures/vc6_stdbool_smoke.c
|
|
...
|
|
vc6_stdbool_smoke.c
|
|
|
|
== cl.exe exit status: 0 ==
|
|
```
|
|
|
|
No errors, no warnings.
|
|
|
|
- `cpython/Modules/main.c`, the same real core translation unit used to
|
|
verify the atomics slice. It now compiles *past* `pycore_interp.h`
|
|
line 11 (reached the same way as before, transitively via
|
|
`pycore_runtime.h`) — the fatal `stdbool.h`/C1083 error is gone — and
|
|
proceeds through `pycore_atomic.h` and `pycore_ast_state.h` before
|
|
hitting a new, unrelated, and considerably larger blocker; see "Current
|
|
first-blocker evidence" below for the full detail.
|
|
|
|
Both probes are covered by 5 new checks added to the existing
|
|
`scripts/vc6-probe-core.test.sh` (positive isolated-fixture compile,
|
|
no-errors check, a negative deliberately-broken-fixture check, its
|
|
error-surfaced check, and a check that `Modules/main.c`'s output no
|
|
longer contains `Cannot open include file: 'stdbool.h'`), bringing that
|
|
file to 11 checks total. Run: `bash scripts/vc6-probe-core.test.sh`. Last
|
|
run: all 11 checks `ok` (wine + MSVC600 available in this environment).
|
|
`scripts/vc6-probe.test.sh` was re-run after this change and still passes
|
|
(all 6 checks `ok`), confirming the public-header probe is unaffected.
|
|
|
|
## Mixed-declarations header slice (`pycore_code.h`, `pycore_dict.h`, `pycore_list.h`, `pycore_call.h`)
|
|
|
|
**Scope: the four `Include/internal/pycore_*.h` headers that the
|
|
`Modules/main.c` `#include` chain directly reaches with VC6 C89
|
|
mixed-declaration violations, and no others.** This is a bounded slice of
|
|
the "Compiler language gap" blocker recorded below, not an attempt at the
|
|
whole gap: it fixes exactly the sites the real core probe (`Modules/main.c`
|
|
via `scripts/vc6-probe-core.sh`) hits inside CPython's own headers, and
|
|
stops there. `Modules/main.c` itself has the same pattern pervasively
|
|
throughout its own function bodies; that is **explicitly out of scope for
|
|
this slice** and is now the next recorded blocker (see "Current
|
|
first-blocker evidence" below) rather than something papered over here.
|
|
|
|
Six `static inline` functions across the four headers declared a variable
|
|
after a preceding statement (typically an `assert(...)`), which C89 (VC6's
|
|
only supported dialect, give or take Microsoft extensions) does not allow
|
|
inside a block — only C99 does. VC6's parser, on hitting the declaration
|
|
where it expects a statement, fails to recognize it as a declaration at
|
|
all and instead cascades into unrelated-looking `C2065`/`C2109`/`C2106`
|
|
errors for the rest of the block, exactly as described for the first
|
|
instance (`pycore_code.h:154`) in earlier revisions of this document.
|
|
|
|
The now-applied, still-uncommitted
|
|
`compat/msvc600/cpython-3.11.16-vc6-headers.patch` fixes each site the
|
|
same way: hoist the declaration (no initializer) to the top of its
|
|
enclosing block, and turn the original `TYPE name = expr;` into a plain
|
|
assignment (`name = expr;`) at its original position. This changes no
|
|
evaluation order and no semantics — it is the standard, mechanical C89
|
|
transform for this exact pattern — and is applied only to the six
|
|
functions that actually needed it, not the surrounding file:
|
|
|
|
- `Include/internal/pycore_code.h`: `_PyLocals_GetKind()`,
|
|
`_PyLocals_SetKind()` (both hoist a `char *ptr`), and
|
|
`adaptive_counter_backoff()` (hoists `unsigned int value`).
|
|
- `Include/internal/pycore_dict.h`: `_PyDictValues_AddToInsertionOrder()`
|
|
(hoists `uint8_t *size_ptr` and `int size`).
|
|
- `Include/internal/pycore_list.h`: `_PyList_AppendTakeRef()` (hoists
|
|
`Py_ssize_t len` and `Py_ssize_t allocated`).
|
|
- `Include/internal/pycore_call.h`: `_PyVectorcall_FunctionInline()`
|
|
(hoists `PyTypeObject *tp`, `Py_ssize_t offset`, and
|
|
`vectorcallfunc ptr`).
|
|
|
|
Every other compiler is unaffected: these are plain C source edits (no
|
|
`#ifdef`/`_MSC_VER` gating, unlike the atomics slice), and hoisting a
|
|
declaration without an initializer, then assigning immediately after the
|
|
same statements that used to compute the initializer, is behavior-identical
|
|
under C89, C99, C11, and C++ alike — no code path, value, or evaluation
|
|
order changes for any compiler already accepting the original source.
|
|
|
|
**Verified with the same two-probe approach used for the atomics and
|
|
stdbool slices:**
|
|
|
|
- `scripts/fixtures/vc6_mixed_decls_smoke.c` (new) — includes the real
|
|
`Python.h` (these four headers assume public types like `PyObject`,
|
|
`PyCodeObject`, `PyListObject`, `PyDictValues`, and `vectorcallfunc`
|
|
already declared, the same way real CPython `.c` sources reach them, so
|
|
full isolation without `Python.h` is not meaningful here unlike the
|
|
atomics/stdbool fixtures) plus all four headers, and calls all six fixed
|
|
functions:
|
|
|
|
```text
|
|
$ bash scripts/vc6-probe-core.sh scripts/fixtures/vc6_mixed_decls_smoke.c
|
|
== MSVC600 Py_BUILD_CORE compile probe ==
|
|
source : scripts/fixtures/vc6_mixed_decls_smoke.c
|
|
...
|
|
vc6_mixed_decls_smoke.c
|
|
...
|
|
== cl.exe exit status: 0 ==
|
|
```
|
|
|
|
No errors, no warnings.
|
|
|
|
- `cpython/Modules/main.c`, the same real core translation unit used for
|
|
the earlier slices. All four headers now compile clean — no
|
|
`pycore_code.h`/`pycore_dict.h`/`pycore_list.h`/`pycore_call.h` error
|
|
appears anywhere in the probe output — and the compiler proceeds
|
|
entirely into `Modules/main.c`'s own body, where a new, considerably
|
|
larger, and out-of-scope-for-this-slice blocker is hit; see "Current
|
|
first-blocker evidence" below.
|
|
|
|
Both probes are covered by 5 new checks added to
|
|
`scripts/vc6-probe-core.test.sh` (positive isolated-fixture compile,
|
|
no-errors check, a negative deliberately-broken-fixture check, its
|
|
error-surfaced check, and a check that `Modules/main.c`'s output no longer
|
|
contains any `pycore_code.h`/`pycore_dict.h`/`pycore_list.h`/
|
|
`pycore_call.h` error line), bringing that file to 16 checks total. Run:
|
|
`bash scripts/vc6-probe-core.test.sh`. Last run: all 16 checks `ok` (wine +
|
|
MSVC600 available in this environment). `scripts/vc6-probe.test.sh` was
|
|
re-run after this change and still passes (all 6 checks `ok`), confirming
|
|
the public-header probe is unaffected. The patch's reproducibility was
|
|
also re-verified directly: `git apply --check --directory=cpython
|
|
compat/msvc600/cpython-3.11.16-vc6-headers.patch` against a freshly
|
|
stashed (pristine `v3.11.16`) `cpython/` working tree exits 0.
|
|
|
|
## Modules/main.c declaration-order slice
|
|
|
|
**Scope: the C89-vs-C99 mixed-declarations pattern inside
|
|
`Modules/main.c`'s own function bodies only — declaration *position*, not
|
|
any other C99 feature. No other file was touched, and no whole-tree
|
|
mechanical rewrite was attempted.** This is the follow-on to the
|
|
"Mixed-declarations header slice" above: the same pattern (a block-scope
|
|
declaration appearing after a preceding statement, which C89's parser
|
|
cannot recognize as a declaration), but recurring roughly a dozen times
|
|
across `Modules/main.c`'s own ~740 lines rather than in a handful of small
|
|
header functions.
|
|
|
|
The now-applied, still-uncommitted
|
|
`compat/msvc600/cpython-3.11.16-vc6-headers.patch` fixes every site the
|
|
same mechanical way used for the header slice: hoist the bare declaration
|
|
(no initializer) to the top of its enclosing block, and turn the original
|
|
`TYPE name = expr;` into a plain assignment (`name = expr;`) at its
|
|
original position. This changes no evaluation order and no semantics for
|
|
any compiler, VC6 or otherwise. Two variants of this same transform were
|
|
needed, both already implicit in the mechanical rule but worth calling out
|
|
explicitly:
|
|
|
|
- **A declaration whose original initializer is a side-effecting call**
|
|
(e.g. `FILE *fp = _Py_fopen_obj(filename, "rb");` in
|
|
`pymain_run_file_obj()`) is hoisted bare and the call is kept as an
|
|
assignment at its *original* position, specifically so the call still
|
|
happens after the same preceding statements (an audit-hook check, in
|
|
this case) instead of moving earlier and changing behavior.
|
|
- **A platform-conditional declaration with the same name but a different
|
|
type per branch** (`pymain_run_startup()`'s `env`: `const wchar_t *env`
|
|
under `#ifdef MS_WINDOWS`, `const char *env` under `#else`) is hoisted
|
|
under the identical `#ifdef`/`#else`/`#endif` structure at the top of the
|
|
function, so each platform still only ever sees its own declaration.
|
|
|
|
Functions touched (8 total, 20 hoisted declarations, all in
|
|
`cpython/Modules/main.c`): `pymain_init()` (`preconfig`, `config`),
|
|
`pymain_import_readline()` (`mod`), `pymain_run_command()` (`cf`),
|
|
`pymain_run_file_obj()` (`fp`, `sb`, `cf`, `run`), `pymain_run_file()`
|
|
(`filename`, `program_name`, `res`), `pymain_run_startup()` (`startup`,
|
|
`env`, `fp`, `cf`), `pymain_run_stdin()` (a nested-block `exitcode`, plus
|
|
outer-scope `cf`, `run`), and `pymain_repl()` (`cf`, `res`).
|
|
`pymain_get_importer()`,
|
|
`pymain_sys_path_add_path0()`, `pymain_run_module()`,
|
|
`pymain_run_interactive_hook()`, `pymain_run_python()`, `Py_RunMain()`,
|
|
and `pymain_main()` were inspected and found to already declare every
|
|
local before the first statement in their block (including two
|
|
back-to-back declarations in `pymain_run_python()`'s `else if
|
|
(!config->safe_path)` block, which is valid C89 since no statement
|
|
separates them) — left untouched, consistent with "only the sites that
|
|
actually need it."
|
|
|
|
**A note on scope, since it matters for what this slice does and does not
|
|
claim:** several of the hoisted declarations are of type `PyCompilerFlags`
|
|
or are assigned via `_PyStatus_OK()`, both of which are initialized through
|
|
C99 compound-literal macros (`_PyCompilerFlags_INIT`, `_PyStatus_OK()`).
|
|
Hoisting the *declaration* is still the correct, necessary C89 fix for the
|
|
mixed-declaration violation at each of those sites — it eliminates that
|
|
specific error class everywhere it appeared in `main.c`, as confirmed
|
|
below — but the compound-literal *initializer itself* is a separate C99
|
|
feature VC6 does not support at any declaration position. That is a new,
|
|
distinct blocker, not a residue of this slice's fix; see "Current
|
|
first-blocker evidence" below, where it is documented rather than
|
|
papered over.
|
|
|
|
**Verified with the same two-probe approach used for the earlier slices:**
|
|
|
|
- `scripts/fixtures/vc6_decl_order_smoke.c` (new) — a minimal,
|
|
self-contained fixture (no CPython headers) that exercises, in
|
|
isolation, the shape of every hoist pattern used in `main.c`: a bare
|
|
hoist above an early return, a side-effecting-initializer hoist, a
|
|
hoisted pair where fixing the first declaration would otherwise turn an
|
|
immediately-following second declaration into a new violation, a
|
|
nested-block hoist, and the platform-conditional same-name/different-type
|
|
hoist. Kept separate from `main.c` itself so it keeps guarding the
|
|
general transformation shape even as `main.c` changes upstream:
|
|
|
|
```text
|
|
$ bash scripts/vc6-probe-core.sh scripts/fixtures/vc6_decl_order_smoke.c
|
|
== MSVC600 Py_BUILD_CORE compile probe ==
|
|
source : scripts/fixtures/vc6_decl_order_smoke.c
|
|
...
|
|
vc6_decl_order_smoke.c
|
|
|
|
== cl.exe exit status: 0 ==
|
|
```
|
|
|
|
No errors, no warnings.
|
|
|
|
- `cpython/Modules/main.c` itself, the same real core translation unit
|
|
used throughout this document. Every C2146/C2065/C2275
|
|
declaration-position error is gone; the previous 29-error-line run is
|
|
now down to 8 error lines, all a different error code (`C2059`) at a
|
|
different root cause (compound literals / designated initializers, not
|
|
declaration position — see "Current first-blocker evidence" below for
|
|
the full transcript).
|
|
|
|
Both probes are covered by 5 new checks added to
|
|
`scripts/vc6-probe-core.test.sh` (positive isolated-fixture compile,
|
|
no-errors check, a negative deliberately-broken-fixture check, its
|
|
error-surfaced check, and a check that `Modules/main.c`'s output no longer
|
|
contains any declaration-position error — `C2146`, an "undeclared
|
|
identifier" `C2065`, or a type-as-expression `C2275` — anywhere in its own
|
|
body), bringing that file to 21 checks total. Run: `bash
|
|
scripts/vc6-probe-core.test.sh`. Last run: all 21 checks `ok` (wine +
|
|
MSVC600 available in this environment). `scripts/vc6-probe.test.sh` was
|
|
re-run after this change and still passes (all 6 checks `ok`), confirming
|
|
the public-header probe is unaffected. The patch's reproducibility was
|
|
re-verified against a genuinely pristine tree (a fresh `cp -a cpython
|
|
/tmp/cpython_pristine_check && git -C /tmp/cpython_pristine_check checkout
|
|
-- .`, not a stash of the working checkout): `git apply --check` from
|
|
inside that pristine copy exits 0, and after applying, `diff -rq` between
|
|
the patched pristine copy and this project's actual `cpython/` working
|
|
tree (excluding `.git`) reports no differences. The temporary copy was
|
|
removed afterward; nothing under `cpython/` or `compat/` was affected by
|
|
this verification step.
|
|
|
|
## Compound-literal / designated-initializer slice
|
|
|
|
**Scope: the 8 C99 compound-literal/designated-initializer call sites
|
|
inside `Modules/main.c`'s own body only — no header-level macro change,
|
|
and no other file touched.** This is the follow-on to the "Modules/main.c
|
|
declaration-order slice" above, addressing exactly the blocker that slice
|
|
left recorded as out of scope: VC6 (a pre-C99 compiler) supports neither
|
|
C99 compound literals (`(PyStatus){...}`) nor C99 designated initializers
|
|
(`{ .field = value, ... }`), at any position in a block, regardless of
|
|
where the destination variable is declared.
|
|
|
|
Three distinct patterns were responsible, each fixed a different way, all
|
|
still uncommitted in `compat/msvc600/cpython-3.11.16-vc6-headers.patch`:
|
|
|
|
- **`status = _PyStatus_OK();`** (1 site, `pymain_init()`). Rather than
|
|
reimplementing the macro's field assignment by hand, this is replaced
|
|
with **`status = PyStatus_Ok();`** — the existing public API function
|
|
declared in `Include/cpython/initconfig.h` (`PyAPI_FUNC(PyStatus)
|
|
PyStatus_Ok(void);`) and defined in `Python/initconfig.c` as `PyStatus
|
|
PyStatus_Ok(void) { return _PyStatus_OK(); }` — i.e. it returns the
|
|
exact value the macro would have produced, by construction, not by
|
|
re-derivation. This needed no header change: the function was already
|
|
declared and part of the public ABI; `main.c` just wasn't calling it.
|
|
Behavior is identical for every compiler, VC6 or otherwise — `main.c`
|
|
now calls a function that itself still uses the compound-literal macro
|
|
internally in `Python/initconfig.c`, which is fine because
|
|
`Python/initconfig.c` is not part of this slice's compile-only probe (no
|
|
link is attempted or claimed) and is explicitly the next kind of file
|
|
this port will need to work through, not something silently skipped.
|
|
- **`cf = _PyCompilerFlags_INIT;`** (5 sites: `pymain_run_command()`,
|
|
`pymain_run_file_obj()`, `pymain_run_startup()`, `pymain_run_stdin()`,
|
|
`pymain_repl()`). No public-API equivalent exists for this one (unlike
|
|
`PyStatus_Ok()`), so each site is replaced with the field-by-field
|
|
assignment the macro itself expands to: `cf.cf_flags = 0;
|
|
cf.cf_feature_version = PY_MINOR_VERSION;`. `PyCompilerFlags` is a
|
|
two-field plain struct (`Include/cpython/compile.h`), and `cf` was
|
|
already declared (bare, no initializer) at the top of its enclosing
|
|
block by the prior declaration-order slice, so this is a pure
|
|
compound-literal-to-assignment substitution with no interaction with
|
|
declaration position.
|
|
- **`_PyArgv args = { .argc = argc, ... };`** (2 sites: `Py_Main()`,
|
|
`Py_BytesMain()`). No macro is involved here, just a plain C99
|
|
designated-initializer aggregate initializer on a local variable
|
|
declaration. Replaced with a bare declaration followed by one assignment
|
|
per field (`args.argc = argc; args.use_bytes_argv = 0; ...`); field
|
|
order in the replacement does not matter, unlike the initializer list it
|
|
replaces, since each is now an independent assignment statement.
|
|
|
|
Every other compiler is unaffected: `PyStatus_Ok()` is a normal function
|
|
call (no macro expansion difference for any compiler), and hoisting a
|
|
designated-initializer aggregate into field-by-field assignment, or a
|
|
compound-literal assignment into field-by-field assignment, changes no
|
|
value, no evaluation order, and no code path for any compiler that already
|
|
accepted the C99 form — it is strictly a syntax substitution for an
|
|
identical runtime effect.
|
|
|
|
**Verified with the same two-probe approach used for the earlier slices:**
|
|
|
|
- `scripts/fixtures/vc6_compound_literal_smoke.c` (new) — includes the
|
|
real `Python.h`/`pycore_initconfig.h` (like
|
|
`vc6_mixed_decls_smoke.c`, since `PyStatus`, `PyCompilerFlags`, and
|
|
`_PyArgv` are real CPython types, not reproducible in isolation the way
|
|
the atomics/stdbool fixtures are) and exercises all three replacement
|
|
patterns:
|
|
|
|
```text
|
|
$ bash scripts/vc6-probe-core.sh scripts/fixtures/vc6_compound_literal_smoke.c
|
|
== MSVC600 Py_BUILD_CORE compile probe ==
|
|
source : scripts/fixtures/vc6_compound_literal_smoke.c
|
|
...
|
|
vc6_compound_literal_smoke.c
|
|
|
|
== cl.exe exit status: 0 ==
|
|
```
|
|
|
|
No errors, no warnings.
|
|
|
|
- `cpython/Modules/main.c` itself, the same real core translation unit
|
|
used throughout this document. Every `C2059` compound-literal/
|
|
designated-initializer error is gone — the previous 8-error-line run is
|
|
now **0 error lines**, and the probe reports `cl.exe exit status: 0`:
|
|
|
|
```text
|
|
$ bash scripts/vc6-probe-core.sh
|
|
== MSVC600 Py_BUILD_CORE compile probe ==
|
|
source : cpython/Modules/main.c
|
|
prefix : /home/ubuntu/.wine-win9xport
|
|
|
|
Setting environment for using Microsoft Visual C++ tools.
|
|
main.c
|
|
...
|
|
== cl.exe exit status: 0 ==
|
|
```
|
|
|
|
(The only other output is MSVC600's standard informational `NOTE:` about
|
|
`WINVER` 0x0500 being a beta-era SDK combination, present on every probe
|
|
run in this environment, not new to this slice and not an error or
|
|
warning.) **This is a compile-only (`/c`) result for one translation
|
|
unit** — `scripts/vc6-probe-core.sh` never invokes the linker (see its
|
|
own header comment) — **not a claim that `Modules/main.c` links, that
|
|
`pythoncore`/`python.exe` builds, or that anything runs on Windows 9x.**
|
|
|
|
Both probes are covered by 6 new checks added to
|
|
`scripts/vc6-probe-core.test.sh` (positive isolated-fixture compile,
|
|
no-errors check, a negative deliberately-broken-fixture check, its
|
|
error-surfaced check, a check that `Modules/main.c`'s output no longer
|
|
contains any `C2059` compound-literal/designated-initializer error
|
|
anywhere in its own body, and a new end-to-end check that the
|
|
`Modules/main.c` probe itself now exits 0), bringing that file to 27
|
|
checks total. Run: `bash scripts/vc6-probe-core.test.sh`. Last run: all 27
|
|
checks `ok` (wine + MSVC600 available in this environment).
|
|
`scripts/vc6-probe.test.sh` was re-run after this change and still passes
|
|
(all 6 checks `ok`), confirming the public-header probe is unaffected. The
|
|
patch's reproducibility was re-verified against a genuinely pristine tree
|
|
(a fresh `cp -a cpython /tmp/cpython_pristine_check && git -C
|
|
/tmp/cpython_pristine_check checkout -- .`, not a stash of the working
|
|
checkout): `git apply --check` from inside that pristine copy exits 0, and
|
|
after applying, `diff -rq` between the patched pristine copy and this
|
|
project's actual `cpython/` working tree (excluding `.git`) reports no
|
|
differences. The temporary copy was removed afterward; nothing under
|
|
`cpython/` or `compat/` was affected by this verification step.
|
|
|
|
## Next blocker: `Python/pylifecycle.c`
|
|
|
|
After the PyRuntime section slice, the same real core probe was re-run:
|
|
|
|
```text
|
|
$ bash scripts/vc6-probe-core.sh cpython/Python/pylifecycle.c
|
|
== MSVC600 Py_BUILD_CORE compile probe ==
|
|
source : cpython/Python/pylifecycle.c
|
|
...
|
|
cpython\Python\pylifecycle.c(112) : error C2059: syntax error : '.'
|
|
cpython\Python\pylifecycle.c(112) : error C2059: syntax error : ','
|
|
cpython\Python\pylifecycle.c(130) : error C2059: syntax error : '{'
|
|
cpython\Python\pylifecycle.c(189) : error C2143: syntax error : missing ';' before 'type'
|
|
cpython\Python\pylifecycle.c(190) : error C2143: syntax error : missing ';' before 'type'
|
|
...
|
|
cpython\Python\pylifecycle.c(1232) : fatal error C1003: error count exceeds 100; stopping compilation
|
|
|
|
== cl.exe exit status: 2 ==
|
|
```
|
|
|
|
The prior first blocker at `Python/pylifecycle.c:91-92` is gone: VC6 no
|
|
longer reports `warning C4068: unknown pragma` for `#pragma section`, and no
|
|
longer rejects `__declspec(allocate("PyRuntime"))` with `C2341`. The new exact
|
|
first blocker is now the `_PyRuntimeState_INIT` designated-initializer macro
|
|
used by `_PyRuntime` itself (`C2059: syntax error : '.'` at the definition
|
|
line, now line 112 after the VC6 guard was added). After that, the file still
|
|
reaches the previously known broader C99 compound-literal/designated-initializer
|
|
and C89 mixed-declaration wall, eventually hitting the compiler's 100-error
|
|
cap. This slice did not attempt those broader `pylifecycle.c` rewrites.
|
|
|
|
## Current first-blocker evidence
|
|
|
|
**The previously recorded `long long` / C99 integer-suffix blocker is now
|
|
resolved and verified.** The uncommitted
|
|
`compat/msvc600/cpython-3.11.16-vc6-headers.patch` (already applied to the
|
|
`cpython/` working tree) replaces bare `long long` with the existing
|
|
`PY_LONG_LONG` macro in `Include/longobject.h`, `Include/pythread.h`, and
|
|
`Include/pyexpat.h`, and introduces a `PY_LL`/`PY_ULL` literal-suffix macro
|
|
pair (spelled `i64`/`ui64` for `_MSC_VER < 1300`, i.e. VC6) in `PC/pyconfig.h`
|
|
and `Include/pyport.h`, used in place of raw `123LL`/`123ULL` literals in
|
|
`Include/pythread.h`. With this patch applied, the full public header stack
|
|
compiles cleanly:
|
|
|
|
```text
|
|
$ bash scripts/vc6-probe.sh
|
|
== MSVC600 compile probe ==
|
|
source : cpython/Programs/python.c
|
|
prefix : /home/ubuntu/.wine-win9xport
|
|
|
|
Setting environment for using Microsoft Visual C++ tools.
|
|
python.c
|
|
|
|
== cl.exe exit status: 0 ==
|
|
```
|
|
|
|
`cpython/Programs/python.c` (`#include "Python.h"` plus a `wmain`/`main`
|
|
stub) now compiles to `build/vc6-probe/python.obj` (verified present,
|
|
7115 bytes) with exit status 0. This exercises the entire public
|
|
`Include/*.h` surface reachable from `Python.h`, not just `pyport.h`.
|
|
|
|
**The `pycore_atomic.h`/`intrin.h` blocker, the `pycore_interp.h`/
|
|
`<stdbool.h>` blocker, and the mixed-declarations blocker in
|
|
`pycore_code.h`/`pycore_dict.h`/`pycore_list.h`/`pycore_call.h` described
|
|
in earlier revisions of this document are all now resolved; see "Atomics
|
|
slice", "stdbool slice", and "Mixed-declarations header slice" above for
|
|
full detail.** Probing the same real core translation unit
|
|
(`cpython/Modules/main.c`, which needs `Include/internal` on the include
|
|
path and `Py_BUILD_CORE` defined — both required unconditionally by every
|
|
`pycore_*.h` header) now gets past all of CPython's internal headers
|
|
reachable from its `#include` chain with no header-level error at all, and
|
|
the compiler proceeds into `Modules/main.c`'s own body — where it hits a
|
|
new wall, spread across nearly the entire 743-line file:
|
|
|
|
```text
|
|
$ bash scripts/vc6-probe-core.sh
|
|
== MSVC600 Py_BUILD_CORE compile probe ==
|
|
source : cpython/Modules/main.c
|
|
...
|
|
cpython\Modules\main.c(44) : error C2275: 'PyPreConfig' : illegal use of this type as an expression
|
|
cpython\Modules\main.c(44) : error C2146: syntax error : missing ';' before identifier 'preconfig'
|
|
cpython\Modules\main.c(44) : error C2065: 'preconfig' : undeclared identifier
|
|
...
|
|
cpython\Modules\main.c(52) : error C2275: 'PyConfig' : illegal use of this type as an expression
|
|
...
|
|
cpython\Modules\main.c(215) : error C2275: 'PyObject' : illegal use of this type as an expression
|
|
...
|
|
cpython\Modules\main.c(253) : error C2275: 'PyCompilerFlags' : illegal use of this type as an expression
|
|
...
|
|
== cl.exe exit status: 2 ==
|
|
```
|
|
|
|
(29 distinct error source lines total in that run, spanning lines 44
|
|
through 734 of `main.c` — effectively the whole file — across roughly a
|
|
dozen separate functions/blocks.)
|
|
|
|
**This mixed-declarations pattern inside `Modules/main.c`'s own body is now
|
|
resolved and verified; see "Modules/main.c declaration-order slice" above
|
|
for full detail.** With that fix applied, the same probe now gets past
|
|
every declaration-position error and hits a new, different, and
|
|
out-of-scope-for-that-slice blocker:
|
|
|
|
```text
|
|
$ bash scripts/vc6-probe-core.sh
|
|
== MSVC600 Py_BUILD_CORE compile probe ==
|
|
source : cpython/Modules/main.c
|
|
...
|
|
cpython\Modules\main.c(71) : error C2059: syntax error : '{'
|
|
cpython\Modules\main.c(256) : error C2059: syntax error : '{'
|
|
cpython\Modules\main.c(366) : error C2059: syntax error : '{'
|
|
cpython\Modules\main.c(448) : error C2059: syntax error : '{'
|
|
cpython\Modules\main.c(531) : error C2059: syntax error : '{'
|
|
cpython\Modules\main.c(564) : error C2059: syntax error : '{'
|
|
cpython\Modules\main.c(748) : error C2059: syntax error : '.'
|
|
cpython\Modules\main.c(760) : error C2059: syntax error : '.'
|
|
|
|
== cl.exe exit status: 2 ==
|
|
```
|
|
|
|
Down from 29 error lines to 8. Every one of these is the *same* new root
|
|
cause, unrelated to declaration position: a **C99 compound literal**,
|
|
optionally with **designated initializers**, neither of which VC6 (a
|
|
pre-C99 compiler) supports at all, at any position in a block. Two
|
|
distinct macros/patterns are responsible:
|
|
|
|
- `main.c:71`, `256`, `366`, `448`, `531`, `564` — six sites assigning
|
|
`PyCompilerFlags cf = _PyCompilerFlags_INIT;` (now `cf =
|
|
_PyCompilerFlags_INIT;` after the declaration-order fix) or `status =
|
|
_PyStatus_OK();`. `_PyCompilerFlags_INIT` and `_PyStatus_OK()` are
|
|
defined in `Include/cpython/compile.h` and
|
|
`Include/internal/pycore_initconfig.h` respectively as C99 compound
|
|
literals with designated initializers, e.g.:
|
|
```c
|
|
#define _PyStatus_OK() \
|
|
(PyStatus){._type = _PyStatus_TYPE_OK,}
|
|
#define _PyCompilerFlags_INIT \
|
|
(PyCompilerFlags){.cf_flags = 0, .cf_feature_version = PY_MINOR_VERSION}
|
|
```
|
|
`(PyStatus){...}` is a compound literal (C99 §6.5.2.5); `._type = ...`
|
|
inside it is a designated initializer (C99 §6.7.8). VC6 recognizes
|
|
neither construct and reports the same `C2059: syntax error : '{'` for
|
|
every one, regardless of whether the assignment is the first statement
|
|
in its block or not — hoisting the declaration (already done) cannot fix
|
|
this, because the problem is the initializer expression's syntax, not
|
|
where the variable is declared.
|
|
- `main.c:748`, `760` — the two `_PyArgv args = { .argc = argc, ... };`
|
|
aggregate initializations in `Py_Main()`/`Py_BytesMain()`, which use
|
|
plain C99 designated-initializer syntax (no compound-literal macro
|
|
involved, just `{ .field = value, ... }` directly in a local variable's
|
|
initializer) for the same reason: VC6 predates C99 designated
|
|
initializers entirely.
|
|
|
|
This is a **distinct C99 language-feature gap from mixed declarations**,
|
|
narrower in one sense (it doesn't force auditing every block in the file
|
|
the way declaration-position did) but not fixable by moving code around:
|
|
the fix, when attempted, will need to replace each compound-literal
|
|
initializer with explicit field-by-field assignment statements (e.g. `cf.cf_flags
|
|
= 0; cf.cf_feature_version = PY_MINOR_VERSION;`) or an equivalent
|
|
non-compound-literal C89 construct, and — for `_PyStatus_OK()` /
|
|
`_PyCompilerFlags_INIT` specifically — probably wants a header-level macro
|
|
change (in `pycore_initconfig.h` / `cpython/compile.h`) analogous to the
|
|
`_Py_ATOMIC_VC6_X86` gating already used for `pycore_atomic.h`, rather than
|
|
a per-call-site rewrite in every `.c` file that uses these macros, since
|
|
they are used far more broadly than just `Modules/main.c`. **No such fix is
|
|
attempted here; it is out of scope for this narrowly-scoped
|
|
declaration-order pass and is recorded as the next blocker.** No link or
|
|
runtime success is claimed at this or any earlier point in this document.
|
|
|
|
**Update: this blocker (all 8 `C2059` sites above) is now resolved for
|
|
`Modules/main.c`, via a per-call-site rewrite rather than the header-level
|
|
macro change speculated above** — see "Compound-literal /
|
|
designated-initializer slice" above for what was actually done and why
|
|
the per-call-site approach (not the header macro change) was chosen for
|
|
this narrow slice, and "Next blocker: `Python/pylifecycle.c`" above for
|
|
where the same two blocker classes (mixed declarations and compound
|
|
literals/designated initializers) recur next. This "Current first-blocker
|
|
evidence" section is kept as a historical record of how the diagnosis
|
|
proceeded; it is no longer the current state.
|
|
|
|
## Larger blockers to plan for
|
|
|
|
1. **Build-system mismatch:** CPython 3.11's Windows build expects Visual Studio
|
|
2017-era MSBuild/vcxproj tooling. VC6 cannot consume the build as-is. A
|
|
dedicated minimal VC6 makefile/project set is required.
|
|
2. **Compiler language gap:** CPython 3.11 source and headers use C99-era and
|
|
later MSVC features that VC6 does not implement. Compatibility macros and
|
|
carefully scoped source backports are required.
|
|
- VC6's C89-only parser rejects CPython's pervasive C99-style mixed
|
|
declarations-and-code. The instances of this pattern in four
|
|
`Include/internal/pycore_*.h` headers reachable from `Modules/main.c`
|
|
are fixed (see "Mixed-declarations header slice" above), and the
|
|
same pattern recurring throughout `Modules/main.c`'s own body (29
|
|
error sites across roughly a dozen functions) is also now fixed (see
|
|
"Modules/main.c declaration-order slice" above). Whether this pattern
|
|
recurs in CPython's other `.c` sources has not yet been surveyed;
|
|
each file would need its own probe-and-fix pass the same way
|
|
`main.c` did, not a whole-tree sweep in one change.
|
|
- **VC6 does not support C99 compound literals or designated
|
|
initializers at all**, independent of declaration position. The 8
|
|
sites of this pattern in `Modules/main.c` itself (`_PyStatus_OK()`,
|
|
`_PyCompilerFlags_INIT`, and inline `{ .field = value }`
|
|
initialization in `Py_Main()`/`Py_BytesMain()`) are now fixed via a
|
|
per-call-site rewrite, not a header-level macro change (see
|
|
"Compound-literal / designated-initializer slice" above for why: a
|
|
public-API function already existed for `_PyStatus_OK()`, and the
|
|
other two patterns are cheap to rewrite in place without touching
|
|
shared headers used far more broadly than `main.c`). `_PyStatus_OK()`
|
|
and `_PyCompilerFlags_INIT` are still used, unmodified, at many other
|
|
call sites across CPython outside `main.c` — this fix is scoped to
|
|
`main.c` only, as directed; a header-level VC6 branch (mirroring
|
|
`_Py_ATOMIC_VC6_X86`) remains a live option if a future slice finds
|
|
the per-call-site approach doesn't scale to the rest of the codebase.
|
|
The same two patterns (mixed declarations and compound literals) both
|
|
recur in `Python/pylifecycle.c`, the next core translation unit in
|
|
`main.c`'s own call chain; see "Next blocker: `Python/pylifecycle.c`"
|
|
above. Not yet fixed there.
|
|
3. **Win9x API gap:** CPython 3.11's Windows layer assumes NT-family Unicode,
|
|
synchronization, process, filesystem, and networking APIs. Windows 98 has
|
|
no wide-character file APIs and lacks multiple newer kernel APIs.
|
|
4. **Runtime/library gap:** SSL, SQLite, ctypes, multiprocessing, asyncio,
|
|
Unicode filesystem behavior, and parts of subprocess will need explicit
|
|
feature gates or Win9x-specific implementations.
|
|
5. **Validation gap:** final success requires booting the built executable on
|
|
Windows 98 SE; Wine compilation alone is not proof of Win9x runtime
|
|
compatibility.
|
|
|
|
## Recommended next implementation slice
|
|
|
|
Do not attempt the complete CPython solution yet. The public header surface
|
|
compiles; `Include/internal/pycore_atomic.h` compiles for VC6/`_M_IX86`
|
|
(see "Atomics slice" above); `Include/internal/pycore_interp.h`'s
|
|
`<stdbool.h>` include now resolves via the project-local
|
|
`compat/msvc600/stdbool.h` shim (see "stdbool slice" above); the C99
|
|
mixed-declarations pattern in the four `pycore_*.h` headers reachable from
|
|
`Modules/main.c` is fixed (see "Mixed-declarations header slice" above);
|
|
that same pattern inside `Modules/main.c`'s own body is fixed (see
|
|
"Modules/main.c declaration-order slice" above); and the C99
|
|
compound-literal/designated-initializer pattern inside `Modules/main.c`'s
|
|
own body is now also fixed (see "Compound-literal /
|
|
designated-initializer slice" above). **`cpython/Modules/main.c` now
|
|
compiles with `cl.exe` exit status 0** under `scripts/vc6-probe-core.sh`
|
|
— no errors, no warnings — a compile-only (`/c`) result for this one
|
|
translation unit; no link or runtime claim is made.
|
|
|
|
The next blocker is still `Python/pylifecycle.c`, the next core translation
|
|
unit in `main.c`'s own call chain (via `Py_InitializeFromConfig()`), but it
|
|
has advanced past the VC6-incompatible PyRuntime section pragma. See "Next
|
|
blocker: `Python/pylifecycle.c`" above for the current probe transcript. It
|
|
now hits the same two already-solved blocker classes recurring at greater
|
|
scale (mixed declarations, compound literals/designated initializers), plus
|
|
enough sites of both to hit the compiler's 100-error cap before the whole
|
|
file is seen, so the true scope is not yet known. Recommended next steps:
|
|
|
|
1. Continue with `Python/pylifecycle.c` by applying the same two mechanical
|
|
transforms already validated on `Modules/main.c` — hoist mid-block
|
|
declarations (declaration-order slice) and replace compound-literal/
|
|
designated-initializer assignments with field-by-field assignment or,
|
|
where a public API function already exists (as `PyStatus_Ok()` did for
|
|
`_PyStatus_OK()`), a function call (compound-literal slice) — fixing
|
|
what the compiler surfaces in batches of roughly 100 (its per-run error
|
|
cap) and re-probing between batches, since the full error count for
|
|
this file is not yet known.
|
|
2. Re-evaluate the header-level-macro-vs-per-call-site tradeoff noted in
|
|
"Larger blockers to plan for" above once `_PyStatus_OK()`/
|
|
`_PyCompilerFlags_INIT`-style macros have been rewritten by hand in two
|
|
files (`main.c` and `pylifecycle.c`): if the same macros keep recurring
|
|
across many more `.c` files beyond these two, a header-level VC6 branch
|
|
(mirroring `_Py_ATOMIC_VC6_X86` in "Atomics slice" above) becomes more
|
|
attractive than continuing per-call-site rewrites file by file.
|
|
3. Only after enough core translation units compile, and a minimal
|
|
`pythoncore`/`Programs/python.c` link is attempted, should work move to
|
|
the Win9x API surface one subsystem at a time.
|
|
|
|
Validate incrementally with `scripts/vc6-probe-core.sh` against each file
|
|
being worked on, the same way this and prior slices did.
|
|
|
|
## Scope boundary
|
|
|
|
This project does not vendor the 223 MB MSVC600 bundle into a distributable
|
|
repository. It records the external tool as a local build prerequisite. No
|
|
Microsoft compiler binaries or runtime files should be committed or published.
|