Added support for designated initializers
authorRobert Pengelly <robertapengelly@hotmail.com>
Mon, 14 Sep 2026 10:08:00 +0000 (11:08 +0100)
committerRobert Pengelly <robertapengelly@hotmail.com>
Mon, 14 Sep 2026 10:08:00 +0000 (11:08 +0100)
amd64.c
i386.c

diff --git a/amd64.c b/amd64.c
index de42ba5bce33c207898a30a5677c30c92173a22e..ec6e95e0c7b127a905c814e0977ee7e61c297a18 100644 (file)
--- a/amd64.c
+++ b/amd64.c
@@ -8546,6 +8546,313 @@ static void append_local_zero_initializer (struct local_init *inits, int *init_c
 
 }
 
+static int aggregate_field_index_from_offset (const int *fields, int field_count, int wanted_offset, int *out_index, long *out_offset) {
+
+    int offset = 0, i;
+    
+    for (i = 0; i < field_count; i++) {
+    
+        int size = fields[i];
+        
+        if (size < 0) {
+        
+            offset += -size;
+            continue;
+        
+        }
+        
+        if (offset == wanted_offset) {
+        
+            if (out_index) {
+                *out_index = i;
+            }
+            
+            if (out_offset) {
+                *out_offset = offset;
+            }
+            
+            return 1;
+        
+        }
+        
+        offset += size;
+    
+    }
+    
+    return 0;
+
+}
+
+static void append_local_aggregate_zero_initializers (struct local_init *inits, int *init_count, int max_inits, long base_offset, const int *fields, int field_count) {
+
+    long offset = 0, i;
+    
+    for (i = 0; i < field_count; i++) {
+    
+        int size = fields[i];
+        
+        if (size < 0) {
+        
+            offset += -size;
+            continue;
+        
+        }
+        
+        append_local_zero_initializer (inits, init_count, max_inits, base_offset + offset, size);
+        offset += size;
+    
+    }
+
+}
+
+static int parse_designated_member_path (int object_size, const char *owner_tag_name, int *out_offset, int *out_size, int *out_elem_size, int *out_is_array, int *out_is_floating, unsigned long *out_line, const char **out_start, const char **out_caret) {
+
+    char current_tag_name[128];
+    
+    int current_size = object_size;
+    int total_offset = 0;
+    
+    int member_elem_size = 0;
+    int member_is_array = 0;
+    int member_size = 0;
+    int member_is_floating = 0;
+    
+    current_tag_name[0] = '\0';
+    
+    if (owner_tag_name && owner_tag_name[0]) {
+    
+        strncpy (current_tag_name, owner_tag_name, sizeof (current_tag_name) - 1);
+        current_tag_name[sizeof (current_tag_name) - 1] = '\0';
+    
+    }
+    
+    while (tok.kind == TOK_DOT) {
+    
+        char *member_name;
+        
+        int found_member_elem_size = 0;
+        int found_member_is_array = 0;
+        
+        int member_offset = 0;
+        int member_pointer_depth = 0;
+        
+        const char *member_start;
+        const char *member_caret;
+        
+        unsigned long member_line;
+        get_token ();
+        
+        member_start = tok.start;
+        member_caret = tok.caret;
+        member_line = get_line_number ();
+        
+        if (out_line) {
+            *out_line = member_line;
+        }
+        
+        if (out_start) {
+            *out_start = member_start;
+        }
+        
+        if (out_caret) {
+            *out_caret = member_caret;
+        }
+        
+        if (tok.kind != TOK_IDENT) {
+        
+            report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after '.'");
+            return 0;
+        
+        }
+        
+        member_name = xstrdup (tok.ident);
+        get_token ();
+        
+        if (!find_member_info_ex_bounded (member_name, current_size,
+            current_tag_name[0] ? current_tag_name : 0,
+                &member_offset, &member_size, &found_member_elem_size,
+                    &member_pointer_depth, &found_member_is_array,
+                        &member_is_floating)) {
+        
+            report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member_name);
+            
+            free (member_name);
+            return 0;
+        
+        }
+        
+        free (member_name);
+        
+        total_offset += member_offset;
+        current_size = member_size;
+        
+        member_elem_size = found_member_elem_size;
+        member_is_array = found_member_is_array;
+        
+        if (last_found_member_tag_name && last_found_member_tag_name[0]) {
+        
+            strncpy (current_tag_name, last_found_member_tag_name, sizeof (current_tag_name) - 1);
+            current_tag_name[sizeof (current_tag_name) - 1] = '\0';
+        
+        } else {
+            current_tag_name[0] = '\0';
+        }
+    
+    }
+    
+    expect (TOK_ASSIGN, "=");
+    
+    if (out_offset) {
+        *out_offset = total_offset;
+    }
+    
+    if (out_size) {
+        *out_size = member_size;
+    }
+    
+    if (out_elem_size) {
+        *out_elem_size = member_elem_size;
+    }
+    
+    if (out_is_array) {
+        *out_is_array = member_is_array;
+    }
+    
+    if (out_is_floating) {
+        *out_is_floating = member_is_floating;
+    }
+    
+    return 1;
+
+}
+
+static int parse_local_designated_aggregate_initializer (struct local_init *inits, int *init_count, int max_inits, long base_offset, const int *fields, int field_count, int object_size, const char *owner_tag_name) {
+
+    long next_field_offset = 0;
+    
+    int explicit_count = 0;
+    int next_field_index = 0;
+    
+    append_local_aggregate_zero_initializers (inits, init_count, max_inits, base_offset, fields, field_count);
+    
+    while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) {
+    
+        int field_index = next_field_index;
+        long field_offset = next_field_offset;
+        
+        int field_size = 0;
+        int field_is_floating = 0;
+        
+        int member_size = 0;
+        int member_elem_size = 0;
+        int member_is_array = 0;
+        
+        if (tok.kind == TOK_DOT) {
+        
+            int member_offset = 0;
+            int member_is_floating = 0;
+            
+            const char *member_start = tok.start;
+            const char *member_caret = tok.caret;
+            
+            unsigned long member_line = get_line_number ();
+            
+            if (!parse_designated_member_path (object_size, owner_tag_name, &member_offset, &member_size, &member_elem_size, &member_is_array, &member_is_floating, &member_line, &member_start, &member_caret)) {
+            
+                skip_initializer ();
+                break;
+            
+            }
+            
+            if (!aggregate_field_index_from_offset (fields, field_count, member_offset, &field_index, &field_offset)) {
+            
+                report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "member designator does not name a directly initializable field");
+                
+                skip_initializer ();
+                break;
+            
+            }
+            
+            field_size = member_size;
+            field_is_floating = member_is_floating;
+        
+        } else {
+        
+            while (field_index < field_count && fields[field_index] < 0) {
+                field_offset += -fields[field_index++];
+            }
+            
+            if (field_index >= field_count) {
+            
+                skip_initializer ();
+                break;
+            
+            }
+            
+            field_size = fields[field_index];
+        
+        }
+        
+        if (member_is_array && member_elem_size == (DATA_CHAR & 0x1f) && is_string_token ()) {
+        
+            int64_s string_values[MAX_STRING_INIT_BYTES];
+            
+            int string_count = 0;
+            int string_index;
+            
+            int string_capacity = member_size / (DATA_CHAR & 0x1f);
+            parse_string_initializer_values (string_values, MAX_STRING_INIT_BYTES, &string_count);
+            
+            if (string_count > string_capacity) {
+            
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "too many initializer values for character array member");
+                string_count = string_capacity;
+            
+            }
+            
+            for (string_index = 0; string_index < string_count && *init_count < max_inits; string_index++) {
+            
+                inits[*init_count].offset = base_offset + field_offset + string_index;
+                inits[*init_count].size = DATA_CHAR & 0x1f;
+                inits[*init_count].kind = LOCAL_INIT_CONST;
+                inits[*init_count].value = string_values[string_index];
+                inits[*init_count].symbol = 0;
+                (*init_count)++;
+            
+            }
+        
+        } else if (*init_count < max_inits) {
+        
+            inits[*init_count].offset = base_offset + field_offset;
+            inits[*init_count].size = field_size;
+            
+            parse_local_initializer_value (&inits[*init_count], field_size, field_is_floating);
+            (*init_count)++;
+        
+        } else {
+            skip_initializer ();
+        }
+        
+        explicit_count++;
+        
+        next_field_index = field_index + 1;
+        next_field_offset = field_offset + fields[field_index];
+        
+        if (!_accept (TOK_COMMA)) {
+            break;
+        }
+        
+        if (tok.kind == TOK_RBRACE) {
+            break;
+        }
+    
+    }
+    
+    expect (TOK_RBRACE, "}");
+    return explicit_count;
+
+}
+
 static void parse_local_aggregate_initializer_group (struct local_init *inits, int *init_count, int max_inits, long base_offset, const int *fields, int field_count, int *field_index, long *field_offset) {
 
     int braced = 0;
@@ -8654,7 +8961,7 @@ static void parse_local_aggregate_initializer_group (struct local_init *inits, i
 
 }
 
-static int parse_local_aggregate_initializer_values (struct local_init *inits, int *init_count, int max_inits, long base_offset, const int *fields, int field_count) {
+static int parse_local_aggregate_initializer_values (struct local_init *inits, int *init_count, int max_inits, long base_offset, const int *fields, int field_count, int object_size, const char *owner_tag_name) {
 
     int initial_init_count = *init_count;
     int provided_init_count;
@@ -8662,7 +8969,73 @@ static int parse_local_aggregate_initializer_values (struct local_init *inits, i
     int field_index = 0;
     long field_offset = 0;
     
-    parse_local_aggregate_initializer_group (inits, init_count, max_inits, base_offset, fields, field_count, &field_index, &field_offset);
+    if (tok.kind == TOK_LBRACE) {
+    
+        get_token ();
+        
+        if (tok.kind == TOK_DOT) {
+            return parse_local_designated_aggregate_initializer (inits, init_count, max_inits, base_offset, fields, field_count, object_size, owner_tag_name);
+        }
+        
+        /* Re-enter the ordinary braced initializer parser after consuming '{'. */
+        while (field_index < field_count) {
+        
+            int field_size;
+            
+            while (field_index < field_count && fields[field_index] < 0) {
+                field_offset += -fields[field_index++];
+            }
+            
+            if (field_index >= field_count || tok.kind == TOK_RBRACE) {
+                break;
+            }
+            
+            field_size = fields[field_index];
+            
+            if (tok.kind == TOK_LBRACE) {
+                parse_local_aggregate_initializer_group (inits, init_count, max_inits, base_offset, fields, field_count, &field_index, &field_offset);
+            } else if (*init_count < max_inits) {
+            
+                if (is_string_token ()) {
+                    parse_local_string_field_initializer (inits, init_count, max_inits, base_offset + field_offset, field_size);
+                } else {
+                
+                    inits[*init_count].offset = base_offset + field_offset;
+                    inits[*init_count].size = field_size;
+                    
+                    parse_local_initializer_value (&inits[*init_count], 0, 0);
+                    (*init_count)++;
+                
+                }
+                
+                field_offset += field_size;
+                field_index++;
+            
+            } else {
+                
+                skip_initializer ();
+                
+                field_offset += field_size;
+                field_index++;
+            
+            }
+            
+            if (!_accept (TOK_COMMA)) {
+                break;
+            }
+            
+            if (tok.kind == TOK_RBRACE) {
+                break;
+            }
+        
+        }
+        
+        expect (TOK_RBRACE, "}");
+    
+    } else {
+        parse_local_aggregate_initializer_group (inits, init_count, max_inits, base_offset, fields, field_count, &field_index, &field_offset);
+    }
+    
     provided_init_count = *init_count - initial_init_count;
     
     while (field_index < field_count) {
@@ -8822,6 +9195,8 @@ static void ensure_block_stack_allocated (long block_stack_start, long *block_st
 }
 
 static int global_initializer_cast_before_address_now (void);
+
+static void parse_global_designated_initializer_values (int64_s *values, char **symbols, int max_values, int *count, int object_size, const char *owner_tag_name, const int *fields, int field_count);
 static void parse_global_initializer_values (int64_s *values, char **symbols, int max_values, int *count);
 
 static int aggregate_initializer_value_field_count (const int *field_sizes, int field_count);
@@ -9147,7 +9522,9 @@ static void parse_block (void) {
                                 int saved_accept_symbol_addresses = global_initializer_accept_symbol_addresses;
                                 global_initializer_accept_symbol_addresses = declarator_is_pointer || parsed_type_is_aggregate || declarator_has_array || global_initializer_cast_before_address_now ();
                                 
-                                if (declarator_has_array && !declarator_is_pointer && parsed_type_is_aggregate && parsed_field_count > 0 && tok.kind == TOK_LBRACE) {
+                                if (!declarator_has_array && !declarator_is_pointer && parsed_type_is_aggregate && tok.kind == TOK_LBRACE) {
+                                    parse_global_designated_initializer_values (init_values, init_symbols, MAX_AGG_FIELDS, &init_value_count, parsed_type_size, parsed_type_tag_name, object_fields, object_field_count);
+                                } else if (declarator_has_array && !declarator_is_pointer && parsed_type_is_aggregate && parsed_field_count > 0 && tok.kind == TOK_LBRACE) {
                                     parse_global_initializer_values_padded_elements (init_values, init_symbols, MAX_AGG_FIELDS, &init_value_count, aggregate_initializer_value_field_count (parsed_field_sizes, parsed_field_count));
                                 } else {
                                     parse_global_initializer_values (init_values, init_symbols, MAX_AGG_FIELDS, &init_value_count);
@@ -9313,7 +9690,7 @@ static void parse_block (void) {
                     } else if (object_is_auto && !declarator_is_pointer && (parsed_type_is_aggregate || declarator_has_array) && tok.kind == TOK_LBRACE) {
                     
                         int first_init_index = init_count;
-                        int provided_init_count = parse_local_aggregate_initializer_values (inits, &init_count, MAX_LOCAL_INITS, object_offset, object_fields, object_field_count);
+                        int provided_init_count = parse_local_aggregate_initializer_values (inits, &init_count, MAX_LOCAL_INITS, object_offset, object_fields, object_field_count, object_size, parsed_type_tag_name);
                         int expected_init_count = aggregate_initializer_value_field_count (parsed_field_sizes, parsed_field_count);
                         int zero_initializer = provided_init_count == 1 && first_init_index < init_count && inits[first_init_index].kind == LOCAL_INIT_CONST && !inits[first_init_index].symbol && inits[first_init_index].value.low == 0 && inits[first_init_index].value.high == 0;
                         
@@ -39799,6 +40176,175 @@ static void parse_global_initializer_values_padded_elements (int64_s *values, ch
 
 }
 
+static int aggregate_value_index_from_offset (const int *fields, int field_count, int wanted_offset) {
+
+    int offset = 0;
+    int value_index = 0;
+    
+    int i;
+    
+    for (i = 0; i < field_count; i++) {
+    
+        int size = fields[i];
+        
+        if (size < 0) {
+        
+            offset += -size;
+            continue;
+        
+        }
+        
+        if (offset == wanted_offset) {
+            return value_index;
+        }
+        
+        offset += size;
+        value_index++;
+    
+    }
+    
+    return -1;
+
+}
+
+static int aggregate_value_field_count (const int *fields, int field_count) {
+
+    int count = 0, i;
+    
+    for (i = 0; i < field_count; i++) {
+    
+        if (fields[i] > 0) {
+            count++;
+        }
+    
+    }
+    
+    return count;
+
+}
+
+static void parse_global_designated_initializer_values (int64_s *values, char **symbols, int max_values, int *count, int object_size, const char *owner_tag_name, const int *fields, int field_count) {
+
+    int field_values = aggregate_value_field_count (fields, field_count);
+    int next_value_index = 0, i;
+    
+    if (field_values > max_values) {
+        field_values = max_values;
+    }
+    
+    for (i = 0; i < field_values; i++) {
+    
+        values[i].low = 0;
+        values[i].high = 0;
+        
+        if (symbols) {
+            symbols[i] = 0;
+        }
+    
+    }
+    
+    *count = field_values;
+    expect (TOK_LBRACE, "{");
+    
+    while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) {
+    
+        int value_index = next_value_index;
+        
+        int member_size = 0;
+        int member_elem_size = 0;
+        int member_is_array = 0;
+        
+        if (tok.kind == TOK_DOT) {
+        
+            int member_offset = 0;
+            int member_is_floating = 0;
+            
+            const char *member_start = tok.start;
+            const char *member_caret = tok.caret;
+            
+            unsigned long member_line = get_line_number ();
+
+            if (!parse_designated_member_path (object_size, owner_tag_name, &member_offset, &member_size, &member_elem_size, &member_is_array, &member_is_floating, &member_line, &member_start, &member_caret)) {
+            
+                skip_initializer ();
+                break;
+            
+            }
+            
+            value_index = aggregate_value_index_from_offset (fields, field_count, member_offset);
+            
+            if (value_index < 0 || value_index >= field_values) {
+            
+                report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "member designator does not name a directly initializable field");
+                
+                skip_initializer ();
+                break;
+            
+            }
+        
+        }
+        
+        if (value_index >= 0 && value_index < field_values) {
+        
+            int parsed_count = 0;
+            
+            if (member_is_array && member_elem_size == (DATA_CHAR & 0x1f) && is_string_token ()) {
+            
+                int64_s string_values[MAX_STRING_INIT_BYTES];
+                
+                int string_count = 0;
+                int string_index;
+                
+                int string_capacity = member_size / (DATA_CHAR & 0x1f);
+                parse_string_initializer_values (string_values, MAX_STRING_INIT_BYTES, &string_count);
+                
+                if (string_count > string_capacity) {
+                
+                    report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "too many initializer values for character array member");
+                    string_count = string_capacity;
+                
+                }
+                
+                for (string_index = 0; string_index < string_count && value_index + string_index < field_values; string_index++) {
+                
+                    values[value_index + string_index] = string_values[string_index];
+                    
+                    if (symbols) {
+                        symbols[value_index + string_index] = 0;
+                    }
+                
+                }
+                
+                parsed_count = string_capacity;
+            
+            } else {
+                parse_global_initializer_values (values + value_index, symbols ? symbols + value_index : 0, field_values - value_index, &parsed_count);
+            }
+            
+            if (parsed_count <= 0) {
+                parsed_count = 1;
+            }
+            
+            next_value_index = value_index + parsed_count;
+        
+        } else {
+            skip_initializer ();
+        }
+        
+        if (!_accept (TOK_COMMA)) {
+            break;
+        }
+        
+        if (tok.kind == TOK_RBRACE) {
+            break;
+        }
+    
+    }
+    
+    expect (TOK_RBRACE, "}");
+
+}
+
 static const char *global_initializer_symbol_name_now (void) {
 
     struct local_symbol *local;
@@ -41036,7 +41582,9 @@ static void parse_external_after_type (void) {
                     int saved_accept_symbol_addresses = global_initializer_accept_symbol_addresses;
                     global_initializer_accept_symbol_addresses = declarator_is_pointer || declarator_has_function || parsed_type_is_aggregate || declarator_has_array || global_initializer_cast_before_address_now ();
                     
-                    if (declarator_has_array && !declarator_is_pointer && parsed_type_is_aggregate && parsed_field_count > 0 && tok.kind == TOK_LBRACE) {
+                    if (!declarator_has_array && !declarator_is_pointer && parsed_type_is_aggregate && tok.kind == TOK_LBRACE) {
+                        parse_global_designated_initializer_values (init_values, init_symbols, MAX_GLOBAL_INIT_FIELDS, &init_value_count, parsed_type_size, parsed_type_tag_name, object_fields, object_field_count);
+                    } else if (declarator_has_array && !declarator_is_pointer && parsed_type_is_aggregate && parsed_field_count > 0 && tok.kind == TOK_LBRACE) {
                         parse_global_initializer_values_padded_elements (init_values, init_symbols, MAX_GLOBAL_INIT_FIELDS, &init_value_count, aggregate_initializer_value_field_count (parsed_field_sizes, parsed_field_count));
                     } else {
                         parse_global_initializer_values (init_values, init_symbols, MAX_GLOBAL_INIT_FIELDS, &init_value_count);
diff --git a/i386.c b/i386.c
index 32323e43a87cf9003106606408fc89ff86e0f5ce..d9543462a776014e5039994303b99bc6a705c5f8 100644 (file)
--- a/i386.c
+++ b/i386.c
@@ -7752,6 +7752,316 @@ static void append_local_zero_initializer (struct local_init *inits, int *init_c
 
 }
 
+static int aggregate_field_index_from_offset (const int *fields, int field_count, int wanted_offset, int *out_index, long *out_offset) {
+
+    int offset = 0;
+    int i;
+    
+    for (i = 0; i < field_count; i++) {
+    
+        int size = fields[i];
+        
+        if (size < 0) {
+        
+            offset += -size;
+            continue;
+        
+        }
+        
+        if (offset == wanted_offset) {
+        
+            if (out_index) {
+                *out_index = i;
+            }
+            
+            if (out_offset) {
+                *out_offset = offset;
+            }
+            
+            return 1;
+        
+        }
+        
+        offset += size;
+    
+    }
+    
+    return 0;
+
+}
+
+
+static void append_local_aggregate_zero_initializers (struct local_init *inits, int *init_count, int max_inits, long base_offset, const int *fields, int field_count) {
+
+    long offset = 0;
+    int i;
+    
+    for (i = 0; i < field_count; i++) {
+    
+        int size = fields[i];
+        
+        if (size < 0) {
+        
+            offset += -size;
+            continue;
+        
+        }
+        
+        append_local_zero_initializer (inits, init_count, max_inits, base_offset + offset, size);
+        offset += size;
+    
+    }
+
+}
+
+static int parse_designated_member_path (int object_size, const char *owner_tag_name, int *out_offset, int *out_size, int *out_elem_size, int *out_is_array, int *out_is_floating, unsigned long *out_line, const char **out_start, const char **out_caret) {
+
+    char current_tag_name[128];
+    
+    int current_size = object_size;
+    int total_offset = 0;
+    
+    int member_elem_size = 0;
+    int member_is_array = 0;
+    int member_size = 0;
+    int member_is_floating = 0;
+    
+    current_tag_name[0] = '\0';
+    
+    if (owner_tag_name && owner_tag_name[0]) {
+    
+        strncpy (current_tag_name, owner_tag_name, sizeof (current_tag_name) - 1);
+        current_tag_name[sizeof (current_tag_name) - 1] = '\0';
+    
+    }
+    
+    while (tok.kind == TOK_DOT) {
+    
+        char *member_name;
+        
+        int found_member_elem_size = 0;
+        int found_member_is_array = 0;
+        
+        int member_offset = 0;
+        int member_pointer_depth = 0;
+        
+        const char *member_start;
+        const char *member_caret;
+        
+        unsigned long member_line;
+        get_token ();
+        
+        member_start = tok.start;
+        member_caret = tok.caret;
+        member_line = get_line_number ();
+        
+        if (out_line) {
+            *out_line = member_line;
+        }
+        
+        if (out_start) {
+            *out_start = member_start;
+        }
+        
+        if (out_caret) {
+            *out_caret = member_caret;
+        }
+        
+        if (tok.kind != TOK_IDENT) {
+        
+            report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "expected member name after '.'");
+            return 0;
+        
+        }
+        
+        member_name = xstrdup (tok.ident);
+        get_token ();
+        
+        if (!find_member_info_ex_bounded (member_name, current_size,
+            current_tag_name[0] ? current_tag_name : 0,
+                &member_offset, &member_size, &found_member_elem_size,
+                    &member_pointer_depth, &found_member_is_array,
+                        &member_is_floating)) {
+        
+            report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "unknown member '%s'", member_name);
+            
+            free (member_name);
+            return 0;
+        
+        }
+        
+        free (member_name);
+        
+        total_offset += member_offset;
+        current_size = member_size;
+        
+        member_elem_size = found_member_elem_size;
+        member_is_array = found_member_is_array;
+        
+        if (last_found_member_tag_name && last_found_member_tag_name[0]) {
+        
+            strncpy (current_tag_name, last_found_member_tag_name, sizeof (current_tag_name) - 1);
+            current_tag_name[sizeof (current_tag_name) - 1] = '\0';
+        
+        } else {
+            current_tag_name[0] = '\0';
+        }
+    
+    }
+    
+    expect (TOK_ASSIGN, "=");
+    
+    if (out_offset) {
+        *out_offset = total_offset;
+    }
+    
+    if (out_size) {
+        *out_size = member_size;
+    }
+    
+    if (out_elem_size) {
+        *out_elem_size = member_elem_size;
+    }
+    
+    if (out_is_array) {
+        *out_is_array = member_is_array;
+    }
+    
+    if (out_is_floating) {
+        *out_is_floating = member_is_floating;
+    }
+    
+    return 1;
+
+}
+
+static int parse_local_designated_aggregate_initializer (struct local_init *inits, int *init_count, int max_inits, long base_offset, const int *fields, int field_count, int object_size, const char *owner_tag_name) {
+
+    long next_field_offset = 0;
+    
+    int explicit_count = 0;
+    int next_field_index = 0;
+    
+    append_local_aggregate_zero_initializers (inits, init_count, max_inits, base_offset, fields, field_count);
+    
+    while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) {
+    
+        int field_index = next_field_index;
+        long field_offset = next_field_offset;
+        
+        int field_size = 0;
+        int field_is_floating = 0;
+        
+        int member_size = 0;
+        int member_elem_size = 0;
+        int member_is_array = 0;
+        
+        if (tok.kind == TOK_DOT) {
+        
+            int member_offset = 0;
+            int member_is_floating = 0;
+            
+            const char *member_start = tok.start;
+            const char *member_caret = tok.caret;
+            
+            unsigned long member_line = get_line_number ();
+            
+            if (!parse_designated_member_path (object_size, owner_tag_name, &member_offset, &member_size, &member_elem_size, &member_is_array, &member_is_floating, &member_line, &member_start, &member_caret)) {
+            
+                skip_initializer ();
+                break;
+            
+            }
+            
+            if (!aggregate_field_index_from_offset (fields, field_count, member_offset, &field_index, &field_offset)) {
+            
+                report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "member designator does not name a directly initializable field");
+                
+                skip_initializer ();
+                break;
+            
+            }
+            
+            field_size = member_size;
+            field_is_floating = member_is_floating;
+        
+        } else {
+        
+            while (field_index < field_count && fields[field_index] < 0) {
+                field_offset += -fields[field_index++];
+            }
+            
+            if (field_index >= field_count) {
+            
+                skip_initializer ();
+                break;
+            
+            }
+            
+            field_size = fields[field_index];
+        
+        }
+        
+        if (member_is_array && member_elem_size == (DATA_CHAR & 0x1f) && is_string_token ()) {
+        
+            int64_s string_values[MAX_STRING_INIT_BYTES];
+            
+            int string_count = 0;
+            int string_index;
+            
+            int string_capacity = member_size / (DATA_CHAR & 0x1f);
+            parse_string_initializer_values (string_values, MAX_STRING_INIT_BYTES, &string_count);
+            
+            if (string_count > string_capacity) {
+            
+                report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "too many initializer values for character array member");
+                string_count = string_capacity;
+            
+            }
+            
+            for (string_index = 0; string_index < string_count && *init_count < max_inits; string_index++) {
+            
+                inits[*init_count].offset = base_offset + field_offset + string_index;
+                inits[*init_count].size = DATA_CHAR & 0x1f;
+                inits[*init_count].kind = LOCAL_INIT_CONST;
+                inits[*init_count].value = string_values[string_index];
+                inits[*init_count].symbol = 0;
+                (*init_count)++;
+            
+            }
+        
+        } else if (*init_count < max_inits) {
+        
+            inits[*init_count].offset = base_offset + field_offset;
+            inits[*init_count].size = field_size;
+            
+            parse_local_initializer_value (&inits[*init_count], field_size, field_is_floating);
+            (*init_count)++;
+        
+        } else {
+            skip_initializer ();
+        }
+        
+        explicit_count++;
+        
+        next_field_index = field_index + 1;
+        next_field_offset = field_offset + fields[field_index];
+        
+        if (!_accept (TOK_COMMA)) {
+            break;
+        }
+        
+        if (tok.kind == TOK_RBRACE) {
+            break;
+        }
+    
+    }
+    
+    expect (TOK_RBRACE, "}");
+    return explicit_count;
+
+}
+
 static void parse_local_aggregate_initializer_group (struct local_init *inits, int *init_count, int max_inits, long base_offset, const int *fields, int field_count, int *field_index, long *field_offset) {
 
     int braced = 0;
@@ -7860,7 +8170,7 @@ static void parse_local_aggregate_initializer_group (struct local_init *inits, i
 
 }
 
-static int parse_local_aggregate_initializer_values (struct local_init *inits, int *init_count, int max_inits, long base_offset, const int *fields, int field_count) {
+static int parse_local_aggregate_initializer_values (struct local_init *inits, int *init_count, int max_inits, long base_offset, const int *fields, int field_count, int object_size, const char *owner_tag_name) {
 
     int initial_init_count = *init_count;
     int provided_init_count;
@@ -7868,7 +8178,73 @@ static int parse_local_aggregate_initializer_values (struct local_init *inits, i
     int field_index = 0;
     long field_offset = 0;
     
-    parse_local_aggregate_initializer_group (inits, init_count, max_inits, base_offset, fields, field_count, &field_index, &field_offset);
+    if (tok.kind == TOK_LBRACE) {
+    
+        get_token ();
+        
+        if (tok.kind == TOK_DOT) {
+            return parse_local_designated_aggregate_initializer (inits, init_count, max_inits, base_offset, fields, field_count, object_size, owner_tag_name);
+        }
+        
+        /* Re-enter the ordinary braced initializer parser after consuming '{'. */
+        while (field_index < field_count) {
+        
+            int field_size;
+            
+            while (field_index < field_count && fields[field_index] < 0) {
+                field_offset += -fields[field_index++];
+            }
+            
+            if (field_index >= field_count || tok.kind == TOK_RBRACE) {
+                break;
+            }
+            
+            field_size = fields[field_index];
+            
+            if (tok.kind == TOK_LBRACE) {
+                parse_local_aggregate_initializer_group (inits, init_count, max_inits, base_offset, fields, field_count, &field_index, &field_offset);
+            } else if (*init_count < max_inits) {
+            
+                if (is_string_token ()) {
+                    parse_local_string_field_initializer (inits, init_count, max_inits, base_offset + field_offset, field_size);
+                } else {
+                
+                    inits[*init_count].offset = base_offset + field_offset;
+                    inits[*init_count].size = field_size;
+                    
+                    parse_local_initializer_value (&inits[*init_count], 0, 0);
+                    (*init_count)++;
+                
+                }
+                
+                field_offset += field_size;
+                field_index++;
+            
+            } else {
+            
+                skip_initializer ();
+                
+                field_offset += field_size;
+                field_index++;
+            
+            }
+            
+            if (!_accept (TOK_COMMA)) {
+                break;
+            }
+            
+            if (tok.kind == TOK_RBRACE) {
+                break;
+            }
+        
+        }
+        
+        expect (TOK_RBRACE, "}");
+    
+    } else {
+        parse_local_aggregate_initializer_group (inits, init_count, max_inits, base_offset, fields, field_count, &field_index, &field_offset);
+    }
+    
     provided_init_count = *init_count - initial_init_count;
     
     while (field_index < field_count) {
@@ -7990,6 +8366,7 @@ static void ensure_block_stack_allocated (long block_stack_start, long *block_st
 
 }
 
+static void parse_global_designated_initializer_values (int64_s *values, char **symbols, int max_values, int *count, int object_size, const char *owner_tag_name, const int *fields, int field_count);
 static void parse_global_initializer_values (int64_s *values, char **symbols, int max_values, int *count);
 
 static int aggregate_initializer_value_field_count (const int *field_sizes, int field_count);
@@ -8298,7 +8675,9 @@ static void parse_block (void) {
                                 int saved_accept_symbol_addresses = global_initializer_accept_symbol_addresses;
                                 global_initializer_accept_symbol_addresses = declarator_is_pointer || parsed_type_is_aggregate || declarator_has_array || global_initializer_cast_before_address_now ();
                                 
-                                if (declarator_has_array && !declarator_is_pointer && parsed_type_is_aggregate && parsed_field_count > 0 && tok.kind == TOK_LBRACE) {
+                                if (!declarator_has_array && !declarator_is_pointer && parsed_type_is_aggregate && tok.kind == TOK_LBRACE) {
+                                    parse_global_designated_initializer_values (init_values, init_symbols, MAX_AGG_FIELDS, &init_value_count, parsed_type_size, parsed_type_tag_name, object_fields, object_field_count);
+                                } else if (declarator_has_array && !declarator_is_pointer && parsed_type_is_aggregate && parsed_field_count > 0 && tok.kind == TOK_LBRACE) {
                                     parse_global_initializer_values_padded_elements (init_values, init_symbols, MAX_AGG_FIELDS, &init_value_count, aggregate_initializer_value_field_count (parsed_field_sizes, parsed_field_count));
                                 } else {
                                     parse_global_initializer_values (init_values, init_symbols, MAX_AGG_FIELDS, &init_value_count);
@@ -8464,7 +8843,7 @@ static void parse_block (void) {
                     } else if (object_is_auto && !declarator_is_pointer && (parsed_type_is_aggregate || declarator_has_array) && tok.kind == TOK_LBRACE) {
                     
                         int first_init_index = init_count;
-                        int provided_init_count = parse_local_aggregate_initializer_values (inits, &init_count, MAX_LOCAL_INITS, object_offset, object_fields, object_field_count);
+                        int provided_init_count = parse_local_aggregate_initializer_values (inits, &init_count, MAX_LOCAL_INITS, object_offset, object_fields, object_field_count, object_size, parsed_type_tag_name);
                         int expected_init_count = aggregate_initializer_value_field_count (parsed_field_sizes, parsed_field_count);
                         int zero_initializer = provided_init_count == 1 && first_init_index < init_count && inits[first_init_index].kind == LOCAL_INIT_CONST && !inits[first_init_index].symbol && inits[first_init_index].value.low == 0 && inits[first_init_index].value.high == 0;
                         
@@ -37388,6 +37767,175 @@ static void parse_global_initializer_values_padded_elements (int64_s *values, ch
 
 }
 
+static int aggregate_value_index_from_offset (const int *fields, int field_count, int wanted_offset) {
+
+    int offset = 0;
+    int value_index = 0;
+    
+    int i;
+    
+    for (i = 0; i < field_count; i++) {
+    
+        int size = fields[i];
+        
+        if (size < 0) {
+        
+            offset += -size;
+            continue;
+        
+        }
+        
+        if (offset == wanted_offset) {
+            return value_index;
+        }
+        
+        offset += size;
+        value_index++;
+    
+    }
+    
+    return -1;
+
+}
+
+static int aggregate_value_field_count (const int *fields, int field_count) {
+
+    int count = 0, i;
+    
+    for (i = 0; i < field_count; i++) {
+    
+        if (fields[i] > 0) {
+            count++;
+        }
+    
+    }
+    
+    return count;
+
+}
+
+static void parse_global_designated_initializer_values (int64_s *values, char **symbols, int max_values, int *count, int object_size, const char *owner_tag_name, const int *fields, int field_count) {
+
+    int field_values = aggregate_value_field_count (fields, field_count);
+    int next_value_index = 0, i;
+    
+    if (field_values > max_values) {
+        field_values = max_values;
+    }
+    
+    for (i = 0; i < field_values; i++) {
+    
+        values[i].low = 0;
+        values[i].high = 0;
+        
+        if (symbols) {
+            symbols[i] = 0;
+        }
+    
+    }
+    
+    *count = field_values;
+    expect (TOK_LBRACE, "{");
+    
+    while (tok.kind != TOK_EOF && tok.kind != TOK_RBRACE) {
+    
+        int value_index = next_value_index;
+        
+        int member_size = 0;
+        int member_elem_size = 0;
+        int member_is_array = 0;
+        
+        if (tok.kind == TOK_DOT) {
+        
+            int member_offset = 0;
+            int member_is_floating = 0;
+            
+            const char *member_start = tok.start;
+            const char *member_caret = tok.caret;
+            
+            unsigned long member_line = get_line_number ();
+            
+            if (!parse_designated_member_path (object_size, owner_tag_name, &member_offset, &member_size, &member_elem_size, &member_is_array, &member_is_floating, &member_line, &member_start, &member_caret)) {
+            
+                skip_initializer ();
+                break;
+            
+            }
+            
+            value_index = aggregate_value_index_from_offset (fields, field_count, member_offset);
+            
+            if (value_index < 0 || value_index >= field_values) {
+            
+                report_line_at (get_filename (), member_line, REPORT_ERROR, member_start, member_caret, "member designator does not name a directly initializable field");
+                
+                skip_initializer ();
+                break;
+            
+            }
+        
+        }
+        
+        if (value_index >= 0 && value_index < field_values) {
+        
+            int parsed_count = 0;
+            
+            if (member_is_array && member_elem_size == (DATA_CHAR & 0x1f) && is_string_token ()) {
+            
+                int64_s string_values[MAX_STRING_INIT_BYTES];
+                
+                int string_count = 0;
+                int string_index;
+                
+                int string_capacity = member_size / (DATA_CHAR & 0x1f);
+                parse_string_initializer_values (string_values, MAX_STRING_INIT_BYTES, &string_count);
+                
+                if (string_count > string_capacity) {
+                
+                    report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "too many initializer values for character array member");
+                    string_count = string_capacity;
+                
+                }
+                
+                for (string_index = 0; string_index < string_count && value_index + string_index < field_values; string_index++) {
+                
+                    values[value_index + string_index] = string_values[string_index];
+                    
+                    if (symbols) {
+                        symbols[value_index + string_index] = 0;
+                    }
+                
+                }
+                
+                parsed_count = string_capacity;
+            
+            } else {
+                parse_global_initializer_values (values + value_index, symbols ? symbols + value_index : 0, field_values - value_index, &parsed_count);
+            }
+            
+            if (parsed_count <= 0) {
+                parsed_count = 1;
+            }
+            
+            next_value_index = value_index + parsed_count;
+        
+        } else {
+            skip_initializer ();
+        }
+        
+        if (!_accept (TOK_COMMA)) {
+            break;
+        }
+        
+        if (tok.kind == TOK_RBRACE) {
+            break;
+        }
+    
+    }
+    
+    expect (TOK_RBRACE, "}");
+
+}
+
 static const char *global_initializer_symbol_name_now (void) {
 
     struct local_symbol *local;
@@ -38539,7 +39087,9 @@ static void parse_external_after_type (void) {
                     int saved_accept_symbol_addresses = global_initializer_accept_symbol_addresses;
                     global_initializer_accept_symbol_addresses = declarator_is_pointer || declarator_has_function || parsed_type_is_aggregate || declarator_has_array || global_initializer_cast_before_address_now ();
                     
-                    if (declarator_has_array && !declarator_is_pointer && parsed_type_is_aggregate && parsed_field_count > 0 && tok.kind == TOK_LBRACE) {
+                    if (!declarator_has_array && !declarator_is_pointer && parsed_type_is_aggregate && tok.kind == TOK_LBRACE) {
+                        parse_global_designated_initializer_values (init_values, init_symbols, MAX_GLOBAL_INIT_FIELDS, &init_value_count, parsed_type_size, parsed_type_tag_name, object_fields, object_field_count);
+                    } else if (declarator_has_array && !declarator_is_pointer && parsed_type_is_aggregate && parsed_field_count > 0 && tok.kind == TOK_LBRACE) {
                         parse_global_initializer_values_padded_elements (init_values, init_symbols, MAX_GLOBAL_INIT_FIELDS, &init_value_count, aggregate_initializer_value_field_count (parsed_field_sizes, parsed_field_count));
                     } else {
                         parse_global_initializer_values (init_values, init_symbols, MAX_GLOBAL_INIT_FIELDS, &init_value_count);