Skip to content

Commit 75ecedf

Browse files
committed
fix 3 bugs, dead const_td_is_array code, braceless switch defer wipe, skip_one_stmt label swallowing
1 parent 0ea1478 commit 75ecedf

File tree

3 files changed

+97
-38
lines changed

3 files changed

+97
-38
lines changed

.github/test.defer.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4892,6 +4892,37 @@ static void test_defer_shadow_stmt_expr_local_false_positive(void) {
48924892
}
48934893
}
48944894

4895+
// BUG90: braceless inner switch caused find_switch_scope to leak to outer switch,
4896+
// resetting defer_count and silently wiping registered defers.
4897+
static void test_braceless_switch_defer_wipe(void) {
4898+
printf("\n--- Braceless Switch Defer Wipe (BUG90) ---\n");
4899+
4900+
// Defer in outer switch must survive a braceless inner switch
4901+
{
4902+
PrismResult r = prism_transpile_source(
4903+
"int result = 0;\n"
4904+
"void test(int outer, int inner) {\n"
4905+
" switch (outer) {\n"
4906+
" case 0: {\n"
4907+
" defer { result = 42; }\n"
4908+
" switch (inner)\n"
4909+
" if (inner == 1) {\n"
4910+
" case 1:\n"
4911+
" break;\n"
4912+
" }\n"
4913+
" }\n"
4914+
" }\n"
4915+
"}\n",
4916+
"braceless_sw_defer.c", prism_defaults());
4917+
CHECK_EQ(r.status, PRISM_OK, "braceless-sw-defer: transpiles OK");
4918+
CHECK(r.output != NULL, "braceless-sw-defer: output not NULL");
4919+
// The defer body must appear in the output (not be wiped)
4920+
CHECK(strstr(r.output, "result = 42") != NULL,
4921+
"braceless-sw-defer: defer must not be wiped by braceless inner switch");
4922+
prism_free(&r);
4923+
}
4924+
}
4925+
48954926
void run_defer_tests(void) {
48964927
printf("\n=== DEFER TESTS ===\n");
48974928
test_defer_in_comma_expr_bug();

.github/test.zeroinit.c

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2842,6 +2842,60 @@ static void test_switch_for_init_not_rejected(void) {
28422842
}
28432843
}
28442844

