@@ -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+
47014803void 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