From 35dc501a40bf3ab598f6a89db7b1164b543d18bc Mon Sep 17 00:00:00 2001 From: Robert Pengelly Date: Fri, 21 Aug 2026 02:27:17 +0100 Subject: [PATCH] Added __attribute__ support --- amd64.c | 4 +- i386.c | 4 +- parse.c | 307 +++++++++++++++++++++++++++++++++++++++++++++----------- token.c | 2 + token.h | 1 + 5 files changed, 256 insertions(+), 62 deletions(-) diff --git a/amd64.c b/amd64.c index e6f9613..2c6572f 100644 --- a/amd64.c +++ b/amd64.c @@ -37154,7 +37154,7 @@ 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)'"); + report_line_at (get_filename (), ret_line, REPORT_WARNING, ret_start, ret_caret, "return in function declared 'noreturn'"); } } @@ -37736,7 +37736,7 @@ static void parse_function_body (const char *name, int storage_class, int is_inl 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"); + report_line_at (function_filename_copy ? function_filename_copy : get_filename (), function_line, REPORT_WARNING, function_start, function_caret, "function declared 'noreturn' may return"); } } else if (!current_function_is_void && !current_function_has_return_statement && !statement_ends_control_flow) { diff --git a/i386.c b/i386.c index b4388e6..76086a6 100644 --- a/i386.c +++ b/i386.c @@ -34607,7 +34607,7 @@ 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)'"); + report_line_at (get_filename (), ret_line, REPORT_WARNING, ret_start, ret_caret, "return in function declared 'noreturn'"); } } @@ -35178,7 +35178,7 @@ static void parse_function_body (const char *name, int storage_class, int is_inl 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"); + report_line_at (function_filename_copy ? function_filename_copy : get_filename (), function_line, REPORT_WARNING, function_start, function_caret, "function declared 'noreturn' may return"); } } else if (!current_function_is_void && !current_function_has_return_statement && !statement_ends_control_flow) { diff --git a/parse.c b/parse.c index cddb837..a46fc8b 100644 --- a/parse.c +++ b/parse.c @@ -402,10 +402,115 @@ static void parse_enum_body (void) { } +static int attribute_name_is (const char *name, const char *wanted) { + + size_t len; + + if (!name || !wanted) { + return 0; + } + + if (strcmp (name, wanted) == 0) { + return 1; + } + + len = strlen (wanted); + return strlen (name) == len + 4 && name[0] == '_' && name[1] == '_' && strncmp (name + 2, wanted, len) == 0 && name[len + 2] == '_' && name[len + 3] == '_'; + +} + +static void skip_attribute_arguments (void) { + + int depth; + + if (tok.kind != TOK_LPAREN) { + return; + } + + depth = 0; + + do { + + if (tok.kind == TOK_LPAREN) { + depth++; + } else if (tok.kind == TOK_RPAREN) { + depth--; + } + + get_token (); + + } while (tok.kind != TOK_EOF && depth > 0); + +} + +static void apply_decl_attribute (const char *spelling, int is_gnu) { + + if (attribute_name_is (spelling, "dllexport")) { + + if (parsed_dllexport) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "duplicate '%s(dllexport)'", is_gnu ? "__attribute__" : "__declspec"); + } else if (parsed_dllimport) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "'dllexport' and 'dllimport' cannot both be specified"); + } else { + + declarator_dllexport = 1; + parsed_dllexport = 1; + + } + + } else if (attribute_name_is (spelling, "dllimport")) { + + if (parsed_dllimport) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "duplicate '%s(dllimport)'", is_gnu ? "__attribute__" : "__declspec"); + } else if (parsed_dllexport) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "'dllexport' and 'dllimport' cannot both be specified"); + } else { + + declarator_dllimport = 1; + parsed_dllimport = 1; + + } + + } else if (attribute_name_is (spelling, "packed")) { + + if (parsed_type_is_packed) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "duplicate '%s(packed)'", is_gnu ? "__attribute__" : "__declspec"); + } else { + parsed_type_is_packed = 1; + } + + } else if (attribute_name_is (spelling, "noreturn")) { + + if (parsed_noreturn || declarator_noreturn) { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "duplicate '%s(noreturn)'", is_gnu ? "__attribute__" : "__declspec"); + } else { + + declarator_noreturn = 1; + parsed_noreturn = 1; + + } + + } else if (is_gnu && attribute_name_is (spelling, "stdcall")) { + + if (parsed_calling_convention == TOK_EOF && declarator_calling_convention == TOK_EOF) { + + parsed_calling_convention = TOK_STDCALL; + declarator_calling_convention = TOK_STDCALL; + + } + + } else if (is_gnu && (attribute_name_is (spelling, "cdecl") || attribute_name_is (spelling, "unused") || attribute_name_is (spelling, "used"))) { + /* Accepted compatibility attributes. They require no code generation. */ + } else { + report_line_at (get_filename (), get_line_number (), REPORT_WARNING, tok.report_start, tok.report_caret, "unsupported '%s' attribute '%s' ignored", is_gnu ? "__attribute__" : "__declspec", spelling); + } + +} + static void parse_decl_modifier (void) { if (tok.kind == TOK_STDCALL) { - + if (parsed_calling_convention == TOK_EOF && declarator_calling_convention == TOK_EOF) { parsed_calling_convention = TOK_STDCALL; } @@ -415,82 +520,105 @@ static void parse_decl_modifier (void) { } - if (tok.kind == TOK_DECLSPEC) { + if (tok.kind == TOK_ATTRIBUTE) { - const char *declspec_start = tok.report_start; - const char *declspec_caret = tok.report_caret; + const char *attribute_start = tok.report_start; + const char *attribute_caret = tok.report_caret; get_token (); if (!_accept (TOK_LPAREN)) { - report_line_at (get_filename (), get_line_number (), REPORT_ERROR, declspec_start, declspec_caret, "expected '(' after '__declspec'"); + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, attribute_start, attribute_caret, "expected '((' after '__attribute__'"); return; } - for (;;) { + if (!_accept (TOK_LPAREN)) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, attribute_start, attribute_caret, "expected '((' after '__attribute__'"); + + expect (TOK_RPAREN, ")"); + return; + } + + while (tok.kind != TOK_EOF && tok.kind != TOK_RPAREN) { + + char *name; + if (tok.kind != TOK_IDENT || !tok.ident) { - report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "expected attribute in '__declspec'"); - } else if (strcmp (tok.ident, "dllexport") == 0) { - if (parsed_dllexport) { - report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "duplicate '__declspec(dllexport)'"); - } else if (parsed_dllimport) { - report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "'__declspec(dllexport)' and '__declspec(dllimport)' cannot both be specified"); - } else { - - declarator_dllexport = 1; - parsed_dllexport = 1; - - } - + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "expected attribute in '__attribute__'"); get_token (); - } else if (strcmp (tok.ident, "dllimport") == 0) { + } else { - if (parsed_dllimport) { - report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "duplicate '__declspec(dllimport)'"); - } else if (parsed_dllexport) { - report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "'__declspec(dllexport)' and '__declspec(dllimport)' cannot both be specified"); - } else { + name = xstrdup (tok.ident); - declarator_dllimport = 1; - parsed_dllimport = 1; + apply_decl_attribute (name, 1); + get_token (); + if (tok.kind == TOK_LPAREN) { + skip_attribute_arguments (); } - get_token (); - - } else if (strcmp (tok.ident, "packed") == 0) { + free (name); - if (parsed_type_is_packed) { - report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "duplicate '__declspec(packed)'"); - } else { - parsed_type_is_packed = 1; - } - - get_token (); + } - } else if (strcmp (tok.ident, "noreturn") == 0) { + if (!_accept (TOK_COMMA)) { + break; + } - 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 (); + if (tok.kind == TOK_RPAREN) { - } else { + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "expected attribute after ',' in '__attribute__'"); + break; - report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "unsupported '__declspec' attribute '%s'", tok.ident); + } + + } + + expect (TOK_RPAREN, ")"); + expect (TOK_RPAREN, ")"); + + return; + + } + + if (tok.kind == TOK_DECLSPEC) { + + const char *declspec_start = tok.report_start; + const char *declspec_caret = tok.report_caret; + + get_token (); + + if (!_accept (TOK_LPAREN)) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, declspec_start, declspec_caret, "expected '(' after '__declspec'"); + return; + + } + + for (;;) { + + char *name; + + if (tok.kind != TOK_IDENT || !tok.ident) { + + report_line_at (get_filename (), get_line_number (), REPORT_ERROR, tok.report_start, tok.report_caret, "expected attribute in '__declspec'"); get_token (); + } else { + + name = xstrdup (tok.ident); + + apply_decl_attribute (name, 0); + get_token (); + + free (name); + } if (!_accept (TOK_COMMA)) { @@ -515,7 +643,7 @@ static void parse_decl_modifier (void) { void parse_declarator_inner (char **out_name) { - while (tok.kind == TOK_STDCALL || tok.kind == TOK_DECLSPEC) { + while (tok.kind == TOK_STDCALL || tok.kind == TOK_DECLSPEC || tok.kind == TOK_ATTRIBUTE) { if (tok.kind == TOK_STDCALL) { @@ -538,7 +666,7 @@ void parse_declarator_inner (char **out_name) { get_token (); - while (tok.kind == TOK_CONST || tok.kind == TOK_VOLATILE || tok.kind == TOK_RESTRICT || tok.kind == TOK_STDCALL || tok.kind == TOK_DECLSPEC) { + while (tok.kind == TOK_CONST || tok.kind == TOK_VOLATILE || tok.kind == TOK_RESTRICT || tok.kind == TOK_STDCALL || tok.kind == TOK_DECLSPEC || tok.kind == TOK_ATTRIBUTE) { if (tok.kind == TOK_STDCALL) { @@ -548,7 +676,7 @@ void parse_declarator_inner (char **out_name) { get_token (); - } else if (tok.kind == TOK_DECLSPEC) { + } else if (tok.kind == TOK_DECLSPEC || tok.kind == TOK_ATTRIBUTE) { parse_decl_modifier (); } else { get_token (); @@ -559,6 +687,10 @@ void parse_declarator_inner (char **out_name) { } parse_direct_declarator (out_name); + + while (tok.kind == TOK_DECLSPEC || tok.kind == TOK_ATTRIBUTE) { + parse_decl_modifier (); + } } @@ -1860,7 +1992,7 @@ int is_type_start (enum token_kind k) { return 0; case TOK_AUTO: case TOK_REGISTER: case TOK_STATIC: - case TOK_DECLSPEC: case TOK_STDCALL: + case TOK_ATTRIBUTE: case TOK_DECLSPEC: case TOK_STDCALL: case TOK_EXTERN: case TOK_TYPEDEF: case TOK_INLINE: case TOK_CONST: case TOK_VOLATILE: case TOK_RESTRICT: case TOK_SIGNED: case TOK_UNSIGNED: @@ -2531,7 +2663,7 @@ void parse_type_spec (void) { tok.kind == TOK_SHORT || tok.kind == TOK_LONG || tok.kind == TOK_CHAR || tok.kind == TOK_INT || tok.kind == TOK_VOID || tok.kind == TOK_FLOAT || tok.kind == TOK_DOUBLE || tok.kind == TOK_INLINE || tok.kind == TOK_RESTRICT || - tok.kind == TOK_DECLSPEC || tok.kind == TOK_STDCALL || token_is_ms_int_type_name ()) { + tok.kind == TOK_DECLSPEC || tok.kind == TOK_ATTRIBUTE || tok.kind == TOK_STDCALL || token_is_ms_int_type_name ()) { saw = 1; @@ -2542,7 +2674,7 @@ void parse_type_spec (void) { saw_real_type = 1; } - if (tok.kind == TOK_STDCALL || tok.kind == TOK_DECLSPEC) { + if (tok.kind == TOK_STDCALL || tok.kind == TOK_DECLSPEC || tok.kind == TOK_ATTRIBUTE) { parse_decl_modifier (); continue; @@ -2800,7 +2932,7 @@ void parse_type_spec (void) { clear_parsed_fields (); get_token (); - while (tok.kind == TOK_DECLSPEC) { + while (tok.kind == TOK_DECLSPEC || tok.kind == TOK_ATTRIBUTE) { parse_decl_modifier (); @@ -2822,7 +2954,7 @@ void parse_type_spec (void) { } - while (tok.kind == TOK_DECLSPEC) { + while (tok.kind == TOK_DECLSPEC || tok.kind == TOK_ATTRIBUTE) { parse_decl_modifier (); @@ -3043,6 +3175,65 @@ void parse_type_spec (void) { expect (TOK_RBRACE, "}"); + /* + * GNU permits type attributes after the closing brace, e.g. + * + * struct S { char c; int i; } __attribute__((packed)); + * + * Member offsets have already been accumulated by this point, so + * if a trailing packed attribute changes the type to packed we + * must compact the direct members before publishing the layout. + */ + { + + int was_packed = aggregate_is_packed; + + while (tok.kind == TOK_ATTRIBUTE || tok.kind == TOK_DECLSPEC) { + + parse_decl_modifier (); + + if (parsed_type_is_packed) { + aggregate_is_packed = 1; + } + + } + + if (!was_packed && aggregate_is_packed && !is_union) { + + int packed_size = 0; + int mi; + int dst = 0; + + for (mi = member_info_start; mi < member_info_count; mi++) { + + if (member_infos[mi].owner_size == 0) { + + member_infos[mi].offset = packed_size; + packed_size += member_infos[mi].size; + + member_infos[mi].is_packed = 1; + + } + + } + + aggregate_size = packed_size; + + /* Padding entries are negative; packed layout has none. */ + for (mi = 0; mi < aggregate_field_count; mi++) { + + if (aggregate_fields[mi] > 0) { + aggregate_fields[dst++] = aggregate_fields[mi]; + } + + } + + aggregate_field_count = dst; + + } + + } + if (aggregate_size <= 0) { aggregate_size = DATA_CHAR & 0x1f; } diff --git a/token.c b/token.c index beafe20..96b0c9f 100755 --- a/token.c +++ b/token.c @@ -1067,6 +1067,8 @@ static int find_kind (const char *start, const char *caret, const char *p) { /* Compiler Specific keywords */ { "__scc_builtin_va_arg", -1, VERSION, TOK_SCC_BUILTIN_VA_ARG }, + { "__attribute__", -1, VERSION, TOK_ATTRIBUTE }, + { "__attribute", -1, VERSION, TOK_ATTRIBUTE }, { "__declspec", -1, VERSION, TOK_DECLSPEC }, { "__stdcall", -1, VERSION, TOK_STDCALL }, diff --git a/token.h b/token.h index 5812b03..3386227 100755 --- a/token.h +++ b/token.h @@ -67,6 +67,7 @@ enum token_kind { /* Start of keywords. */ TOK_ASM = 256, + TOK_ATTRIBUTE, TOK_AUTO, TOK_BREAK, TOK_CASE, -- 2.34.1