/* 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; }