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