rewrite of attribute handling
[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(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(const attribute_t *attribute,
250                                     entity_t *entity)
251 {
252         warn_arguments(attribute);
253
254         if (entity->kind == ENTITY_STRUCT) {
255                 entity->compound.packed = true;
256         } else if (warning.other) {
257                 warningf(&attribute->source_position,
258                          "packed attribute on %s ignored",
259                                  get_entity_kind_name(entity->kind));
260         }
261 }
262
263 void handle_entity_attributes(const attribute_t *attributes, entity_t *entity)
264 {
265         if (entity->kind == ENTITY_TYPEDEF) {
266                 type_t *type = entity->typedefe.type;
267                 type = handle_type_attributes(attributes, type);
268                 entity->typedefe.type = type;
269         } else if (is_declaration(entity)) {
270                 type_t *type = entity->declaration.type;
271                 type = handle_type_attributes(attributes, type);
272                 entity->declaration.type = type;
273         }
274
275         decl_modifiers_t modifiers = 0;
276         const attribute_t *attribute = attributes;
277         for ( ; attribute != NULL; attribute = attribute->next) {
278                 switch(attribute->kind) {
279                 case ATTRIBUTE_GNU_CONST:         modifiers |= DM_CONST; break;
280                 case ATTRIBUTE_GNU_DEPRECATED:    modifiers |= DM_DEPRECATED; break;
281                 case ATTRIBUTE_GNU_NOINLINE:      modifiers |= DM_NOINLINE; break;
282                 case ATTRIBUTE_GNU_RETURNS_TWICE: modifiers |= DM_RETURNS_TWICE; break;
283                 case ATTRIBUTE_GNU_NORETURN:      modifiers |= DM_NORETURN; break;
284                 case ATTRIBUTE_GNU_NAKED:         modifiers |= DM_NAKED; break;
285                 case ATTRIBUTE_GNU_PURE:          modifiers |= DM_PURE; break;
286                 case ATTRIBUTE_GNU_ALWAYS_INLINE: modifiers |= DM_FORCEINLINE; break;
287                 case ATTRIBUTE_GNU_MALLOC:        modifiers |= DM_MALLOC; break;
288                 case ATTRIBUTE_GNU_CONSTRUCTOR:   modifiers |= DM_CONSTRUCTOR; break;
289                 case ATTRIBUTE_GNU_DESTRUCTOR:    modifiers |= DM_DESTRUCTOR; break;
290                 case ATTRIBUTE_GNU_NOTHROW:       modifiers |= DM_NOTHROW; break;
291                 case ATTRIBUTE_GNU_TRANSPARENT_UNION:
292                                                                                   modifiers |= DM_TRANSPARENT_UNION;
293                                                                                   break;
294                 case ATTRIBUTE_GNU_USED:          modifiers |= DM_USED; break;
295                 case ATTRIBUTE_GNU_UNUSED:        modifiers |= DM_UNUSED; break;
296                 case ATTRIBUTE_GNU_DLLIMPORT:     modifiers |= DM_DLLIMPORT; break;
297                 case ATTRIBUTE_GNU_DLLEXPORT:     modifiers |= DM_DLLEXPORT; break;
298
299                 case ATTRIBUTE_MS_ALLOCATE:      modifiers |= DM_MALLOC; break;
300                 case ATTRIBUTE_MS_DLLIMPORT:     modifiers |= DM_DLLIMPORT; break;
301                 case ATTRIBUTE_MS_DLLEXPORT:     modifiers |= DM_DLLEXPORT; break;
302                 case ATTRIBUTE_MS_NAKED:         modifiers |= DM_NAKED; break;
303                 case ATTRIBUTE_MS_NOINLINE:      modifiers |= DM_NOINLINE; break;
304                 case ATTRIBUTE_MS_RETURNS_TWICE: modifiers |= DM_RETURNS_TWICE; break;
305                 case ATTRIBUTE_MS_NORETURN:      modifiers |= DM_NORETURN; break;
306                 case ATTRIBUTE_MS_NOTHROW:       modifiers |= DM_NOTHROW; break;
307                 case ATTRIBUTE_MS_THREAD:        modifiers |= DM_THREAD; break;
308                 case ATTRIBUTE_MS_DEPRECATED:    modifiers |= DM_DEPRECATED; break;
309                 case ATTRIBUTE_MS_RESTRICT:      modifiers |= DM_RESTRICT; break;
310                 case ATTRIBUTE_MS_NOALIAS:       modifiers |= DM_NOALIAS; break;
311
312                 case ATTRIBUTE_GNU_PACKED:
313                         handle_attribute_packed(attribute, entity);
314                         break;
315                 case ATTRIBUTE_MS_ALIGN:
316                 case ATTRIBUTE_GNU_ALIGNED:
317                         handle_attribute_aligned(attribute, entity);
318                         break;
319                 default: break;
320                 }
321         }
322
323         if (modifiers != 0) {
324                 switch(entity->kind) {
325                 case ENTITY_TYPEDEF:
326                         entity->typedefe.modifiers |= modifiers;
327                         break;
328                 case ENTITY_UNION:
329                 case ENTITY_STRUCT:
330                         entity->compound.modifiers |= modifiers;
331                         break;
332                 case ENTITY_COMPOUND_MEMBER:
333                 case ENTITY_VARIABLE:
334                 case ENTITY_FUNCTION:
335                         entity->declaration.modifiers |= modifiers;
336                         break;
337                 default:
338                         /* TODO: warning */
339                         break;
340                 }
341         }
342 }
343
344 static type_t *change_calling_convention(const source_position_t *pos,
345                                          type_t *type, cc_kind_t cconv)
346 {
347         if (!is_type_function(type)) {
348                 if (warning.other) {
349                         warningf(pos,
350                                  "Calling convention specified on non-function type '%T'",
351                                  type);
352                 }
353                 return type;
354         }
355
356         if (type->function.calling_convention == cconv)
357                 return type;
358
359         type_t* new_type = duplicate_type(type);
360         new_type->function.calling_convention = cconv;
361         return identify_new_type(new_type);
362 }
363
364 type_t *handle_type_attributes(const attribute_t *attributes, type_t *type)
365 {
366         const attribute_t *attribute = attributes;
367         for ( ; attribute != NULL; attribute = attribute->next) {
368                 switch(attribute->kind) {
369                 case ATTRIBUTE_GNU_CDECL:
370                 case ATTRIBUTE_MS_CDECL:
371                         type = change_calling_convention(&attribute->source_position,
372                                                          type, CC_CDECL);
373                         break;
374                 case ATTRIBUTE_MS_STDCALL:
375                 case ATTRIBUTE_GNU_STDCALL:
376                         type = change_calling_convention(&attribute->source_position,
377                                                          type, CC_STDCALL);
378                         break;
379                 case ATTRIBUTE_MS_FASTCALL:
380                 case ATTRIBUTE_GNU_FASTCALL:
381                         type = change_calling_convention(&attribute->source_position,
382                                                          type, CC_FASTCALL);
383                         break;
384                 case ATTRIBUTE_MS_THISCALL:
385                         type = change_calling_convention(&attribute->source_position,
386                                                          type, CC_THISCALL);
387                         break;
388                 case ATTRIBUTE_GNU_MODE:
389                         type = handle_attribute_mode(attribute, type);
390                         break;
391                 default:
392                         break;
393                 }
394         }
395
396         return type;
397 }
398
399 const char *get_deprecated_string(const attribute_t *attribute)
400 {
401         for ( ; attribute != NULL; attribute = attribute->next) {
402                 if (attribute->kind != ATTRIBUTE_MS_DEPRECATED)
403                         continue;
404
405                 attribute_argument_t *argument = attribute->a.arguments;
406                 if (argument == NULL)
407                         return NULL;
408                 if (argument->kind != ATTRIBUTE_ARGUMENT_EXPRESSION)
409                         return NULL;
410                 expression_t *expression = argument->v.expression;
411                 if (expression->kind != EXPR_STRING_LITERAL)
412                         return NULL;
413                 return expression->string.value.begin;
414         }
415         return NULL;
416 }