Added noreturn support
authorRobert Pengelly <robertapengelly@hotmail.com>
Thu, 20 Aug 2026 14:01:40 +0000 (15:01 +0100)
committerRobert Pengelly <robertapengelly@hotmail.com>
Thu, 20 Aug 2026 14:01:40 +0000 (15:01 +0100)
amd64.c
i386.c
parse.c
parse.h

diff --git a/amd64.c b/amd64.c
index ef55b2b58719a2e011bbbaa91a7fb8e0060f5133..e6f96130318bc0026b44b8212d7edb5bbfb76b0c 100644 (file)
--- a/amd64.c
+++ b/amd64.c
@@ -97,8 +97,11 @@ static void clear_rhs_last_pointer_info (void) {
 }
 
 static int current_function_has_return_statement = 0;
+static int statement_ends_control_flow = 0;
+
 static int current_parse_block_depth = 0;
 static int current_function_is_void = 0;
+static int current_function_is_noreturn = 0;
 static int current_function_is_floating = 0;
 static int current_function_return_size = DATA_NONE;
 static int current_function_return_is_unsigned = 0;
@@ -453,6 +456,7 @@ static void clear_global_symbols (void) {
         global_symbols[i].is_unsigned = 0;
         global_symbols[i].is_floating = 0;
         global_symbols[i].returns_void = 0;
+        global_symbols[i].is_noreturn = 0;
         
         global_symbols[i].calling_convention = TOK_EOF;
         
@@ -925,6 +929,16 @@ static void set_global_symbol_returns_void (const char *name, int returns_void)
 
 }
 