2845+
// BUG91: skip_one_stmt didn't handle user labels (L: stmt), causing it to
2846+
// over-consume tokens past the labeled statement, extending variable scopes
2847+
// and triggering false CFG verifier rejections.
2848+
static void test_skip_one_stmt_label_parsing(void) {
2849+
printf("\n--- skip_one_stmt Label Parsing (BUG91) ---\n");
2850+
2851+
// Label before braceless if body in for loop
2852+
{
2853+
PrismResult r = prism_transpile_source(
2854+
"void test(void) {\n"
2855+
" goto SAFE_TARGET;\n"
2856+
" for (int secret = 0; secret < 1; secret++)\n"
2857+
" L: if (1) { }\n"
2858+
" SAFE_TARGET: ;\n"
2859+
" int safe_var = 1;\n"
2860+
" (void)safe_var;\n"
2861+
"}\n",
2862+
"label_skip.c", prism_defaults());
2863+
CHECK_EQ(r.status, PRISM_OK,
2864+
"label-skip: goto past labeled braceless for body must not be rejected");
2865+
prism_free(&r);
2866+
}
2867+
2868+
// Multiple chained labels
2869+
{
2870+
PrismResult r = prism_transpile_source(
2871+
"void test(void) {\n"
2872+
" goto END;\n"
2873+
" for (int x = 0; x < 1; x++)\n"
2874+
" A: B: C: if (1) { }\n"
2875+
" END: ;\n"
2876+
"}\n",
2877+
"chain_label_skip.c", prism_defaults());
2878+
CHECK_EQ(r.status, PRISM_OK,
2879+
"chain-label-skip: chained labels before braceless body must work");
2880+
prism_free(&r);
2881+
}
2882+
2883+
// Label before braced block in while loop
2884+
{
2885+
PrismResult r = prism_transpile_source(
2886+
"void test(void) {\n"
2887+
" goto DONE;\n"
2888+
" while (0)\n"
2889+
" BODY: { }\n"
2890+
" DONE: ;\n"
2891+
"}\n",
2892+
"label_while_skip.c", prism_defaults());
2893+
CHECK_EQ(r.status, PRISM_OK,
2894+
"label-while-skip: label before braced while body must work");
2895+
prism_free(&r);
2896+
}
2897+
}
2898+
28452899
// BUG89: emit_type_range stmt-expr bypass in struct bodies
28462900
static void test_struct_body_stmt_expr_features(void) {
28472901
printf("\n--- Struct Body Statement Expression Features (BUG89) ---\n");
@@ -3058,4 +3112,7 @@ void run_zeroinit_tests(void) {
30583112
#ifdef __GNUC__
30593113
test_struct_body_stmt_expr_features();
30603114
#endif
3115+
3116+
// BUG91: skip_one_stmt label parsing
3117+
test_skip_one_stmt_label_parsing();
30613118
}

prism.c

Lines changed: 9 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,12 +1097,6 @@ static void defer_add(Token *defer_keyword, Token *start, Token *end) {
10971097
defer_stack[defer_count++] = (DeferEntry){start, end, defer_keyword};
10981098
}
10991099

1100-
static int find_switch_scope(void) {
1101-
for (int d = ctx->scope_depth - 1; d >= 0; d--)
1102-
if (scope_stack[d].kind == SCOPE_BLOCK && scope_stack[d].is_switch) return d;
1103-
return -1;
1104-
}
1105-
11061100

11071101
static bool needs_space(Token *prev, Token *tok) {
11081102
if (!prev || tok_at_bol(tok)) return false;
@@ -3549,10 +3543,8 @@ static Token *handle_const_orelse_fallback(Token *tok,
35493543
// The old &*(T)0 trick dereferences void* which is a constraint violation
35503544
// under C99 §6.5.3.2p2 (GCC/Clang tolerate it, but it's non-conforming).
35513545
bool const_td_is_ptr = false;
3552-
bool const_td_is_array = false;
35533546
for (Token *t = type_start; t != type->end; t = tok_next(t)) {
35543547
if (is_ptr_typedef(t)) { const_td_is_ptr = true; break; }
3555-
if (is_array_typedef(t)) { const_td_is_array = true; break; }
35563548
}
35573549

35583550
if (pragma_start != type_start)
@@ -3563,12 +3555,6 @@ static Token *handle_const_orelse_fallback(Token *tok,
35633555
out_char(' '); emit_typeof_keyword(); OUT_LIT("((");
35643556
emit_type_range(type_start, type->end, strip_type_const, true);
35653557
OUT_LIT(")0)");
3566-
} else if (const_td_is_array) {
3567-
out_char(' '); emit_typeof_keyword(); OUT_LIT("(*(0 ? (");
3568-
emit_type_range(type_start, type->end, strip_type_const, true);
3569-
OUT_LIT("*)0 : (");
3570-
emit_type_range(type_start, type->end, strip_type_const, true);
3571-
OUT_LIT("*)0))");
35723558
} else {
35733559
out_char(' '); emit_typeof_keyword(); OUT_LIT("((");
35743560
emit_type_range(type_start, type->end, strip_type_const, true);
@@ -5090,30 +5076,7 @@ static Token *handle_goto_keyword(Token *tok) {
50905076
}
50915077

50925078
static void handle_case_default(Token *tok) {
5093-
if (!FEAT(F_DEFER)) return;
5094-
// Braceless switch: ctrl_state.pending is still active, meaning no
5095-
// SCOPE_BLOCK was pushed for this switch. find_switch_scope() would
5096-
// leak through to an enclosing braced switch and corrupt its defer
5097-
// stack. Braceless bodies can't contain defers, so just bail out.
5098-
if (ctrl_state.pending && ctrl_state.parens_just_closed) return;
5099-
int sd = find_switch_scope();
5100-
if (sd < 0) return;
5101-
bool is_case = tok->tag & TT_CASE;
5102-
bool is_default = (tok->tag & TT_DEFAULT) && !in_generic();
5103-
if (is_default) {
5104-
Token *t = skip_noise(tok_next(tok));
5105-
if (!t || !match_ch(t, ':')) return;
5106-
}
5107-
if (!is_case && !is_default) return;
5108-
5109-
// Defer-fallthrough and zero-init-bypass errors are now caught by
5110-
// p1_verify_cfg (Phase 2A) before any code is emitted. Pass 2 only
5111-
// needs to reset the defer stack to the switch scope level so that
5112-
// defers emitted in previous case arms don't bleed into the next one.
5113-
for (int d = ctx->scope_depth - 1; d >= sd; d--) {
5114-
if (scope_stack[d].kind != SCOPE_BLOCK) continue;
5115-
defer_count = scope_stack[d].defer_start_idx;
5116-
}
5079+
(void)tok; // defer-fallthrough is caught by p1_verify_cfg (Phase 2A)
51175080
}
51185081

51195082
static Token *handle_sue_body(Token *tok) {
@@ -6911,6 +6874,14 @@ static Token *skip_one_stmt_impl(Token *tok, uint32_t *cache) {
69116874
goto unwind_if;
69126875
}
69136876

6877+
// User-defined label: ident ':' (not '::')
6878+
if (is_identifier_like(tok) && !(tok->tag & (TT_CASE | TT_DEFAULT | TT_TYPE | TT_QUALIFIER | TT_STORAGE))) {
6879+
Token *colon = skip_noise(tok_next(tok));
6880+
if (colon && match_ch(colon, ':') && !(tok_next(colon) && match_ch(tok_next(colon), ':'))) {
6881+
tok = tok_next(colon); goto restart;
6882+
}
6883+
}
6884+
69146885
// 'case' / 'default' label: skip expr + ':', tail-call on body
69156886
if ((tok->tag & (TT_CASE | TT_DEFAULT)) && !is_known_typedef(tok)) {
69166887
int td = 0;

0 commit comments

Comments
 (0)