simple support for __attribute__((alias("symbol")))
[cparser] / parser.c
index b637dd2..38f8794 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -3755,6 +3755,7 @@ static entity_t *parse_declarator(const declaration_specifiers_t *specifiers,
 
        if (attributes != NULL) {
                handle_entity_attributes(attributes, entity);
+               // TODO: maybe drop alias symbol here if not file_scope
        }
 
        if (entity->kind == ENTITY_FUNCTION && !freestanding) {
@@ -3907,6 +3908,50 @@ static void merge_in_attributes(declaration_t *decl, attribute_t *attributes)
 
 static bool is_main(entity_t*);
 
+/*
+__attribute__(alias("sym")) provides a definition
+in function scope function and variable declarations
+
+the sym must be declared in the same translation unit
+but the sym declaration can come later than the alias one
+(weakref is not supported)
+
+sym must be marked used
+*/
+static struct alias {
+       struct alias *next;
+       entity_t *entity;
+       symbol_t *symbol;
+} *aliases;
+static void add_alias(entity_t *entity, symbol_t *symbol)
+{
+       struct alias *a;
+
+       assert(entity->kind == ENTITY_FUNCTION || entity->kind == ENTITY_VARIABLE);
+       a = malloc(sizeof *a);
+       a->entity = entity;
+       a->symbol = symbol;
+       a->next = aliases;
+       aliases = a;
+}
+static void mark_aliases_used(void)
+{
+       struct alias *alias;
+       for (alias = aliases; alias; alias = alias->next) {
+               const namespace_tag_t namespc = (namespace_tag_t)alias->entity->base.namespc;
+               entity_t *entity = get_entity(alias->symbol, namespc);
+               if (entity == NULL) {
+                       errorf(&alias->entity->base.pos, "'%N' is aliased to undefined symbol \"%s\"", alias->entity, alias->symbol->string);
+               } else if (entity->base.parent_scope != file_scope) {
+                       errorf(&alias->entity->base.pos, "'%N' is aliased to undefined symbol \"%s\" (TODO: scope)", alias->entity, alias->symbol->string);
+               } else {
+                       // printf("handle alias %s -> %s\n", alias->entity->base.symbol->string, alias->symbol->string);
+                       assert(entity->kind == ENTITY_FUNCTION || entity->kind == ENTITY_VARIABLE);
+                       entity->declaration.used = true;
+               }
+       }
+}
+
 /**
  * record entities for the NAMESPACE_NORMAL, and produce error messages/warnings
  * for various problems that occur for multiple definitions
@@ -3952,6 +3997,27 @@ entity_t *record_entity(entity_t *entity, const bool is_definition)
                warningf(WARN_NESTED_EXTERNS, pos, "nested extern declaration of '%#N'", entity);
        }
 
+       if (current_scope == file_scope) {
+               if (entity->kind == ENTITY_FUNCTION) {
+                       if (entity->function.alias) {
+                               if (is_definition)
+                                       errorf(pos, "definition of '%N' with alias attribute", entity);
+                               /* TODO alias provides a definition */
+                               *(bool*)&is_definition = true;
+                               add_alias(entity, entity->function.alias);
+                       }
+               }
+               if (entity->kind == ENTITY_VARIABLE) {
+                       if (entity->variable.alias) {
+                               if (is_definition)
+                                       errorf(pos, "definition of '%N' with alias attribute", entity);
+                               /* TODO alias provides a definition */
+                               *(bool*)&is_definition = true;
+                               add_alias(entity, entity->variable.alias);
+                       }
+               }
+       }
+
        if (previous_entity != NULL) {
                position_t const *const ppos = &previous_entity->base.pos;
 
@@ -4094,6 +4160,16 @@ error_redeclaration:
                                }
                        }
 
+                       /* merge alias attribute into earlier declaration */
+                       if (entity->kind == ENTITY_FUNCTION) {
+                               if (entity->function.alias)
+                                       previous_entity->function.alias = entity->function.alias;
+                       }
+                       if (entity->kind == ENTITY_VARIABLE) {
+                               if (entity->variable.alias)
+                                       previous_entity->variable.alias = entity->variable.alias;
+                       }
+
                        prev_decl->modifiers |= decl->modifiers;
                        if (entity->kind == ENTITY_FUNCTION) {
                                previous_entity->function.is_inline |= entity->function.is_inline;
@@ -10068,6 +10144,8 @@ static statement_t *parse_compound_statement(bool inside_expression_statement)
  */
 static void check_unused_globals(void)
 {
+       mark_aliases_used();
+
        if (!is_warn_on(WARN_UNUSED_FUNCTION) && !is_warn_on(WARN_UNUSED_VARIABLE))
                return;