Skip to content

Commit fc098bb

Browse files
committed
fix 6 bugs, orelse block defer bypass, braceless ctrl-flow in defer/orelse, paren comma corruption, MSVC test compat
1 parent 2b422a8 commit fc098bb

File tree

3 files changed

+675
-11
lines changed

3 files changed

+675
-11
lines changed

.github/test.defer.c

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4698,6 +4698,108 @@ static void test_emit_range_no_prep_stmtexpr_defer(void) {
46984698
prism_free(&r);
46994699
}
47004700

4701+
// BUG81: emit_deferred_range did not track control-flow keywords,
4702+
// so at_stmt_start was false for braceless if/else/for/while/do bodies
4703+
// inside defer blocks. orelse was emitted verbatim; zero-init was skipped.
4704+
static void test_defer_braceless_ctrl_orelse(void) {
4705+
printf("\n--- BUG81: braceless ctrl-flow orelse/zeroinit in defer ---\n");
4706+
// orelse in braceless if body inside defer
4707+
{
4708+
PrismResult r = prism_transpile_source(
4709+
"int f(void) {\n"
4710+
" int result = -1;\n"
4711+
" int zero = 0;\n"
4712+
" {\n"
4713+
" defer {\n"
4714+
" if (1)\n"
4715+
" result = *(&zero) orelse 99;\n"
4716+
" }\n"
4717+
" }\n"
4718+
" return result;\n"
4719+
"}\n",
4720+
"bug81_if.c", prism_defaults());
4721+
CHECK_EQ(r.status, PRISM_OK,
4722+
"bug81-braceless-if: transpiles OK");
4723+
if (r.status == PRISM_OK && r.output) {
4724+
CHECK(strstr(r.output, "orelse") == NULL,
4725+
"bug81-braceless-if: no literal 'orelse' in output");
4726+
CHECK(strstr(r.output, "__prism_oe_") != NULL,
4727+
"bug81-braceless-if: orelse temp generated");
4728+
}
4729+
prism_free(&r);
4730+
}
4731+
// orelse in else body inside defer
4732+
{
4733+
PrismResult r = prism_transpile_source(
4734+
"int f(void) {\n"
4735+
" int result = -1;\n"
4736+
" int zero = 0;\n"
4737+
" {\n"
4738+
" defer {\n"
4739+
" if (0) result = 0;\n"
4740+
" else result = *(&zero) orelse 77;\n"
4741+
" }\n"
4742+
" }\n"
4743+
" return result;\n"
4744+
"}\n",
4745+
"bug81_else.c", prism_defaults());
4746+
CHECK_EQ(r.status, PRISM_OK,
4747+
"bug81-braceless-else: transpiles OK");
4748+
if (r.status == PRISM_OK && r.output) {
4749+
CHECK(strstr(r.output, "orelse") == NULL,
4750+
"bug81-braceless-else: no literal 'orelse' in output");
4751+
}
4752+
prism_free(&r);
4753+
}
4754+
// orelse in braceless for body inside defer
4755+
{
4756+
PrismResult r = prism_transpile_source(
4757+
"int f(void) {\n"
4758+
" int result = -1;\n"
4759+
" int zero = 0;\n"
4760+
" {\n"
4761+
" defer {\n"
4762+
" for (int i = 0; i < 1; i++)\n"
4763+
" result = *(&zero) orelse 55;\n"
4764+
" }\n"
4765+
" }\n"
4766+
" return result;\n"
4767+
"}\n",
4768+
"bug81_for.c", prism_defaults());
4769+
CHECK_EQ(r.status, PRISM_OK,
4770+
"bug81-braceless-for: transpiles OK");
4771+
if (r.status == PRISM_OK && r.output) {
4772+
CHECK(strstr(r.output, "orelse") == NULL,
4773+
"bug81-braceless-for: no literal 'orelse' in output");
4774+
}
4775+
prism_free(&r);
4776+
}
4777+
// orelse in braceless do body inside defer
4778+
{
4779+
PrismResult r = prism_transpile_source(
4780+
"int f(void) {\n"
4781+
" int result = -1;\n"
4782+
" int zero = 0;\n"
4783+
" {\n"
4784+
" defer {\n"
4785+
" do\n"
4786+
" result = *(&zero) orelse 11;\n"
4787+
" while (0);\n"
4788+
" }\n"
4789+
" }\n"
4790+
" return result;\n"
4791+
"}\n",
4792+
"bug81_do.c", prism_defaults());
4793+
CHECK_EQ(r.status, PRISM_OK,
4794+
"bug81-braceless-do: transpiles OK");
4795+
if (r.status == PRISM_OK && r.output) {
4796+
CHECK(strstr(r.output, "orelse") == NULL,
4797+
"bug81-braceless-do: no literal 'orelse' in output");
4798+
}
4799+
prism_free(&r);
4800+
}
4801+
}
4802+
47014803
void run_defer_tests(void) {
47024804
printf("\n=== DEFER TESTS ===\n");
47034805
test_defer_in_comma_expr_bug();
@@ -4975,6 +5077,205 @@ void run_defer_tests(void) {
49755077
// BUG79: emit_range_no_prep missing stmt-expr dispatch
49765078
test_emit_range_no_prep_stmtexpr_defer();
49775079

5080+
// BUG81: braceless ctrl-flow in defer body — orelse/zeroinit not processed
5081+
test_defer_braceless_ctrl_orelse();
5082+
5083+
// BUG82: __builtin_unreachable() leaks outside braceless if-body in defer
5084+
{
5085+
printf("\n--- BUG82: unreachable in braceless ctrl-flow defer ---\n");
5086+
// noreturn call in braceless if(0) inside defer body:
5087+
// __builtin_unreachable() must NOT appear outside the if-body
5088+
PrismResult r = prism_transpile_source(
5089+
"_Noreturn void die(void);\n"
5090+
"void f(void) {\n"
5091+
" defer {\n"
5092+
" if (0)\n"
5093+
" die();\n"
5094+
" }\n"
5095+
"}\n",
5096+
"bug82_braceless.c", prism_defaults());
5097+
CHECK_EQ(r.status, PRISM_OK,
5098+
"bug82-braceless-unreachable: transpiles OK");
5099+
if (r.status == PRISM_OK && r.output) {
5100+
// The output should NOT contain __builtin_unreachable
5101+
// because the noreturn call is in a braceless body
5102+
// and emit_deferred_range cannot inject scope braces.
5103+
const char *fn = strstr(r.output, "void f(");
5104+
CHECK(fn != NULL, "bug82: function found");
5105+
if (fn)
5106+
CHECK(strstr(fn, "__builtin_unreachable") == NULL &&
5107+
strstr(fn, "__assume") == NULL,
5108+
"bug82: no unreachable leak outside braceless if");
5109+
}
5110+
prism_free(&r);
5111+
5112+
// Top-level noreturn in defer body SHOULD still get unreachable
5113+
r = prism_transpile_source(
5114+
"_Noreturn void die(void);\n"
5115+
"void f(void) {\n"
5116+
" defer { die(); }\n"
5117+
"}\n",
5118+
"bug82_toplevel.c", prism_defaults());
5119+
CHECK_EQ(r.status, PRISM_OK,
5120+
"bug82-toplevel: transpiles OK");
5121+
if (r.status == PRISM_OK && r.output) {
5122+
CHECK(strstr(r.output, "__builtin_unreachable") != NULL ||
5123+
strstr(r.output, "__assume") != NULL,
5124+
"bug82-toplevel: unreachable injected at top level");
5125+
}
5126+
prism_free(&r);
5127+
}
5128+
5129+
// BUG85: braceless ctrl-flow in defer body missing brace_wrap for multi-stmt expansion
5130+
{
5131+
printf("\n--- BUG85: braceless ctrl-flow brace_wrap in defer ---\n");
5132+
// typeof zeroinit in braceless if inside defer: memset must be inside braces
5133+
{
5134+
PrismResult r = prism_transpile_source(
5135+
"void f(void) {\n"
5136+
" defer {\n"
5137+
" if (1)\n"
5138+
" typeof(int) x;\n"
5139+
" (void)0;\n"
5140+
" }\n"
5141+
"}\n",
5142+
"bug85_typeof_if.c", prism_defaults());
5143+
CHECK_EQ(r.status, PRISM_OK,
5144+
"bug85-typeof-if: transpiles OK");
5145+
if (r.status == PRISM_OK && r.output) {
5146+
// The memset MUST be inside braces with the declaration
5147+
CHECK(strstr(r.output, "if (1) {") != NULL ||
5148+
strstr(r.output, "if (1){") != NULL,
5149+
"bug85-typeof-if: braceless if body wrapped in braces");
5150+
CHECK(has_zeroing(r.output),
5151+
"bug85-typeof-if: memset generated");
5152+
}
5153+
prism_free(&r);
5154+
}
5155+
// typeof zeroinit in braceless for inside defer
5156+
{
5157+
PrismResult r = prism_transpile_source(
5158+
"void f(void) {\n"
5159+
" defer {\n"
5160+
" for (int i = 0; i < 1; i++)\n"
5161+
" typeof(int) x;\n"
5162+
" }\n"
5163+
"}\n",
5164+
"bug85_typeof_for.c", prism_defaults());
5165+
CHECK_EQ(r.status, PRISM_OK,
5166+
"bug85-typeof-for: transpiles OK");
5167+
if (r.status == PRISM_OK && r.output) {
5168+
const char *fn = strstr(r.output, "void f(");
5169+
CHECK(fn != NULL, "bug85-typeof-for: function found");
5170+
if (fn) {
5171+
CHECK(has_zeroing(fn),
5172+
"bug85-typeof-for: memset generated");
5173+
}
5174+
}
5175+
prism_free(&r);
5176+
}
5177+
// typeof zeroinit in braceless else inside defer
5178+
{
5179+
PrismResult r = prism_transpile_source(
5180+
"void f(void) {\n"
5181+
" defer {\n"
5182+
" if (0) (void)0;\n"
5183+
" else\n"
5184+
" typeof(int) x;\n"
5185+
" }\n"
5186+
"}\n",
5187+
"bug85_typeof_else.c", prism_defaults());
5188+
CHECK_EQ(r.status, PRISM_OK,
5189+
"bug85-typeof-else: transpiles OK");
5190+
if (r.status == PRISM_OK && r.output) {
5191+
CHECK(has_zeroing(r.output),
5192+
"bug85-typeof-else: memset generated");
5193+
}
5194+
prism_free(&r);
5195+
}
5196+
// orelse decl in braceless if inside defer: multi-stmt must be wrapped
5197+
{
5198+
PrismResult r = prism_transpile_source(
5199+
"int *get_null(void);\n"
5200+
"void f(void) {\n"
5201+
" defer {\n"
5202+
" if (1)\n"
5203+
" int *p = get_null() orelse 0;\n"
5204+
" }\n"
5205+
"}\n",
5206+
"bug85_orelse_if.c", prism_defaults());
5207+
CHECK_EQ(r.status, PRISM_OK,
5208+
"bug85-orelse-if: transpiles OK");
5209+
if (r.status == PRISM_OK && r.output) {
5210+
const char *fn = strstr(r.output, "void f(");
5211+
CHECK(fn != NULL, "bug85-orelse-if: function found");
5212+
if (fn) {
5213+
CHECK(strstr(fn, "orelse") == NULL,
5214+
"bug85-orelse-if: no literal orelse");
5215+
// Must have brace wrap for multi-stmt expansion
5216+
CHECK(strstr(fn, "if (1) {") != NULL ||
5217+
strstr(fn, "if (1){") != NULL,
5218+
"bug85-orelse-if: braceless body wrapped in braces");
5219+
}
5220+
}
5221+
prism_free(&r);
5222+
}
5223+
}
5224+
5225+
// BUG85b: orelse block mini-loop missing ctrl-flow keyword tracking
5226+
{
5227+
printf("\n--- BUG85b: ctrl-flow in orelse block body ---\n");
5228+
// typeof zeroinit in braceless if inside orelse block
5229+
{
5230+
PrismResult r = prism_transpile_source(
5231+
"int *get_null(void);\n"
5232+
"void f(void) {\n"
5233+
" int *p = get_null() orelse {\n"
5234+
" if (1)\n"
5235+
" typeof(int) x;\n"
5236+
" return;\n"
5237+
" };\n"
5238+
"}\n",
5239+
"bug85b_typeof_if.c", prism_defaults());
5240+
CHECK_EQ(r.status, PRISM_OK,
5241+
"bug85b-typeof-if-orelse: transpiles OK");
5242+
if (r.status == PRISM_OK && r.output) {
5243+
const char *fn = strstr(r.output, "void f(");
5244+
CHECK(fn != NULL, "bug85b: function found");
5245+
if (fn) {
5246+
CHECK(has_zeroing(fn),
5247+
"bug85b-typeof-if-orelse: memset generated for typeof");
5248+
}
5249+
}
5250+
prism_free(&r);
5251+
}
5252+
// orelse in braceless while inside orelse block
5253+
{
5254+
PrismResult r = prism_transpile_source(
5255+
"int f(int *p);\n"
5256+
"void g(void) {\n"
5257+
" int *q = (int*)(0) orelse {\n"
5258+
" while (0)\n"
5259+
" typeof(int) y;\n"
5260+
" return;\n"
5261+
" };\n"
5262+
" (void)q;\n"
5263+
"}\n",
5264+
"bug85b_while.c", prism_defaults());
5265+
CHECK_EQ(r.status, PRISM_OK,
5266+
"bug85b-while-orelse: transpiles OK");
5267+
if (r.status == PRISM_OK && r.output) {
5268+
const char *fn = strstr(r.output, "void g(");
5269+
CHECK(fn != NULL, "bug85b-while: function found");
5270+
if (fn) {
5271+
CHECK(has_zeroing(fn),
5272+
"bug85b-while-orelse: memset generated");
5273+
}
5274+
}
5275+
prism_free(&r);
5276+
}
5277+
}
5278+
49785279
// Taint propagation: non-wrapper deep chain via function pointer assignment
49795280
{
49805281
printf("\n--- Taint propagation: non-wrapper fp chain ---\n");

0 commit comments

Comments
 (0)