+static void set_global_symbol_noreturn (const char *name, int is_noreturn) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0) {
+        global_symbols[i].is_noreturn = is_noreturn ? 1 : 0;
+    }
+
+}
+
 static void set_global_symbol_calling_convention (const char *name, enum token_kind calling_convention) {
 
     int i = find_global_symbol (name);
@@ -947,6 +961,18 @@ static int get_global_symbol_returns_void (const char *name) {
 
 }
 
+static int get_global_symbol_noreturn (const char *name) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0 && global_symbols[i].kind == GLOBAL_SYMBOL_FUNCTION) {
+        return global_symbols[i].is_noreturn;
+    }
+    
+    return 0;
+
+}
+
 static void set_global_symbol_param_count (const char *name, int param_count, int has_prototype, int is_variadic) {
 
     int i = find_global_symbol (name);
@@ -1114,6 +1140,7 @@ static int add_global_symbol (const char *name, int kind, int is_extern, const c
     global_symbols[global_symbol_count].pointed_is_floating = 0;
     global_symbols[global_symbol_count].pointed_is_unsigned = 0;
     global_symbols[global_symbol_count].returns_void = 0;
+    global_symbols[global_symbol_count].is_noreturn = 0;
     global_symbols[global_symbol_count].calling_convention = TOK_EOF;
     
     {
@@ -8605,6 +8632,8 @@ static void parse_block (void) {
                 char static_label[128];
                 
                 int declaration_dllimport = parsed_dllimport;
+                int declaration_noreturn = parsed_noreturn;
+                
                 int declared_pointer_depth;
                 int declared_pointed_size;
                 int declared_type_size;
@@ -8617,6 +8646,8 @@ static void parse_block (void) {
                 parse_declarator (&name);
                 
                 declaration_dllimport = declaration_dllimport || parsed_dllimport || declarator_dllimport;
+                declaration_noreturn = declaration_noreturn || parsed_noreturn || declarator_noreturn;
+                
                 apply_typedef_array_to_declarator ();
                 
                 if (declarator_has_array && declarator_array_unsized && tok.kind == TOK_ASSIGN) {
@@ -8686,6 +8717,10 @@ static void parse_block (void) {
                         set_global_symbol_floating (name, (declarator_is_pointer || (declarator_has_function && declarator_function_is_pointer)) ? 0 : parsed_type_is_floating);
                         set_global_symbol_returns_void (name, declarator_has_function && parsed_type_is_void && !declarator_is_pointer && !declarator_function_is_pointer);
                         
+                        if (declaration_noreturn) {
+                            set_global_symbol_noreturn (name, 1);
+                        }
+                        
                         if (declarator_has_function) {
                         
                             set_global_symbol_param_count (name, declarator_function_param_count, declarator_function_has_prototype || declarator_function_param_count > 0, declarator_function_is_variadic);
@@ -25848,6 +25883,10 @@ static void emit_call_identifier_to_reg_now (const char *name, const char *reg,
     int hidden_struct_return_arg = 0;
     int effective_argc = 0;
     
+    if (get_global_symbol_noreturn (name)) {
+        statement_ends_control_flow = 1;
+    }
+    
     if (tok.kind != TOK_LPAREN) {
         return;
     }
@@ -26516,6 +26555,10 @@ static int goto_ref_count = 0;
 static int current_break_label = -1;
 static int current_continue_label = -1;
 
+/* Track escapes from the innermost loop for fall-through analysis. */
+static int *current_loop_escape_seen = 0;
+static int current_loop_break_label_for_flow = -1;
+
 static long current_break_cleanup_base = 0;
 static long current_continue_cleanup_base = 0;
 
@@ -26531,6 +26574,7 @@ struct switch_case_entry {
 struct switch_context {
 
     struct switch_case_entry cases[MAX_SWITCH_CASES];
+    
     int case_count;
     int default_label;
     int break_label;
@@ -26538,7 +26582,6 @@ struct switch_context {
 };
 
 static struct switch_context *current_switch_context = 0;
-static int statement_ends_control_flow = 0;
 
 static void reset_goto_labels (void) {
 
@@ -34497,6 +34540,12 @@ static void parse_for_statement (void) {
     int cond_known = 0;
     int cond_value = 1;
     
+    int condition_is_infinite = 0;
+    int loop_escape_seen = 0;
+    
+    int *old_loop_escape_seen = current_loop_escape_seen;
+    int old_loop_break_label_for_flow = current_loop_break_label_for_flow;
+    
     FILE *saved_ofp;
     FILE *step_tmp = 0;
     
@@ -34512,7 +34561,10 @@ static void parse_for_statement (void) {
     emit_statement_label (loop_label);
     
     if (tok.kind == TOK_SEMI) {
+    
+        condition_is_infinite = 1;
         get_token ();
+    
     } else {
     
         emit_statement_jump_if_false (break_label);
@@ -34520,6 +34572,8 @@ static void parse_for_statement (void) {
         cond_known = statement_condition_constant_known;
         cond_value = statement_condition_constant_value;
         
+        condition_is_infinite = cond_known && cond_value;
+        
         if (tok.kind != TOK_SEMI) {
             skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
         }
@@ -34553,6 +34607,8 @@ static void parse_for_statement (void) {
     
     current_break_label = break_label;
     current_continue_label = continue_label;
+    current_loop_escape_seen = &loop_escape_seen;
+    current_loop_break_label_for_flow = break_label;
     current_break_cleanup_base = current_block_cleanup_bytes;
     current_continue_cleanup_base = current_block_cleanup_bytes;
     
@@ -34587,10 +34643,12 @@ static void parse_for_statement (void) {
     
     current_break_label = old_break_label;
     current_continue_label = old_continue_label;
+    current_loop_escape_seen = old_loop_escape_seen;
+    current_loop_break_label_for_flow = old_loop_break_label_for_flow;
     current_break_cleanup_base = old_break_cleanup_base;
     current_continue_cleanup_base = old_continue_cleanup_base;
     
-    statement_ends_control_flow = 0;
+    statement_ends_control_flow = condition_is_infinite && !loop_escape_seen;
 
 }
 
@@ -34608,6 +34666,11 @@ static void parse_while_statement (void) {
     int cond_known;
     int cond_value;
     
+    int loop_escape_seen = 0;
+    
+    int *old_loop_escape_seen = current_loop_escape_seen;
+    int old_loop_break_label_for_flow = current_loop_break_label_for_flow;
+    
     get_token ();
     expect (TOK_LPAREN, "(");
     
@@ -34625,6 +34688,8 @@ static void parse_while_statement (void) {
     
     current_break_label = break_label;
     current_continue_label = loop_label;
+    current_loop_escape_seen = &loop_escape_seen;
+    current_loop_break_label_for_flow = break_label;
     current_break_cleanup_base = current_block_cleanup_bytes;
     current_continue_cleanup_base = current_block_cleanup_bytes;
     
@@ -34647,10 +34712,12 @@ static void parse_while_statement (void) {
     
     current_break_label = old_break_label;
     current_continue_label = old_continue_label;
+    current_loop_escape_seen = old_loop_escape_seen;
+    current_loop_break_label_for_flow = old_loop_break_label_for_flow;
     current_break_cleanup_base = old_break_cleanup_base;
     current_continue_cleanup_base = old_continue_cleanup_base;
     
-    statement_ends_control_flow = 0;
+    statement_ends_control_flow = cond_known && cond_value && !loop_escape_seen;
 
 }
 
@@ -34668,10 +34735,17 @@ static void parse_do_statement (void) {
     int cond_known = 0;
     int cond_value = 0;
     
+    int loop_escape_seen = 0;
+    
+    int *old_loop_escape_seen = current_loop_escape_seen;
+    int old_loop_break_label_for_flow = current_loop_break_label_for_flow;
+    
     get_token ();
     
     current_break_label = break_label;
     current_continue_label = cond_label;
+    current_loop_escape_seen = &loop_escape_seen;
+    current_loop_break_label_for_flow = break_label;
     current_break_cleanup_base = current_block_cleanup_bytes;
     current_continue_cleanup_base = current_block_cleanup_bytes;
     
@@ -34709,10 +34783,12 @@ static void parse_do_statement (void) {
     
     current_break_label = old_break_label;
     current_continue_label = old_continue_label;
+    current_loop_escape_seen = old_loop_escape_seen;
+    current_loop_break_label_for_flow = old_loop_break_label_for_flow;
     current_break_cleanup_base = old_break_cleanup_base;
     current_continue_cleanup_base = old_continue_cleanup_base;
     
-    statement_ends_control_flow = 0;
+    statement_ends_control_flow = cond_known && cond_value && !loop_escape_seen;
 
 }
 
@@ -37075,6 +37151,14 @@ static void parse_statement (void) {
         
         }
         
+        if (current_function_is_noreturn) {
+        
+            if (state->warn & WARN_NO_RETURN) {
+                report_line_at (get_filename (), ret_line, REPORT_WARNING, ret_start, ret_caret, "return in function declared '__declspec(noreturn)'");
+            }
+        
+        }
+        
         if (current_function_is_void && has_value) {
         
             if (state->warn & WARN_NO_RETURN) {
@@ -37295,8 +37379,12 @@ static void parse_statement (void) {
              */
             
             reference_goto_label (label_name, label_line, label_start, label_caret);
-            statement_ends_control_flow = 1;
             
+            if (current_loop_escape_seen) {
+                *current_loop_escape_seen = 1;
+            }
+            
+            statement_ends_control_flow = 1;
             free (label_name);
         
         } else {
@@ -37326,6 +37414,11 @@ static void parse_statement (void) {
             }
             
             emit_statement_jump (target_label);
+            
+            if (current_loop_escape_seen && target_label == current_loop_break_label_for_flow) {
+                *current_loop_escape_seen = 1;
+            }
+            
             statement_ends_control_flow = 1;
         
         }
@@ -37429,7 +37522,18 @@ static void parse_statement (void) {
         
         }
         
-        emit_load_assignment_rhs_expression_to_reg ("rax");
+        {
+        
+            int expression_ends_control_flow = (tok.kind == TOK_IDENT && tok.ident &&
+                token_identifier_is_function_call_rhs_now () && get_global_symbol_noreturn (tok.ident));
+            
+            emit_load_assignment_rhs_expression_to_reg ("rax");
+            
+            if (expression_ends_control_flow) {
+                statement_ends_control_flow = 1;
+            }
+        
+        }
         
         if (tok.kind == TOK_SEMI) {
         
@@ -37449,12 +37553,14 @@ static void parse_statement (void) {
 
 }
 
-static void parse_function_body (const char *name, int storage_class, int is_inline, int return_is_void, int return_is_floating, int return_is_unsigned, int return_size, int return_pointer_depth, unsigned long function_line, const char *function_start, const char *function_caret) {
+static void parse_function_body (const char *name, int storage_class, int is_inline, int is_noreturn, int return_is_void, int return_is_floating, int return_is_unsigned, int return_size, int return_pointer_depth, unsigned long function_line, const char *function_start, const char *function_caret) {
 
     int old_return_label = current_return_label;
     int should_emit = 1;
     int emit_body = 1;
     int emit_public = 1;
+    
+    int effective_noreturn = is_noreturn || get_global_symbol_noreturn (name);
     int emit_inline_definition_to_output = 1;
     
     FILE *saved_ofp;
@@ -37472,6 +37578,7 @@ static void parse_function_body (const char *name, int storage_class, int is_inl
     int saved_current_section = current_section;
     
     int old_function_is_void;
+    int old_function_is_noreturn;
     int old_function_is_floating;
     int old_function_return_size;
     int old_function_return_is_unsigned;
@@ -37542,6 +37649,7 @@ static void parse_function_body (const char *name, int storage_class, int is_inl
         set_global_symbol_unsigned (name, 0);
         set_global_symbol_floating (name, return_is_floating);
         set_global_symbol_returns_void (name, return_is_void);
+        set_global_symbol_noreturn (name, effective_noreturn);
         set_global_symbol_calling_convention (name, (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention);
         set_global_symbol_dllimport (name, 0);
         set_global_symbol_param_count (name, saved_declarator_function_param_count, saved_declarator_function_has_prototype || saved_declarator_function_param_count > 0, saved_declarator_function_is_variadic);
@@ -37557,6 +37665,7 @@ static void parse_function_body (const char *name, int storage_class, int is_inl
     
     old_function_has_return_statement = current_function_has_return_statement;
     old_function_is_void = current_function_is_void;
+    old_function_is_noreturn = current_function_is_noreturn;
     old_function_is_floating = current_function_is_floating;
     old_function_return_size = current_function_return_size;
     old_function_return_is_unsigned = current_function_return_is_unsigned;
@@ -37564,6 +37673,7 @@ static void parse_function_body (const char *name, int storage_class, int is_inl
     old_function_returns_aggregate = current_function_returns_aggregate;
     
     current_function_is_void = return_is_void;
+    current_function_is_noreturn = effective_noreturn;
     current_function_is_floating = return_is_floating;
     current_function_return_is_unsigned = return_is_unsigned;
     current_function_return_size = return_size;
@@ -37618,10 +37728,18 @@ static void parse_function_body (const char *name, int storage_class, int is_inl
     current_function_frame_enabled = (!is_inline && capture_function_body);
     emit_function_start (name, !emit_public);
     
+    statement_ends_control_flow = 0;
+    
     parse_block ();
     check_goto_labels ();
     
-    if (!current_function_is_void && !current_function_has_return_statement) {
+    if (current_function_is_noreturn && !current_function_has_return_statement && !statement_ends_control_flow) {
+    
+        if (state->warn & WARN_NO_RETURN) {
+            report_line_at (function_filename_copy ? function_filename_copy : get_filename (), function_line, REPORT_WARNING, function_start, function_caret, "function declared '__declspec(noreturn)' may return");
+        }
+    
+    } else if (!current_function_is_void && !current_function_has_return_statement && !statement_ends_control_flow) {
     
         if (state->warn & WARN_NO_RETURN) {
             report_line_at (function_filename_copy ? function_filename_copy : get_filename (), function_line, REPORT_WARNING, function_start, function_caret, "control reaches end of non-void function");
@@ -37690,6 +37808,7 @@ static void parse_function_body (const char *name, int storage_class, int is_inl
     
     current_function_has_return_statement = old_function_has_return_statement;
     current_function_is_void = old_function_is_void;
+    current_function_is_noreturn = old_function_is_noreturn;
     current_function_is_floating = old_function_is_floating;
     current_function_return_size = old_function_return_size;
     current_function_return_is_unsigned = old_function_return_is_unsigned;
@@ -37859,7 +37978,7 @@ static int parse_possible_knr_function (void) {
     
     if (is_type_start (tok.kind) || tok.kind == TOK_LBRACE) {
     
-        parse_function_body (name, STORAGE_NONE, 0, 0, 0, 0, DATA_INT & 0x1f, 0, name_line, name_start, name_caret);
+        parse_function_body (name, STORAGE_NONE, 0, 0, 0, 0, 0, DATA_INT & 0x1f, 0, name_line, name_start, name_caret);
         
         if (parsed_dllexport || declarator_dllexport) {
         
@@ -39166,6 +39285,8 @@ static void parse_external_after_type (void) {
         
         int declaration_is_inline;
         int declaration_storage;
+        
+        int declaration_noreturn = 0;
         int declaration_dllimport = 0;
         int declaration_dllexport = 0;
         
@@ -39191,11 +39312,13 @@ static void parse_external_after_type (void) {
         
         declaration_is_inline = parsed_type_is_inline;
         declaration_storage = parsed_storage_class;
+        declaration_noreturn = parsed_noreturn;
         declaration_dllimport = parsed_dllimport;
         declaration_dllexport = parsed_dllexport;
         
         parse_declarator (&name);
         
+        declaration_noreturn = declaration_noreturn || parsed_noreturn || declarator_noreturn;
         declaration_dllimport = declaration_dllimport || parsed_dllimport || declarator_dllimport;
         declaration_dllexport = declaration_dllexport || parsed_dllexport || declarator_dllexport;
         
@@ -39267,7 +39390,7 @@ static void parse_external_after_type (void) {
             
             }
             
-            parse_function_body (name, declaration_storage, declaration_is_inline,
+            parse_function_body (name, declaration_storage, declaration_is_inline, declaration_noreturn,
                 (parsed_type_is_void && !declarator_is_pointer),
                     (declarator_is_pointer ? 0 : parsed_type_is_floating),
                         (declarator_is_pointer ? 0 : parsed_type_is_unsigned),
@@ -39403,8 +39526,12 @@ static void parse_external_after_type (void) {
                     set_global_symbol_unsigned (name, 0);
                     set_global_symbol_floating (name, declarator_is_pointer ? 0 : parsed_type_is_floating);
                     set_global_symbol_returns_void (name, parsed_type_is_void && !declarator_is_pointer && !declarator_function_is_pointer);
-                    set_global_symbol_param_count (name, declarator_function_param_count, declarator_function_has_prototype || declarator_function_param_count > 0, declarator_function_is_variadic);
                     
+                    if (declaration_noreturn) {
+                        set_global_symbol_noreturn (name, 1);
+                    }
+                    
+                    set_global_symbol_param_count (name, declarator_function_param_count, declarator_function_has_prototype || declarator_function_param_count > 0, declarator_function_is_variadic);
                     set_global_symbol_calling_convention (name, (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention);
                     set_global_symbol_dllimport (name, declaration_dllimport);
                     
diff --git a/i386.c b/i386.c
index 98f1ba53126ffc229f777864003e2dfc2e989549..b4388e62dd84175fabc1bc3071cc5508533941cb 100644 (file)
--- a/i386.c
+++ b/i386.c
@@ -70,8 +70,11 @@ static void clear_rhs_last_pointer_info (void) {
 }
 
 static int current_function_has_return_statement = 0;
+static int statement_ends_control_flow = 0;
+
 static int current_parse_block_depth = 0;
 static int current_function_is_void = 0;
+static int current_function_is_noreturn = 0;
 static int current_function_is_floating = 0;
 static int current_function_return_size = DATA_NONE;
 static int current_function_return_is_unsigned = 0;
@@ -253,6 +256,7 @@ static void clear_global_symbols (void) {
         global_symbols[i].is_unsigned = 0;
         global_symbols[i].is_floating = 0;
         global_symbols[i].returns_void = 0;
+        global_symbols[i].is_noreturn = 0;
         
         global_symbols[i].calling_convention = TOK_EOF;
         
@@ -728,6 +732,16 @@ static void set_global_symbol_returns_void (const char *name, int returns_void)
 
 }
 
+static void set_global_symbol_noreturn (const char *name, int is_noreturn) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0) {
+        global_symbols[i].is_noreturn = is_noreturn ? 1 : 0;
+    }
+
+}
+
 static void set_global_symbol_calling_convention (const char *name, enum token_kind calling_convention) {
 
     int i = find_global_symbol (name);
@@ -762,6 +776,18 @@ static int get_global_symbol_returns_void (const char *name) {
 
 }
 
+static int get_global_symbol_noreturn (const char *name) {
+
+    int i = find_global_symbol (name);
+    
+    if (i >= 0 && global_symbols[i].kind == GLOBAL_SYMBOL_FUNCTION) {
+        return global_symbols[i].is_noreturn;
+    }
+    
+    return 0;
+
+}
+
 static void set_global_symbol_param_count (const char *name, int param_count, int has_prototype, int is_variadic) {
 
     int i = find_global_symbol (name);
@@ -929,6 +955,7 @@ static int add_global_symbol (const char *name, int kind, int is_extern, const c
     global_symbols[global_symbol_count].pointed_is_floating = 0;
     global_symbols[global_symbol_count].pointed_is_unsigned = 0;
     global_symbols[global_symbol_count].returns_void = 0;
+    global_symbols[global_symbol_count].is_noreturn = 0;
     global_symbols[global_symbol_count].calling_convention = TOK_EOF;
     
     {
@@ -7862,6 +7889,7 @@ static void parse_block (void) {
                 char static_label[128];
                 
                 int declaration_dllimport = parsed_dllimport;
+                int declaration_noreturn = parsed_noreturn;
                 
                 for (i = 0; i < MAX_AGG_FIELDS; i++) {
                     init_symbols[i] = 0;
@@ -7870,6 +7898,8 @@ static void parse_block (void) {
                 parse_declarator (&name);
                 
                 declaration_dllimport = declaration_dllimport || parsed_dllimport || declarator_dllimport;
+                declaration_noreturn = declaration_noreturn || parsed_noreturn || declarator_noreturn;
+                
                 apply_typedef_array_to_declarator ();
                 
                 if (declarator_has_array && declarator_array_unsized && tok.kind == TOK_ASSIGN) {
@@ -7924,6 +7954,10 @@ static void parse_block (void) {
                         set_global_symbol_floating (name, (declarator_is_pointer || (declarator_has_function && declarator_function_is_pointer)) ? 0 : parsed_type_is_floating);
                         set_global_symbol_returns_void (name, declarator_has_function && parsed_type_is_void && !declarator_is_pointer && !declarator_function_is_pointer);
                         
+                        if (declaration_noreturn) {
+                            set_global_symbol_noreturn (name, 1);
+                        }
+                        
                         if (declarator_has_function) {
                         
                             set_global_symbol_param_count (name, declarator_function_param_count, declarator_function_has_prototype || declarator_function_param_count > 0, declarator_function_is_variadic);
@@ -23087,6 +23121,10 @@ static void emit_call_identifier_to_reg_now (const char *name, const char *reg,
     FILE **arg_tmp_ofps = 0;
     FILE **new_arg_tmp_ofps = 0;
     
+    if (get_global_symbol_noreturn (name)) {
+        statement_ends_control_flow = 1;
+    }
+    
     if (tok.kind != TOK_LPAREN) {
         return;
     }
@@ -23643,6 +23681,10 @@ static int goto_ref_count = 0;
 static int current_break_label = -1;
 static int current_continue_label = -1;
 
+/* Track escapes from the innermost loop for fall-through analysis. */
+static int *current_loop_escape_seen = 0;
+static int current_loop_break_label_for_flow = -1;
+
 static long current_break_cleanup_base = 0;
 static long current_continue_cleanup_base = 0;
 
@@ -23658,6 +23700,7 @@ struct switch_case_entry {
 struct switch_context {
 
     struct switch_case_entry cases[MAX_SWITCH_CASES];
+    
     int case_count;
     int default_label;
     int break_label;
@@ -23665,7 +23708,6 @@ struct switch_context {
 };
 
 static struct switch_context *current_switch_context = 0;
-static int statement_ends_control_flow = 0;
 
 static void reset_goto_labels (void) {
 
@@ -31976,6 +32018,12 @@ static void parse_for_statement (void) {
     int cond_known = 0;
     int cond_value = 1;
     
+    int condition_is_infinite = 0;
+    int loop_escape_seen = 0;
+    
+    int *old_loop_escape_seen = current_loop_escape_seen;
+    int old_loop_break_label_for_flow = current_loop_break_label_for_flow;
+    
     FILE *saved_ofp;
     FILE *step_tmp = 0;
     
@@ -31991,7 +32039,10 @@ static void parse_for_statement (void) {
     emit_statement_label (loop_label);
     
     if (tok.kind == TOK_SEMI) {
+    
+        condition_is_infinite = 1;
         get_token ();
+    
     } else {
     
         emit_statement_jump_if_false (break_label);
@@ -31999,6 +32050,8 @@ static void parse_for_statement (void) {
         cond_known = statement_condition_constant_known;
         cond_value = statement_condition_constant_value;
         
+        condition_is_infinite = cond_known && cond_value;
+        
         if (tok.kind != TOK_SEMI) {
             skip_balanced_until (TOK_SEMI, TOK_EOF, TOK_EOF);
         }
@@ -32032,6 +32085,8 @@ static void parse_for_statement (void) {
     
     current_break_label = break_label;
     current_continue_label = continue_label;
+    current_loop_escape_seen = &loop_escape_seen;
+    current_loop_break_label_for_flow = break_label;
     current_break_cleanup_base = current_block_cleanup_bytes;
     current_continue_cleanup_base = current_block_cleanup_bytes;
     
@@ -32066,10 +32121,12 @@ static void parse_for_statement (void) {
     
     current_break_label = old_break_label;
     current_continue_label = old_continue_label;
+    current_loop_escape_seen = old_loop_escape_seen;
+    current_loop_break_label_for_flow = old_loop_break_label_for_flow;
     current_break_cleanup_base = old_break_cleanup_base;
     current_continue_cleanup_base = old_continue_cleanup_base;
     
-    statement_ends_control_flow = 0;
+    statement_ends_control_flow = condition_is_infinite && !loop_escape_seen;
 
 }
 
@@ -32087,6 +32144,11 @@ static void parse_while_statement (void) {
     int cond_known;
     int cond_value;
     
+    int loop_escape_seen = 0;
+    
+    int *old_loop_escape_seen = current_loop_escape_seen;
+    int old_loop_break_label_for_flow = current_loop_break_label_for_flow;
+    
     get_token ();
     expect (TOK_LPAREN, "(");
     
@@ -32104,6 +32166,8 @@ static void parse_while_statement (void) {
     
     current_break_label = break_label;
     current_continue_label = loop_label;
+    current_loop_escape_seen = &loop_escape_seen;
+    current_loop_break_label_for_flow = break_label;
     current_break_cleanup_base = current_block_cleanup_bytes;
     current_continue_cleanup_base = current_block_cleanup_bytes;
     
@@ -32126,10 +32190,12 @@ static void parse_while_statement (void) {
     
     current_break_label = old_break_label;
     current_continue_label = old_continue_label;
+    current_loop_escape_seen = old_loop_escape_seen;
+    current_loop_break_label_for_flow = old_loop_break_label_for_flow;
     current_break_cleanup_base = old_break_cleanup_base;
     current_continue_cleanup_base = old_continue_cleanup_base;
     
-    statement_ends_control_flow = 0;
+    statement_ends_control_flow = cond_known && cond_value && !loop_escape_seen;
 
 }
 
@@ -32147,10 +32213,17 @@ static void parse_do_statement (void) {
     int cond_known = 0;
     int cond_value = 0;
     
+    int loop_escape_seen = 0;
+    
+    int *old_loop_escape_seen = current_loop_escape_seen;
+    int old_loop_break_label_for_flow = current_loop_break_label_for_flow;
+    
     get_token ();
     
     current_break_label = break_label;
     current_continue_label = cond_label;
+    current_loop_escape_seen = &loop_escape_seen;
+    current_loop_break_label_for_flow = break_label;
     current_break_cleanup_base = current_block_cleanup_bytes;
     current_continue_cleanup_base = current_block_cleanup_bytes;
     
@@ -32188,10 +32261,12 @@ static void parse_do_statement (void) {
     
     current_break_label = old_break_label;
     current_continue_label = old_continue_label;
+    current_loop_escape_seen = old_loop_escape_seen;
+    current_loop_break_label_for_flow = old_loop_break_label_for_flow;
     current_break_cleanup_base = old_break_cleanup_base;
     current_continue_cleanup_base = old_continue_cleanup_base;
     
-    statement_ends_control_flow = 0;
+    statement_ends_control_flow = cond_known && cond_value && !loop_escape_seen;
 
 }
 
@@ -34529,6 +34604,14 @@ static void parse_statement (void) {
         
         }
         
+        if (current_function_is_noreturn) {
+        
+            if (state->warn & WARN_NO_RETURN) {
+                report_line_at (get_filename (), ret_line, REPORT_WARNING, ret_start, ret_caret, "return in function declared '__declspec(noreturn)'");
+            }
+        
+        }
+        
         if (current_function_is_void && has_value) {
         
             if (state->warn & WARN_NO_RETURN) {
@@ -34743,8 +34826,12 @@ static void parse_statement (void) {
              */
             
             reference_goto_label (label_name, label_line, label_start, label_caret);
-            statement_ends_control_flow = 1;
             
+            if (current_loop_escape_seen) {
+                *current_loop_escape_seen = 1;
+            }
+            
+            statement_ends_control_flow = 1;
             free (label_name);
         
         } else {
@@ -34774,6 +34861,11 @@ static void parse_statement (void) {
             }
             
             emit_statement_jump (target_label);
+            
+            if (current_loop_escape_seen && target_label == current_loop_break_label_for_flow) {
+                *current_loop_escape_seen = 1;
+            }
+            
             statement_ends_control_flow = 1;
         
         }
@@ -34877,7 +34969,18 @@ static void parse_statement (void) {
         
         }
         
-        emit_load_assignment_rhs_expression_to_reg ("eax");
+        {
+        
+            int expression_ends_control_flow = (tok.kind == TOK_IDENT && tok.ident &&
+                token_identifier_is_function_call_rhs_now () && get_global_symbol_noreturn (tok.ident));
+            
+            emit_load_assignment_rhs_expression_to_reg ("eax");
+            
+            if (expression_ends_control_flow) {
+                statement_ends_control_flow = 1;
+            }
+        
+        }
         
         if (tok.kind == TOK_SEMI) {
         
@@ -34897,12 +35000,14 @@ static void parse_statement (void) {
 
 }
 
-static void parse_function_body (const char *name, int storage_class, int is_inline, int return_is_void, int return_is_floating, int return_is_unsigned, int return_size, unsigned long function_line, const char *function_start, const char *function_caret) {
+static void parse_function_body (const char *name, int storage_class, int is_inline, int is_noreturn, int return_is_void, int return_is_floating, int return_is_unsigned, int return_size, unsigned long function_line, const char *function_start, const char *function_caret) {
 
     int old_return_label = current_return_label;
     int should_emit = 1;
     int emit_body = 1;
     int emit_public = 1;
+    
+    int effective_noreturn = is_noreturn || get_global_symbol_noreturn (name);
     int emit_inline_definition_to_output = 1;
     
     FILE *saved_ofp;
@@ -34920,6 +35025,7 @@ static void parse_function_body (const char *name, int storage_class, int is_inl
     int saved_current_section = current_section;
     
     int old_function_is_void;
+    int old_function_is_noreturn;
     int old_function_is_floating;
     int old_function_return_size;
     int old_function_return_is_unsigned;
@@ -34989,6 +35095,7 @@ static void parse_function_body (const char *name, int storage_class, int is_inl
         set_global_symbol_unsigned (name, 0);
         set_global_symbol_floating (name, return_is_floating);
         set_global_symbol_returns_void (name, return_is_void);
+        set_global_symbol_noreturn (name, effective_noreturn);
         set_global_symbol_calling_convention (name, (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention);
         set_global_symbol_dllimport (name, 0);
         set_global_symbol_param_count (name, saved_declarator_function_param_count, saved_declarator_function_has_prototype || saved_declarator_function_param_count > 0, saved_declarator_function_is_variadic);
@@ -35004,12 +35111,14 @@ static void parse_function_body (const char *name, int storage_class, int is_inl
     
     old_function_has_return_statement = current_function_has_return_statement;
     old_function_is_void = current_function_is_void;
+    old_function_is_noreturn = current_function_is_noreturn;
     old_function_is_floating = current_function_is_floating;
     old_function_return_size = current_function_return_size;
     old_function_return_is_unsigned = current_function_return_is_unsigned;
     old_function_returns_aggregate = current_function_returns_aggregate;
     
     current_function_is_void = return_is_void;
+    current_function_is_noreturn = effective_noreturn;
     current_function_is_floating = return_is_floating;
     current_function_return_is_unsigned = return_is_unsigned;
     current_function_return_size = return_size;
@@ -35061,10 +35170,18 @@ static void parse_function_body (const char *name, int storage_class, int is_inl
     current_function_frame_enabled = (!is_inline && capture_function_body);
     emit_function_start (name, !emit_public);
     
+    statement_ends_control_flow = 0;
+    
     parse_block ();
     check_goto_labels ();
     
-    if (!current_function_is_void && !current_function_has_return_statement) {
+    if (current_function_is_noreturn && !current_function_has_return_statement && !statement_ends_control_flow) {
+    
+        if (state->warn & WARN_NO_RETURN) {
+            report_line_at (function_filename_copy ? function_filename_copy : get_filename (), function_line, REPORT_WARNING, function_start, function_caret, "function declared '__declspec(noreturn)' may return");
+        }
+    
+    } else if (!current_function_is_void && !current_function_has_return_statement && !statement_ends_control_flow) {
     
         if (state->warn & WARN_NO_RETURN) {
             report_line_at (function_filename_copy ? function_filename_copy : get_filename (), function_line, REPORT_WARNING, function_start, function_caret, "control reaches end of non-void function");
@@ -35132,6 +35249,7 @@ static void parse_function_body (const char *name, int storage_class, int is_inl
     
     current_function_has_return_statement = old_function_has_return_statement;
     current_function_is_void = old_function_is_void;
+    current_function_is_noreturn = old_function_is_noreturn;
     current_function_is_floating = old_function_is_floating;
     current_function_return_size = old_function_return_size;
     current_function_return_is_unsigned = old_function_return_is_unsigned;
@@ -35300,7 +35418,7 @@ static int parse_possible_knr_function (void) {
     
     if (is_type_start (tok.kind) || tok.kind == TOK_LBRACE) {
     
-        parse_function_body (name, STORAGE_NONE, 0, 0, 0, 0, DATA_INT & 0x1f, name_line, name_start, name_caret);
+        parse_function_body (name, STORAGE_NONE, 0, 0, 0, 0, 0, DATA_INT & 0x1f, name_line, name_start, name_caret);
         
         if (parsed_dllexport || declarator_dllexport) {
         
@@ -36500,6 +36618,8 @@ static void parse_external_after_type (void) {
         
         int declaration_is_inline;
         int declaration_storage;
+        
+        int declaration_noreturn = 0;
         int declaration_dllimport = 0;
         
         int decl_is_pointer;
@@ -36525,10 +36645,13 @@ static void parse_external_after_type (void) {
         declaration_is_inline = parsed_type_is_inline;
         declaration_storage = parsed_storage_class;
         declaration_dllimport = parsed_dllimport;
+        declaration_noreturn = parsed_noreturn;
         
         parse_declarator (&name);
         
         declaration_dllimport = declaration_dllimport || parsed_dllimport || declarator_dllimport;
+        declaration_noreturn = declaration_noreturn || parsed_noreturn || declarator_noreturn;
+        
         apply_typedef_array_to_declarator ();
         
         decl_is_pointer = declarator_is_pointer;
@@ -36596,7 +36719,7 @@ static void parse_external_after_type (void) {
             
             }
             
-            parse_function_body (name, declaration_storage, declaration_is_inline,
+            parse_function_body (name, declaration_storage, declaration_is_inline, declaration_noreturn,
                 (parsed_type_is_void && !declarator_is_pointer),
                     (declarator_is_pointer ? 0 : parsed_type_is_floating),
                         (declarator_is_pointer ? 0 : parsed_type_is_unsigned),
@@ -36731,8 +36854,12 @@ static void parse_external_after_type (void) {
                     set_global_symbol_unsigned (name, 0);
                     set_global_symbol_floating (name, declarator_is_pointer ? 0 : parsed_type_is_floating);
                     set_global_symbol_returns_void (name, parsed_type_is_void && !declarator_is_pointer && !declarator_function_is_pointer);
-                    set_global_symbol_param_count (name, declarator_function_param_count, declarator_function_has_prototype || declarator_function_param_count > 0, declarator_function_is_variadic);
                     
+                    if (declaration_noreturn) {
+                        set_global_symbol_noreturn (name, 1);
+                    }
+                    
+                    set_global_symbol_param_count (name, declarator_function_param_count, declarator_function_has_prototype || declarator_function_param_count > 0, declarator_function_is_variadic);
                     set_global_symbol_calling_convention (name, (declarator_calling_convention != TOK_EOF) ? declarator_calling_convention : parsed_calling_convention);
                     set_global_symbol_dllimport (name, declaration_dllimport);
                     
diff --git a/parse.c b/parse.c
index 11160d539a03cee261a95fa5e4deab3ad1f7d99f..5e82a0cfc049a72d34c3765c27718e206acd7b6c 100644 (file)
--- a/parse.c
+++ b/parse.c
@@ -23,8 +23,10 @@ enum token_kind postfix_member_calling_convention = TOK_EOF;
 enum token_kind parsed_calling_convention = TOK_EOF;
 
 struct vector vec_dllexports = { 0 };
+
 int parsed_dllexport = 0;
 int parsed_dllimport = 0;
+int parsed_noreturn = 0;
 
 int parsed_type_size = DATA_NONE;
 int parsed_type_is_inline = 0;
@@ -57,9 +59,11 @@ int declarator_function_has_prototype = 0;
 int declarator_function_is_variadic = 0;
 int declarator_array_unsized = 0;
 
-int declarator_is_pointer = 0;
 int declarator_pointer_depth = 0;
+int declarator_is_pointer = 0;
+
 int declarator_has_array = 0;
+int declarator_noreturn = 0;
 
 int declarator_dllexport = 0;
 int declarator_dllimport = 0;
@@ -467,6 +471,19 @@ static void parse_decl_modifier (void) {
             
             get_token ();
         
+        } else if (strcmp (tok.ident, "noreturn") == 0) {
+        
+            if (parsed_noreturn || declarator_noreturn) {
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "duplicate '__declspec(noreturn)'");
+            } else {
+            
+                declarator_noreturn = 1;
+                parsed_noreturn = 1;
+            
+            }
+            
+            get_token ();
+        
         } else {
         
             report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "unsupported '__declspec' attribute '%s'", tok.ident);
@@ -580,6 +597,7 @@ void parse_declarator (char **out_name) {
     
     int saved_declarator_dllexport = declarator_dllexport;
     int saved_declarator_dllimport = declarator_dllimport;
+    int saved_declarator_noreturn = declarator_noreturn;
     
     unsigned long saved_captured_declarator_name_line = captured_declarator_name_line;
     
@@ -594,6 +612,7 @@ void parse_declarator (char **out_name) {
         
         declarator_dllexport = 0;
         declarator_dllimport = 0;
+        declarator_noreturn = 0;
         
         captured_declarator_name_location = 0;
         captured_declarator_name_start = 0;
@@ -658,6 +677,7 @@ void parse_declarator (char **out_name) {
         declarator_calling_convention = saved_declarator_calling_convention;
         declarator_dllexport = saved_declarator_dllexport;
         declarator_dllimport = saved_declarator_dllimport;
+        declarator_noreturn = saved_declarator_noreturn;
     
     }
 
@@ -2486,6 +2506,7 @@ void parse_type_spec (void) {
     
     parsed_dllexport = 0;
     parsed_dllimport = 0;
+    parsed_noreturn = 0;
     
     clear_parsed_fields ();
     
diff --git a/parse.h b/parse.h
index ab491d1e03775a3b0bcb85d6c3fde7736e61e6e3..edef0cb4e769310b10975517b1905eb19a4ea5a6 100644 (file)
--- a/parse.h
+++ b/parse.h
@@ -19,8 +19,10 @@ extern enum token_kind postfix_member_calling_convention;
 extern enum token_kind parsed_calling_convention;
 
 extern struct vector vec_dllexports;
+
 extern int parsed_dllexport;
 extern int parsed_dllimport;
+extern int parsed_noreturn;
 
 extern int parsed_type_size;
 extern int parsed_type_is_inline;
@@ -59,6 +61,7 @@ extern int declarator_has_array;
 
 extern int declarator_dllexport;
 extern int declarator_dllimport;
+extern int declarator_noreturn;
 
 extern enum token_kind declarator_calling_convention;
 extern int global_initializer_accept_symbol_addresses;
@@ -218,6 +221,7 @@ struct global_symbol_entry {
     int pointed_is_floating;
     int pointed_is_unsigned;
     int returns_void;
+    int is_noreturn;
     
     int param_count;
     int has_prototype;