Fixed assertion on C/gnu99/attributes5.c
[cparser] / attribute.c
1 /*
2  * This file is part of cparser.
3  * Copyright (C) 2007-2008 Matthias Braun <matze@braunis.de>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18  * 02111-1307, USA.
19  */
20 #include <config.h>
21
22 #include <assert.h>
23 #include "diagnostic.h"
24 #include "warning.h"
25 #include "attribute_t.h"
26 #include "symbol_t.h"
27 #include "type_t.h"
28
29 static const char *const attribute_names[ATTRIBUTE_LAST+1] = {
30         [ATTRIBUTE_GNU_CONST]                  = "const",
31         [ATTRIBUTE_GNU_VOLATILE]               = "volatile",
32         [ATTRIBUTE_GNU_CDECL]                  = "cdecl",
33         [ATTRIBUTE_GNU_STDCALL]                = "stdcall",
34         [ATTRIBUTE_GNU_FASTCALL]               = "fastcall",
35         [ATTRIBUTE_GNU_DEPRECATED]             = "deprecated",
36         [ATTRIBUTE_GNU_NOINLINE]               = "noinline",
37         [ATTRIBUTE_GNU_RETURNS_TWICE]          = "returns_twice",
38         [ATTRIBUTE_GNU_NORETURN]               = "noreturn",
39         [ATTRIBUTE_GNU_NAKED]                  = "naked",
40         [ATTRIBUTE_GNU_PURE]                   = "pure",
41         [ATTRIBUTE_GNU_ALWAYS_INLINE]          = "always_inline",
42         [ATTRIBUTE_GNU_MALLOC]                 = "malloc",
43         [ATTRIBUTE_GNU_WEAK]                   = "weak",
44         [ATTRIBUTE_GNU_CONSTRUCTOR]            = "constructor",
45         [ATTRIBUTE_GNU_DESTRUCTOR]             = "destructor",
46         [ATTRIBUTE_GNU_NOTHROW]                = "nothrow",
47         [ATTRIBUTE_GNU_TRANSPARENT_UNION]      = "transparent_union",
48         [ATTRIBUTE_GNU_COMMON]                 = "common",
49         [ATTRIBUTE_GNU_NOCOMMON]               = "nocommon",
50         [ATTRIBUTE_GNU_PACKED]                 = "packed",
51         [ATTRIBUTE_GNU_SHARED]                 = "shared",
52         [ATTRIBUTE_GNU_NOTSHARED]              = "notshared",
53         [ATTRIBUTE_GNU_USED]                   = "used",
54         [ATTRIBUTE_GNU_UNUSED]                 = "unused",
55         [ATTRIBUTE_GNU_NO_INSTRUMENT_FUNCTION] = "no_instrument_function",
56         [ATTRIBUTE_GNU_WARN_UNUSED_RESULT]     = "warn_unused_result",
57         [ATTRIBUTE_GNU_LONGCALL]               = "longcall",
58         [ATTRIBUTE_GNU_SHORTCALL]              = "shortcall",
59         [ATTRIBUTE_GNU_LONG_CALL]              = "long_call",
60         [ATTRIBUTE_GNU_SHORT_CALL]             = "short_call",
61         [ATTRIBUTE_GNU_FUNCTION_VECTOR]        = "function_vector",
62         [ATTRIBUTE_GNU_INTERRUPT]              = "interrupt",
63         [ATTRIBUTE_GNU_INTERRUPT_HANDLER]      = "interrupt_handler",
64         [ATTRIBUTE_GNU_NMI_HANDLER]            = "nmi_handler",
65         [ATTRIBUTE_GNU_NESTING]                = "nesting",
66         [ATTRIBUTE_GNU_NEAR]                   = "near",
67         [ATTRIBUTE_GNU_FAR]                    = "far",
68         [ATTRIBUTE_GNU_SIGNAL]                 = "signal",
69         [ATTRIBUTE_GNU_EIGTHBIT_DATA]          = "eightbit_data",
70         [ATTRIBUTE_GNU_TINY_DATA]              = "tiny_data",
71         [ATTRIBUTE_GNU_SAVEALL]                = "saveall",
72         [ATTRIBUTE_GNU_FLATTEN]                = "flatten",
73         [ATTRIBUTE_GNU_SSEREGPARM]             = "sseregparm",
74         [ATTRIBUTE_GNU_EXTERNALLY_VISIBLE]     = "externally_visible",
75         [ATTRIBUTE_GNU_MAY_ALIAS]              = "may_alias",
76         [ATTRIBUTE_GNU_MS_STRUCT]              = "ms_struct",
77         [ATTRIBUTE_GNU_GCC_STRUCT]             = "gcc_struct",
78         [ATTRIBUTE_GNU_DLLIMPORT]              = "dllimport",
79         [ATTRIBUTE_GNU_DLLEXPORT]              = "dllexport",
80         [ATTRIBUTE_GNU_ALIGNED]                = "aligned",
81         [ATTRIBUTE_GNU_ALIAS]                  = "alias",
82         [ATTRIBUTE_GNU_SECTION]                = "section",
83         [ATTRIBUTE_GNU_FORMAT]                 = "format",
84         [ATTRIBUTE_GNU_FORMAT_ARG]             = "format_arg",
85         [ATTRIBUTE_GNU_WEAKREF]                = "weakref",
86         [ATTRIBUTE_GNU_NONNULL]                = "nonnull",
87         [ATTRIBUTE_GNU_TLS_MODEL]              = "tls_model",
88         [ATTRIBUTE_GNU_VISIBILITY]             = "visibility",
89         [ATTRIBUTE_GNU_REGPARM]                = "regparm",
90         [ATTRIBUTE_GNU_MODE]                   = "mode",
91         [ATTRIBUTE_GNU_MODEL]                  = "model",
92         [ATTRIBUTE_GNU_TRAP_EXIT]              = "trap_exit",
93         [ATTRIBUTE_GNU_SP_SWITCH]              = "sp_switch",
94         [ATTRIBUTE_GNU_SENTINEL]               = "sentinel",
95
96         [ATTRIBUTE_MS_ALIGN]                   = "align",
97         [ATTRIBUTE_MS_ALLOCATE]                = "allocate",
98         [ATTRIBUTE_MS_DLLIMPORT]               = "dllimport",
99         [ATTRIBUTE_MS_DLLEXPORT]               = "dllexport",
100         [ATTRIBUTE_MS_NAKED]                   = "naked",
101         [ATTRIBUTE_MS_NOINLINE]                = "noinline",
102         [ATTRIBUTE_MS_RETURNS_TWICE]           = "returns_twice",
103         [ATTRIBUTE_MS_NORETURN]                = "noreturn",
104         [ATTRIBUTE_MS_NOTHROW]                 = "nothrow",
105         [ATTRIBUTE_MS_NOVTABLE]                = "novtable",
106         [ATTRIBUTE_MS_PROPERTY]                = "property",
107         [ATTRIBUTE_MS_SELECTANY]               = "selectany",
108         [ATTRIBUTE_MS_THREAD]                  = "thread",
109         [ATTRIBUTE_MS_UUID]                    = "uuid",
110         [ATTRIBUTE_MS_DEPRECATED]              = "deprecated",
111         [ATTRIBUTE_MS_RESTRICT]                = "restrict",
112         [ATTRIBUTE_MS_NOALIAS]                 = "noalias",
113 };
114
115 const char *get_attribute_name(attribute_kind_t kind)
116 {
117         assert(kind <= ATTRIBUTE_LAST);
118         return attribute_names[kind];
119 }
120
121 /**
122  * compare two string, ignoring double underscores on the second.
123  */
124 static int strcmp_underscore(const char *s1, const char *s2)
125 {
126         if (s2[0] == '_' && s2[1] == '_') {
127                 size_t len2 = strlen(s2);
128                 size_t len1 = strlen(s1);
129                 if (len1 == len2-4 && s2[len2-2] == '_' && s2[len2-1] == '_') {
130                         return strncmp(s1, s2+2, len2-4);
131                 }
132         }
133
134         return strcmp(s1, s2);
135 }
136
137 type_t *handle_attribute_mode(const attribute_t *attribute, type_t *orig_type)
138 {
139         type_t *type = skip_typeref(orig_type);
140
141         /* at least: byte, word, pointer, list of machine modes
142          * __XXX___ is interpreted as XXX */
143
144         /* This isn't really correct, the backend should provide a list of machine
145          * specific modes (according to gcc philosophy that is...) */
146         attribute_argument_t *arg        = attribute->a.arguments;
147         const char           *symbol_str = arg->v.symbol->string;
148         bool                  sign       = is_type_signed(type);
149         atomic_type_kind_t  akind;
150         if (strcmp_underscore("QI",   symbol_str) == 0 ||
151             strcmp_underscore("byte", symbol_str) == 0) {
152                 akind = sign ? ATOMIC_TYPE_CHAR : ATOMIC_TYPE_UCHAR;
153         } else if (strcmp_underscore("HI", symbol_str) == 0) {
154                 akind = sign ? ATOMIC_TYPE_SHORT : ATOMIC_TYPE_USHORT;
155         } else if (strcmp_underscore("SI",      symbol_str) == 0
156                 || strcmp_underscore("word",    symbol_str) == 0
157                 || strcmp_underscore("pointer", symbol_str) == 0) {
158                 akind = sign ? ATOMIC_TYPE_INT : ATOMIC_TYPE_UINT;
159         } else if (strcmp_underscore("DI", symbol_str) == 0) {
160                 akind = sign ? ATOMIC_TYPE_LONGLONG : ATOMIC_TYPE_ULONGLONG;
161         } else {
162                 if (warning.other)
163                         warningf(&attribute->source_position, "ignoring unknown mode '%s'",
164                                  symbol_str);
165                 return orig_type;
166         }
167
168         if (type->kind == TYPE_ATOMIC) {
169                 type_t *copy       = duplicate_type(type);
170                 copy->atomic.akind = akind;
171                 return identify_new_type(copy);
172         } else if (type->kind == TYPE_ENUM) {
173                 type_t *copy      = duplicate_type(type);
174                 copy->enumt.akind = akind;
175                 return identify_new_type(copy);
176         } else if (is_type_pointer(type)) {
177                 warningf(&attribute->source_position,
178                          "__attribute__((mode)) on pointers not implemented yet (ignored)");
179                 return type;
180         }
181
182         errorf(&attribute->source_position,
183                "__attribute__((mode)) only allowed on integer, enum or pointer type");
184         return orig_type;
185 }
186
187 static inline bool is_po2(unsigned x)
188 {
189         return (x & (x-1)) == 0;
190 }
191
192 static void handle_attribute_aligned(const attribute_t *attribute,
193                                      entity_t *entity)
194 {
195         int alignment = 32; /* TODO: fill in maximum useful alignment for
196                                                    target machine */
197         if (attribute->a.arguments) {
198                 attribute_argument_t *argument = attribute->a.arguments;
199                 alignment = fold_constant_to_int(argument->v.expression);
200         }
201
202         if (!is_po2(alignment)) {
203                 errorf(&attribute->source_position,
204                            "alignment must be a power of 2 but is %d\n",
205                            alignment);
206                 return;
207         }
208         if (alignment <= 0) {
209                 errorf(&attribute->source_position,
210                            "alignment must be bigger than 0 but is %d\n",
211                            alignment);
212                 return;
213         }
214
215         switch (entity->kind) {
216         DECLARATION_KIND_CASES
217                 entity->declaration.alignment = alignment;
218         case ENTITY_TYPEDEF:
219                 entity->typedefe.alignment = alignment;
220                 break;
221         case ENTITY_STRUCT:
222         case ENTITY_UNION:
223                 if (alignment > entity->compound.alignment) {
224                         entity->compound.alignment = alignment;
225                 }
226                 break;
227         default:
228                 if (warning.other) {
229                         warningf(&attribute->source_position,
230                                          "alignment attribute specification on '%S' ignored",
231                                          entity->base.symbol);
232                 }
233                 break;
234         }
235 }
236
237 static void warn_arguments(const attribute_t *attribute)
238 {
239         if (attribute->a.arguments == NULL)
240                 return;
241
242         if (warning.other) {
243                 warningf(&attribute->source_position,
244                                  "attribute '%s' needs no attributes",
245                                  get_attribute_name(attribute->kind));
246         }
247 }
248
249 static void handle_attribute_packed_e(const attribute_t *attribute,
250                                       entity_t *entity)
251 {
252 #if 0
253         if (entity->kind != ENTITY_STRUCT) {
254                 warningf(&attribute->source_position,
255                          "packed attribute on %s '%s' ignored",
256                                  get_entity_kind_name(entity->kind),
257                                  entity->base.symbol->string);
258                 return;
259         }
260 #endif
261
262         warn_arguments(attribute);
263         entity->compound.packed = true;
264 }
265
266 static void handle_attribute_packed(const attribute_t *attribute, type_t *type)
267 {
268         if (type->kind != TYPE_COMPOUND_STRUCT) {
269                 warningf(&attribute->source_position,
270                          "packed attribute on type '%T' ignored", type);
271                 return;
272         }
273
274         handle_attribute_packed_e(attribute, (entity_t*) type->compound.compound);
275 }
276
277 void handle_entity_attributes(const attribute_t *attributes, entity_t *entity)
278 {
279         if (entity->kind == ENTITY_TYPEDEF) {
280                 type_t *type = entity->typedefe.type;
281                 type = handle_type_attributes(attributes, type);
282                 entity->typedefe.type = type;
283         } else if (is_declaration(entity)) {
284                 type_t *type = entity->declaration.type;
285                 type = handle_type_attributes(attributes, type);
286                 entity->declaration.type = type;
287         }
288
289         decl_modifiers_t modifiers = 0;
290         const attribute_t *attribute = attributes;
291         for ( ; attribute != NULL; attribute = attribute->next) {
292                 switch(attribute->kind) {
293                 case ATTRIBUTE_GNU_CONST:         modifiers |= DM_CONST; break;
294                 case ATTRIBUTE_GNU_DEPRECATED:    modifiers |= DM_DEPRECATED; break;
295                 case ATTRIBUTE_GNU_NOINLINE:      modifiers |= DM_NOINLINE; break;
296                 case ATTRIBUTE_GNU_RETURNS_TWICE: modifiers |= DM_RETURNS_TWICE; break;
297                 case ATTRIBUTE_GNU_NORETURN:      modifiers |= DM_NORETURN; break;
298                 case ATTRIBUTE_GNU_NAKED:         modifiers |= DM_NAKED; break;
299                 case ATTRIBUTE_GNU_PURE:          modifiers |= DM_PURE; break;
300                 case ATTRIBUTE_GNU_ALWAYS_INLINE: modifiers |= DM_FORCEINLINE; break;
301                 case ATTRIBUTE_GNU_MALLOC:        modifiers |= DM_MALLOC; break;
302                 case ATTRIBUTE_GNU_CONSTRUCTOR:   modifiers |= DM_CONSTRUCTOR; break;
303                 case ATTRIBUTE_GNU_DESTRUCTOR:    modifiers |= DM_DESTRUCTOR; break;
304                 case ATTRIBUTE_GNU_NOTHROW:       modifiers |= DM_NOTHROW; break;
305                 case ATTRIBUTE_GNU_TRANSPARENT_UNION:
306                                                                                   modifiers |= DM_TRANSPARENT_UNION;
307                                                                                   break;
308                 case ATTRIBUTE_GNU_USED:          modifiers |= DM_USED; break;
309                 case ATTRIBUTE_GNU_UNUSED:        modifiers |= DM_UNUSED; break;
310                 case ATTRIBUTE_GNU_DLLIMPORT:     modifiers |= DM_DLLIMPORT; break;
311                 case ATTRIBUTE_GNU_DLLEXPORT:     modifiers |= DM_DLLEXPORT; break;
312
313                 case ATTRIBUTE_MS_ALLOCATE:      modifiers |= DM_MALLOC; break;
314                 case ATTRIBUTE_MS_DLLIMPORT:     modifiers |= DM_DLLIMPORT; break;
315                 case ATTRIBUTE_MS_DLLEXPORT:     modifiers |= DM_DLLEXPORT; break;
316                 case ATTRIBUTE_MS_NAKED:         modifiers |= DM_NAKED; break;
317                 case ATTRIBUTE_MS_NOINLINE:      modifiers |= DM_NOINLINE; break;
318                 case ATTRIBUTE_MS_RETURNS_TWICE: modifiers |= DM_RETURNS_TWICE; break;
319                 case ATTRIBUTE_MS_NORETURN:      modifiers |= DM_NORETURN; break;
320                 case ATTRIBUTE_MS_NOTHROW:       modifiers |= DM_NOTHROW; break;
321                 case ATTRIBUTE_MS_THREAD:        modifiers |= DM_THREAD; break;
322                 case ATTRIBUTE_MS_DEPRECATED:    modifiers |= DM_DEPRECATED; break;
323                 case ATTRIBUTE_MS_RESTRICT:      modifiers |= DM_RESTRICT; break;
324                 case ATTRIBUTE_MS_NOALIAS:       modifiers |= DM_NOALIAS; break;
325
326                 case ATTRIBUTE_GNU_PACKED:
327                         handle_attribute_packed_e(attribute, entity);
328                         break;
329
330                 case ATTRIBUTE_MS_ALIGN:
331                 case ATTRIBUTE_GNU_ALIGNED:
332                         handle_attribute_aligned(attribute, entity);
333                         break;
334                 default: break;
335                 }
336         }
337
338         if (modifiers != 0) {
339                 switch(entity->kind) {
340                 case ENTITY_TYPEDEF:
341                         entity->typedefe.modifiers |= modifiers;
342                         break;
343                 case ENTITY_UNION:
344                 case ENTITY_STRUCT:
345                         entity->compound.modifiers |= modifiers;
346                         break;
347                 case ENTITY_COMPOUND_MEMBER:
348                 case ENTITY_VARIABLE:
349                 case ENTITY_FUNCTION:
350                         entity->declaration.modifiers |= modifiers;
351                         break;
352                 default:
353                         /* TODO: warning */
354                         break;
355                 }
356         }
357 }
358
359 static type_t *change_calling_convention(type_t *type, cc_kind_t cconv)
360 {
361         if (is_typeref(type) || !is_type_function(type)) {
362                 return type;
363         }
364
365         if (type->function.calling_convention == cconv)
366                 return type;
367
368         type_t* new_type = duplicate_type(type);
369         new_type->function.calling_convention = cconv;
370         return identify_new_type(new_type);
371 }
372
373 type_t *handle_type_attributes(const attribute_t *attributes, type_t *type)
374 {
375         const attribute_t *attribute = attributes;
376         for ( ; attribute != NULL; attribute = attribute->next) {
377                 switch(attribute->kind) {
378                 case ATTRIBUTE_GNU_PACKED:
379                         handle_attribute_packed(attribute, type);
380                         break;
381                 case ATTRIBUTE_GNU_CDECL:
382                 case ATTRIBUTE_MS_CDECL:
383                         type = change_calling_convention(type, CC_CDECL);
384                         break;
385                 case ATTRIBUTE_MS_STDCALL:
386                 case ATTRIBUTE_GNU_STDCALL:
387                         type = change_calling_convention(type, CC_STDCALL);
388                         break;
389                 case ATTRIBUTE_MS_FASTCALL:
390                 case ATTRIBUTE_GNU_FASTCALL:
391                         type = change_calling_convention(type, CC_FASTCALL);
392                         break;
393                 case ATTRIBUTE_MS_THISCALL:
394                         type = change_calling_convention(type, CC_THISCALL);
395                         break;
396                 case ATTRIBUTE_GNU_MODE:
397                         type = handle_attribute_mode(attribute, type);
398                         break;
399                 default:
400                         break;
401                 }
402         }
403
404         return type;
405 }
406
407 const char *get_deprecated_string(const attribute_t *attribute)
408 {
409         for ( ; attribute != NULL; attribute = attribute->next) {
410                 if (attribute->kind != ATTRIBUTE_MS_DEPRECATED)
411                         continue;
412
413                 attribute_argument_t *argument = attribute->a.arguments;
414                 if (argument == NULL)
415                         return NULL;
416                 if (argument->kind != ATTRIBUTE_ARGUMENT_EXPRESSION)
417                         return NULL;
418                 expression_t *expression = argument->v.expression;
419                 if (expression->kind != EXPR_STRING_LITERAL)
420                         return NULL;
421                 return expression->string.value.begin;
422         }
423         return NULL;
424 }