simple support for __attribute__((alias("symbol")))
[cparser] / attribute.c
1 /*
2  * This file is part of cparser.
3  * Copyright (C) 2012 Matthias Braun <matze@braunis.de>
4  */
5 #include <config.h>
6
7 #include <assert.h>
8
9 #include "adt/bitfiddle.h"
10 #include "adt/strutil.h"
11 #include "ast_t.h"
12 #include "diagnostic.h"
13 #include "warning.h"
14 #include "attribute_t.h"
15 #include "symbol_t.h"
16 #include "adt/error.h"
17 #include "entity_t.h"
18 #include "symbol_table.h"
19 #include "type_t.h"
20
21 static const char *const attribute_names[ATTRIBUTE_LAST+1] = {
22         [ATTRIBUTE_GNU_ALIAS]                  = "alias",
23         [ATTRIBUTE_GNU_ALIGNED]                = "aligned",
24         [ATTRIBUTE_GNU_ALWAYS_INLINE]          = "always_inline",
25         [ATTRIBUTE_GNU_CDECL]                  = "cdecl",
26         [ATTRIBUTE_GNU_COMMON]                 = "common",
27         [ATTRIBUTE_GNU_CONST]                  = "const",
28         [ATTRIBUTE_GNU_CONSTRUCTOR]            = "constructor",
29         [ATTRIBUTE_GNU_DEPRECATED]             = "deprecated",
30         [ATTRIBUTE_GNU_DESTRUCTOR]             = "destructor",
31         [ATTRIBUTE_GNU_DLLEXPORT]              = "dllexport",
32         [ATTRIBUTE_GNU_DLLIMPORT]              = "dllimport",
33         [ATTRIBUTE_GNU_EIGTHBIT_DATA]          = "eightbit_data",
34         [ATTRIBUTE_GNU_EXTERNALLY_VISIBLE]     = "externally_visible",
35         [ATTRIBUTE_GNU_FAR]                    = "far",
36         [ATTRIBUTE_GNU_FASTCALL]               = "fastcall",
37         [ATTRIBUTE_GNU_FLATTEN]                = "flatten",
38         [ATTRIBUTE_GNU_FORMAT_ARG]             = "format_arg",
39         [ATTRIBUTE_GNU_FORMAT]                 = "format",
40         [ATTRIBUTE_GNU_FUNCTION_VECTOR]        = "function_vector",
41         [ATTRIBUTE_GNU_GCC_STRUCT]             = "gcc_struct",
42         [ATTRIBUTE_GNU_INTERRUPT_HANDLER]      = "interrupt_handler",
43         [ATTRIBUTE_GNU_INTERRUPT]              = "interrupt",
44         [ATTRIBUTE_GNU_LEAF]                   = "leaf",
45         [ATTRIBUTE_GNU_LONGCALL]               = "longcall",
46         [ATTRIBUTE_GNU_LONG_CALL]              = "long_call",
47         [ATTRIBUTE_GNU_MALLOC]                 = "malloc",
48         [ATTRIBUTE_GNU_MAY_ALIAS]              = "may_alias",
49         [ATTRIBUTE_GNU_MODEL]                  = "model",
50         [ATTRIBUTE_GNU_MODE]                   = "mode",
51         [ATTRIBUTE_GNU_MS_STRUCT]              = "ms_struct",
52         [ATTRIBUTE_GNU_NAKED]                  = "naked",
53         [ATTRIBUTE_GNU_NEAR]                   = "near",
54         [ATTRIBUTE_GNU_NESTING]                = "nesting",
55         [ATTRIBUTE_GNU_NMI_HANDLER]            = "nmi_handler",
56         [ATTRIBUTE_GNU_NOCOMMON]               = "nocommon",
57         [ATTRIBUTE_GNU_NOINLINE]               = "noinline",
58         [ATTRIBUTE_GNU_NO_INSTRUMENT_FUNCTION] = "no_instrument_function",
59         [ATTRIBUTE_GNU_NONNULL]                = "nonnull",
60         [ATTRIBUTE_GNU_NORETURN]               = "noreturn",
61         [ATTRIBUTE_GNU_NOTHROW]                = "nothrow",
62         [ATTRIBUTE_GNU_NOTSHARED]              = "notshared",
63         [ATTRIBUTE_GNU_PACKED]                 = "packed",
64         [ATTRIBUTE_GNU_PURE]                   = "pure",
65         [ATTRIBUTE_GNU_REGPARM]                = "regparm",
66         [ATTRIBUTE_GNU_RETURNS_TWICE]          = "returns_twice",
67         [ATTRIBUTE_GNU_SAVEALL]                = "saveall",
68         [ATTRIBUTE_GNU_SECTION]                = "section",
69         [ATTRIBUTE_GNU_SENTINEL]               = "sentinel",
70         [ATTRIBUTE_GNU_SHARED]                 = "shared",
71         [ATTRIBUTE_GNU_SHORTCALL]              = "shortcall",
72         [ATTRIBUTE_GNU_SHORT_CALL]             = "short_call",
73         [ATTRIBUTE_GNU_SIGNAL]                 = "signal",
74         [ATTRIBUTE_GNU_SP_SWITCH]              = "sp_switch",
75         [ATTRIBUTE_GNU_SSEREGPARM]             = "sseregparm",
76         [ATTRIBUTE_GNU_STDCALL]                = "stdcall",
77         [ATTRIBUTE_GNU_TINY_DATA]              = "tiny_data",
78         [ATTRIBUTE_GNU_TLS_MODEL]              = "tls_model",
79         [ATTRIBUTE_GNU_TRANSPARENT_UNION]      = "transparent_union",
80         [ATTRIBUTE_GNU_TRAP_EXIT]              = "trap_exit",
81         [ATTRIBUTE_GNU_UNUSED]                 = "unused",
82         [ATTRIBUTE_GNU_USED]                   = "used",
83         [ATTRIBUTE_GNU_VISIBILITY]             = "visibility",
84         [ATTRIBUTE_GNU_VOLATILE]               = "volatile",
85         [ATTRIBUTE_GNU_WARN_UNUSED_RESULT]     = "warn_unused_result",
86         [ATTRIBUTE_GNU_WEAKREF]                = "weakref",
87         [ATTRIBUTE_GNU_WEAK]                   = "weak",
88
89         [ATTRIBUTE_MS_ALIGN]                   = "align",
90         [ATTRIBUTE_MS_ALLOCATE]                = "allocate",
91         [ATTRIBUTE_MS_DEPRECATED]              = "deprecated",
92         [ATTRIBUTE_MS_DLLEXPORT]               = "dllexport",
93         [ATTRIBUTE_MS_DLLIMPORT]               = "dllimport",
94         [ATTRIBUTE_MS_NAKED]                   = "naked",
95         [ATTRIBUTE_MS_NOALIAS]                 = "noalias",
96         [ATTRIBUTE_MS_NOINLINE]                = "noinline",
97         [ATTRIBUTE_MS_NORETURN]                = "noreturn",
98         [ATTRIBUTE_MS_NOTHROW]                 = "nothrow",
99         [ATTRIBUTE_MS_NOVTABLE]                = "novtable",
100         [ATTRIBUTE_MS_PROPERTY]                = "property",
101         [ATTRIBUTE_MS_RESTRICT]                = "restrict",
102         [ATTRIBUTE_MS_RETURNS_TWICE]           = "returns_twice",
103         [ATTRIBUTE_MS_SELECTANY]               = "selectany",
104         [ATTRIBUTE_MS_THREAD]                  = "thread",
105         [ATTRIBUTE_MS_UUID]                    = "uuid",
106 };
107
108 const char *get_attribute_name(attribute_kind_t kind)
109 {
110         assert(kind <= ATTRIBUTE_LAST);
111         return attribute_names[kind];
112 }
113
114 type_t *handle_attribute_mode(const attribute_t *attribute, type_t *orig_type)
115 {
116         type_t *type = skip_typeref(orig_type);
117
118         /* at least: byte, word, pointer, list of machine modes
119          * __XXX___ is interpreted as XXX */
120
121         /* This isn't really correct, the backend should provide a list of machine
122          * specific modes (according to gcc philosophy that is...) */
123         attribute_argument_t *arg = attribute->a.arguments;
124         if (arg == NULL) {
125                 errorf(&attribute->pos, "__attribute__((mode(X))) misses argument");
126                 return orig_type;
127         }
128
129         const char         *symbol_str = arg->v.symbol->string;
130         bool                sign       = is_type_signed(type);
131         atomic_type_kind_t  akind;
132         if (streq_underscore("QI",   symbol_str) ||
133             streq_underscore("byte", symbol_str)) {
134                 akind = sign ? ATOMIC_TYPE_CHAR : ATOMIC_TYPE_UCHAR;
135         } else if (streq_underscore("HI", symbol_str)) {
136                 akind = sign ? ATOMIC_TYPE_SHORT : ATOMIC_TYPE_USHORT;
137         } else if (streq_underscore("SI",      symbol_str)
138                 || streq_underscore("word",    symbol_str)
139                 || streq_underscore("pointer", symbol_str)) {
140                 akind = sign ? ATOMIC_TYPE_INT : ATOMIC_TYPE_UINT;
141         } else if (streq_underscore("DI", symbol_str)) {
142                 akind = sign ? ATOMIC_TYPE_LONGLONG : ATOMIC_TYPE_ULONGLONG;
143         } else {
144                 warningf(WARN_OTHER, &attribute->pos, "ignoring unknown mode '%s'",
145                          symbol_str);
146                 return orig_type;
147         }
148
149         if (type->kind == TYPE_ATOMIC || type->kind == TYPE_ENUM) {
150                 type_t *copy       = duplicate_type(type);
151                 copy->atomic.akind = akind;
152                 return identify_new_type(copy);
153         } else if (is_type_pointer(type)) {
154                 warningf(WARN_OTHER, &attribute->pos, "__attribute__((mode)) on pointers not implemented yet (ignored)");
155                 return type;
156         }
157
158         errorf(&attribute->pos,
159                "__attribute__((mode)) only allowed on integer, enum or pointer type");
160         return orig_type;
161 }
162
163 static void handle_attribute_aligned(const attribute_t *attribute,
164                                      entity_t *entity)
165 {
166         int alignment = 32; /* TODO: fill in maximum useful alignment for
167                                                    target machine */
168         if (attribute->a.arguments) {
169                 attribute_argument_t *argument = attribute->a.arguments;
170                 alignment = fold_constant_to_int(argument->v.expression);
171         }
172
173         if (!is_po2(alignment)) {
174                 errorf(&attribute->pos, "alignment must be a power of 2 but is %d", alignment);
175                 return;
176         }
177         if (alignment <= 0) {
178                 errorf(&attribute->pos, "alignment must be bigger than 0 but is %d", alignment);
179                 return;
180         }
181
182         switch (entity->kind) {
183         case DECLARATION_KIND_CASES:
184                 entity->declaration.alignment = alignment;
185         case ENTITY_TYPEDEF:
186                 entity->typedefe.alignment = alignment;
187                 break;
188         case ENTITY_STRUCT:
189         case ENTITY_UNION:
190                 entity->compound.alignment = MAX(entity->compound.alignment, (il_alignment_t)alignment);
191                 break;
192
193         default:
194                 warningf(WARN_OTHER, &attribute->pos, "alignment attribute specification on '%N' ignored", entity);
195                 break;
196         }
197 }
198
199 static const char *get_argument_string(const attribute_argument_t *argument)
200 {
201         if (argument == NULL)
202                 return NULL;
203         if (argument->kind != ATTRIBUTE_ARGUMENT_EXPRESSION)
204                 return NULL;
205         expression_t *expression = argument->v.expression;
206         if (expression->kind != EXPR_STRING_LITERAL)
207                 return NULL;
208         return expression->string_literal.value.begin;
209 }
210
211 static void handle_attribute_visibility(const attribute_t *attribute,
212                                         entity_t *entity)
213 {
214         /* This isn't really correct, the backend should provide a list of machine
215          * specific modes (according to gcc philosophy that is...) */
216         attribute_argument_t *arg = attribute->a.arguments;
217         if (arg == NULL) {
218                 errorf(&attribute->pos,
219                        "__attribute__((visibility(X))) misses argument");
220                 return;
221         }
222         const char *string = get_argument_string(arg);
223         if (string == NULL) {
224                 errorf(&attribute->pos,
225                        "__attribute__((visibility(X))) argument is not a string");
226                 return;
227         }
228         elf_visibility_tag_t visibility = get_elf_visibility_from_string(string);
229         if (visibility == ELF_VISIBILITY_ERROR) {
230                 errorf(&attribute->pos,
231                        "unknown visibility type '%s'", string);
232                 return;
233         }
234
235         switch (entity->kind) {
236         case ENTITY_VARIABLE:
237                 entity->variable.elf_visibility = visibility;
238                 break;
239         case ENTITY_FUNCTION:
240                 entity->function.elf_visibility = visibility;
241                 break;
242
243         default:
244                 warningf(WARN_OTHER, &attribute->pos, "visibility attribute specification on '%N' ignored", entity);
245                 break;
246         }
247 }
248
249 static void warn_arguments(const attribute_t *attribute)
250 {
251         if (attribute->a.arguments == NULL)
252                 return;
253
254         position_t const *const pos  = &attribute->pos;
255         char       const *const what = get_attribute_name(attribute->kind);
256         warningf(WARN_OTHER, pos, "attribute '%s' needs no arguments", what);
257 }
258
259 static void handle_attribute_packed_e(const attribute_t *attribute,
260                                       entity_t *entity)
261 {
262 #if 0
263         if (entity->kind != ENTITY_STRUCT) {
264                 warningf(WARN_OTHER, &attribute->pos, "packed attribute on '%N' ignored", entity);
265                 return;
266         }
267 #endif
268
269         warn_arguments(attribute);
270         entity->compound.packed = true;
271 }
272
273 static void handle_attribute_packed(const attribute_t *attribute, type_t *type)
274 {
275         if (type->kind != TYPE_COMPOUND_STRUCT) {
276                 position_t const *const pos  = &attribute->pos;
277                 warningf(WARN_OTHER, pos, "packed attribute on type '%T' ignored", type);
278                 return;
279         }
280
281         handle_attribute_packed_e(attribute, (entity_t*) type->compound.compound);
282 }
283
284 static void handle_attribute_asm(const attribute_t *attribute,
285                                       entity_t *entity)
286 {
287         attribute_argument_t *argument = attribute->a.arguments;
288         assert (argument->kind == ATTRIBUTE_ARGUMENT_EXPRESSION);
289         expression_t *expression = argument->v.expression;
290         if (expression->kind != EXPR_STRING_LITERAL)
291                 errorf(&attribute->pos, "Invalid asm attribute expression");
292         symbol_t *sym = symbol_table_insert(expression->string_literal.value.begin);
293         entity->function.actual_name = sym;
294         assert(argument->next == NULL);
295         return;
296 }
297
298 static void handle_attribute_alias(const attribute_t *attribute,
299                                   entity_t *entity)
300 {
301         // TODO: return if not at function_scope
302         attribute_argument_t *arg = attribute->a.arguments;
303         if (arg == NULL) {
304                 errorf(&attribute->pos,
305                        "__attribute__((alias(X))) misses argument");
306                 return;
307         }
308         const char *string = get_argument_string(arg);
309         if (string == NULL) {
310                 errorf(&attribute->pos,
311                        "__attribute__((alias(X))) argument is not a string");
312                 return;
313         }
314         symbol_t *symbol = symbol_table_insert(string);
315
316         switch (entity->kind) {
317         case ENTITY_VARIABLE:
318                 entity->variable.alias = symbol;
319                 break;
320         case ENTITY_FUNCTION:
321                 entity->function.alias = symbol;
322                 break;
323         default:
324                 warningf(WARN_OTHER, &attribute->pos, "alias attribute on '%N' ignored", entity);
325                 break;
326         }
327         return;
328 }
329
330 void handle_entity_attributes(const attribute_t *attributes, entity_t *entity)
331 {
332         if (entity->kind == ENTITY_TYPEDEF) {
333                 type_t *type = entity->typedefe.type;
334                 type = handle_type_attributes(attributes, type);
335                 entity->typedefe.type = type;
336         } else if (is_declaration(entity)) {
337                 type_t *type = entity->declaration.type;
338                 type = handle_type_attributes(attributes, type);
339                 entity->declaration.type = type;
340         }
341
342         decl_modifiers_t modifiers = 0;
343         const attribute_t *attribute = attributes;
344         for ( ; attribute != NULL; attribute = attribute->next) {
345                 switch (attribute->kind) {
346                 case ATTRIBUTE_GNU_CONST:         modifiers |= DM_CONST; break;
347                 case ATTRIBUTE_GNU_DEPRECATED:    modifiers |= DM_DEPRECATED; break;
348                 case ATTRIBUTE_GNU_NOINLINE:      modifiers |= DM_NOINLINE; break;
349                 case ATTRIBUTE_GNU_NAKED:         modifiers |= DM_NAKED; break;
350                 case ATTRIBUTE_GNU_PURE:          modifiers |= DM_PURE; break;
351                 case ATTRIBUTE_GNU_ALWAYS_INLINE: modifiers |= DM_FORCEINLINE; break;
352                 case ATTRIBUTE_GNU_CONSTRUCTOR:   modifiers |= DM_CONSTRUCTOR; break;
353                 case ATTRIBUTE_GNU_DESTRUCTOR:    modifiers |= DM_DESTRUCTOR; break;
354                 case ATTRIBUTE_GNU_TRANSPARENT_UNION:
355                                                                                   modifiers |= DM_TRANSPARENT_UNION;
356                                                                                   break;
357                 case ATTRIBUTE_GNU_USED:          modifiers |= DM_USED; break;
358                 case ATTRIBUTE_GNU_UNUSED:        modifiers |= DM_UNUSED; break;
359                 case ATTRIBUTE_GNU_DLLIMPORT:     modifiers |= DM_DLLIMPORT; break;
360                 case ATTRIBUTE_GNU_DLLEXPORT:     modifiers |= DM_DLLEXPORT; break;
361                 case ATTRIBUTE_GNU_WEAK:          modifiers |= DM_WEAK; break;
362                 case ATTRIBUTE_GNU_LEAF:          modifiers |= DM_LEAF; break;
363
364                 case ATTRIBUTE_MS_DLLIMPORT:     modifiers |= DM_DLLIMPORT; break;
365                 case ATTRIBUTE_MS_DLLEXPORT:     modifiers |= DM_DLLEXPORT; break;
366                 case ATTRIBUTE_MS_NAKED:         modifiers |= DM_NAKED; break;
367                 case ATTRIBUTE_MS_NOINLINE:      modifiers |= DM_NOINLINE; break;
368                 case ATTRIBUTE_MS_THREAD:        modifiers |= DM_THREAD; break;
369                 case ATTRIBUTE_MS_DEPRECATED:    modifiers |= DM_DEPRECATED; break;
370                 case ATTRIBUTE_MS_RESTRICT:      modifiers |= DM_RESTRICT; break;
371                 case ATTRIBUTE_MS_NOALIAS:       modifiers |= DM_NOALIAS; break;
372
373                 case ATTRIBUTE_GNU_ALIAS:
374                         handle_attribute_alias(attribute, entity);
375                         break;
376
377                 case ATTRIBUTE_GNU_PACKED:
378                         handle_attribute_packed_e(attribute, entity);
379                         break;
380
381                 case ATTRIBUTE_GNU_ASM:
382                         handle_attribute_asm(attribute, entity);
383                         break;
384
385                 case ATTRIBUTE_GNU_VISIBILITY:
386                         handle_attribute_visibility(attribute, entity);
387                         break;
388
389                 case ATTRIBUTE_MS_ALIGN:
390                 case ATTRIBUTE_GNU_ALIGNED:
391                         handle_attribute_aligned(attribute, entity);
392                         break;
393                 default: break;
394                 }
395         }
396
397         if (modifiers != 0) {
398                 switch (entity->kind) {
399                 case ENTITY_TYPEDEF:
400                         entity->typedefe.modifiers |= modifiers;
401                         break;
402                 case ENTITY_UNION:
403                 case ENTITY_STRUCT:
404                         entity->compound.modifiers |= modifiers;
405                         break;
406                 case ENTITY_COMPOUND_MEMBER:
407                 case ENTITY_VARIABLE:
408                 case ENTITY_FUNCTION:
409                         entity->declaration.modifiers |= modifiers;
410                         break;
411                 default:
412                         /* TODO: warning */
413                         break;
414                 }
415         }
416 }
417
418 static type_t *change_calling_convention(type_t *type, cc_kind_t cconv)
419 {
420         if (is_typeref(type) || !is_type_function(type)) {
421                 return type;
422         }
423
424         if (type->function.calling_convention == cconv)
425                 return type;
426
427         type_t* new_type = duplicate_type(type);
428         new_type->function.calling_convention = cconv;
429         return identify_new_type(new_type);
430 }
431
432 static type_t *add_modifiers(type_t *type, decl_modifiers_t modifiers)
433 {
434         if (is_typeref(type) || !is_type_function(type)) {
435                 return type;
436         }
437
438         if ((type->function.modifiers & modifiers) == modifiers)
439                 return type;
440
441         type_t* new_type = duplicate_type(type);
442         new_type->function.modifiers |= modifiers;
443         return identify_new_type(new_type);
444 }
445
446 type_t *handle_type_attributes(const attribute_t *attributes, type_t *type)
447 {
448         const attribute_t *attribute = attributes;
449         for ( ; attribute != NULL; attribute = attribute->next) {
450                 switch (attribute->kind) {
451                 case ATTRIBUTE_GNU_PACKED:
452                         handle_attribute_packed(attribute, type);
453                         break;
454                 case ATTRIBUTE_GNU_CDECL:
455                 case ATTRIBUTE_MS_CDECL:
456                         type = change_calling_convention(type, CC_CDECL);
457                         break;
458                 case ATTRIBUTE_MS_STDCALL:
459                 case ATTRIBUTE_GNU_STDCALL:
460                         type = change_calling_convention(type, CC_STDCALL);
461                         break;
462                 case ATTRIBUTE_MS_FASTCALL:
463                 case ATTRIBUTE_GNU_FASTCALL:
464                         type = change_calling_convention(type, CC_FASTCALL);
465                         break;
466                 case ATTRIBUTE_MS_THISCALL:
467                         type = change_calling_convention(type, CC_THISCALL);
468                         break;
469                 case ATTRIBUTE_GNU_RETURNS_TWICE:
470                 case ATTRIBUTE_MS_RETURNS_TWICE:
471                         type = add_modifiers(type, DM_RETURNS_TWICE);
472                         break;
473                 case ATTRIBUTE_GNU_NORETURN:
474                 case ATTRIBUTE_MS_NORETURN:
475                         type = add_modifiers(type, DM_NORETURN);
476                         break;
477                 case ATTRIBUTE_GNU_MALLOC:
478                 case ATTRIBUTE_MS_ALLOCATE:
479                         type = add_modifiers(type, DM_MALLOC);
480                         break;
481                 case ATTRIBUTE_GNU_NOTHROW:
482                 case ATTRIBUTE_MS_NOTHROW:
483                         type = add_modifiers(type, DM_NOTHROW);
484                         break;
485                 case ATTRIBUTE_GNU_MODE:
486                         type = handle_attribute_mode(attribute, type);
487                         break;
488                 default:
489                         break;
490                 }
491         }
492
493         return type;
494 }
495
496 const char *get_deprecated_string(const attribute_t *attribute)
497 {
498         for ( ; attribute != NULL; attribute = attribute->next) {
499                 if (attribute->kind != ATTRIBUTE_MS_DEPRECATED)
500                         continue;
501
502                 attribute_argument_t *argument = attribute->a.arguments;
503                 return get_argument_string(argument);
504         }
505         return NULL;
506 }
507
508 static bool property_attribute_equal(const attribute_property_argument_t *prop1,
509                                      const attribute_property_argument_t *prop2)
510 {
511         return prop1->put_symbol == prop2->put_symbol
512                 && prop1->get_symbol == prop2->get_symbol;
513 }
514
515 static bool attribute_argument_equal(const attribute_argument_t *arg1,
516                                      const attribute_argument_t *arg2)
517 {
518         if (arg1->kind != arg2->kind)
519                 return false;
520
521         switch (arg1->kind) {
522         case ATTRIBUTE_ARGUMENT_SYMBOL:
523                 return arg1->v.symbol == arg2->v.symbol;
524         case ATTRIBUTE_ARGUMENT_EXPRESSION:
525                 /* TODO */
526                 return false;
527         }
528         panic("unknown argument type");
529 }
530
531 static bool attribute_arguments_equal(const attribute_argument_t *args1,
532                                       const attribute_argument_t *args2)
533 {
534         for ( ; args1 != NULL && args2 != NULL;
535              args1 = args1->next, args2 = args2->next) {
536                 if (!attribute_argument_equal(args1, args2))
537                         return false;
538         }
539         /* both should be NULL now */
540         return args1 == args2;
541 }
542
543 bool attributes_equal(const attribute_t *attr1, const attribute_t *attr2)
544 {
545         if (attr1->kind != attr2->kind)
546                 return false;
547
548         switch (attr1->kind) {
549         case ATTRIBUTE_MS_PROPERTY:
550                 return property_attribute_equal(attr1->a.property, attr2->a.property);
551         default:
552                 return attribute_arguments_equal(attr1->a.arguments,
553                                                  attr2->a.arguments);
554         }
555 }