/* * Copyright (c) 2003 - 2007, Nils R. Weller * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Functions to create, populate and search scopes with and for * structure and enum definitions and variable declarations */ #include "scope.h" #include #include #include #include "token.h" #include "decl.h" #include "misc.h" #include "expr.h" #include "error.h" #include "type.h" #include "symlist.h" #include "functions.h" #include "debug.h" #include "n_libc.h" struct scope global_scope = { 0, NULL, 0, { NULL, NULL, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, NULL, NULL, { NULL, NULL, 0, 0 }, { NULL, NULL, 0, 0 }, NULL, NULL, NULL }; struct scope *curscope; static struct scope *scopelist_tail = &global_scope; struct decl *static_init_vars; struct decl *siv_tail; struct decl *static_uninit_vars; struct decl *siuv_tail; struct sym_entry *extern_vars; struct sym_entry *extern_vars_tail; /* * Looks up structure definition (not instance!) with tag ``tag''. If * ``nested'' is nonzero, the lookup will use scopes above the current * scope. If it's not, the lookup will only consider the current scope * On success, a pointer to the structure definition is returned, else a * null pointer */ struct ty_struct * lookup_struct(struct scope *s, const char *tag, int nested) { do { struct ty_struct *ts; for (ts = s->struct_defs.head; ts != NULL; ts = ts->next) { if (ts->tag != NULL && strcmp(ts->tag, tag) == 0) { return ts; } } if (nested == 0) { break; } } while ((s = s->parent) != NULL); return NULL; } struct ty_enum * lookup_enum(struct scope *s, const char *tag, int nested) { int i; char *p; do { if (s->enum_defs.data) { for (i = 0; i < s->enum_defs.ndecls; ++i) { p = s->enum_defs.data[i]->tag; if (p != NULL && strcmp(p, tag) == 0) { return s->enum_defs.data[i]; } } } if (nested == 0) { break; } } while ((s = s->parent) != NULL); return NULL; } struct type * lookup_typedef(struct scope *s, const char *name) { struct decl **d; int i; size_t namelen = strlen(name); do { if (s->typedef_hash.used) { struct sym_entry *se; #if 0 se = lookup_hash(s->typedef_hash, s->n_typedef_slots, name, namelen); #endif se = new_lookup_hash(&s->typedef_hash, name, namelen); if (se != NULL) { return se->dec->dtype; } } else { if (s->typedef_decls.data) { d = s->typedef_decls.data; for (i = 0; i < s->typedef_decls.ndecls; ++i) { if (strcmp(d[i]->dtype->name, name) == 0) { return d[i]->dtype; } } } } } while ((s = s->parent) != NULL); return NULL; } struct scope * new_scope(int type) { struct scope *ret; static struct scope nullscope; static unsigned long scopeno = 1; /* 0 = global scope */ #ifdef DEBUG2 puts("opening new scope"); #endif ret = n_xmalloc(sizeof *ret); *ret = nullscope; ret->type = type; ret->parent = curscope; ret->scopeno = scopeno++; scopelist_tail->next = ret; scopelist_tail = ret; curscope = ret; return ret; } void close_scope(void) { #if defined(DEBUG) || defined(DEBUG2) || defined(DEBUG3) if ((curscope = curscope->parent) == NULL) { fprintf(stderr, "Fatal error: Attempting to close global scope\n"); ++errors; /* exit() handler checks this */ abort(); } #else if (curscope->parent == NULL) { ++errors; } else { curscope = curscope->parent; } #endif } static void do_store_decl(struct scope *s, struct dec_block *destdec, struct decl *d) { struct statement *st; if (destdec->ndecls >= destdec->nslots) { if (destdec->nslots == 0) { destdec->nslots = 8; } destdec->nslots *= 2; destdec->data = n_xrealloc(destdec->data, sizeof *destdec->data * destdec->nslots); } destdec->data[ destdec->ndecls++ ] = d; if (s != NULL) { st = alloc_statement(); st->type = ST_DECL; st->data = d; append_statement(&s->code, &s->code_tail, st); } } static int check_redef(struct scope *s, struct type *ty, int *need_def) { struct decl *d; if ((d = lookup_symbol(s, ty->name, 0)) == NULL) { return 0; } if (s == &global_scope) { int compres; /* * Declarations at file scope are tentative. They * might be repeated as often as desired, as long * as none of them differs */ if ((compres = compare_types(d->dtype, ty, CMPTY_SIGN|CMPTY_CONST|CMPTY_TENTDEC)) == -1 || (d->dtype->storage && ty->storage && d->dtype->storage != ty->storage)) { /* * 09/30/07: gcc and tinycc allow arbitrary * redeclarations of static/extern storage * specifiers, e.g. * * static int foo; * extern int foo; * * BitchX and probably other programs have * come to depend on this, so we must support * it too */ if (compres != -1) { /* * Yes, storage is only difference * * XXX symbol management is almost * certainly wrong, and will cause * problems for nasm, because the * ``has_def'' stuff below is not * handled */ warningfl(d->tok, "Redeclaration of " "`%s' with different storage " "class, this may cause problems " "with some assemblers", ty->name); d->dtype->storage = ty->storage; } else { errorfl(d->tok, "Redefinition of `%s' with " "conflicting type", ty->name); return -1; } } if (d->dtype->is_func) { /* * Might have to replace old function entry * with new one if this is a definition * and the old one is a declaration. In * either case, one can be deallocated */ if (d->dtype->is_def && ty->is_def) { errorfl(d->tok, "Multiple definitions of function `%s'", ty->name); } else if (ty->is_def) { if (d->dtype->tlist->tfunc->nargs == -1) { ty->tlist->tfunc->was_just_declared = 1; } d->dtype = ty; } } *need_def = 0; if (ty->storage != TOK_KEY_EXTERN && !ty->is_func) { if (!d->has_def) { d->has_def = 1; *need_def = 1; } } if (ty->tlist != NULL && ty->tlist->type == TN_ARRAY_OF) { /* May have to update array information */ if (d->dtype->tlist->arrarg->const_value == NULL) { d->dtype->tlist->arrarg->const_value = ty->tlist->arrarg->const_value; } } return 1; /* already has entry */ } else { struct token *ttmp; ttmp = errorfl_mk_tok( ty->file, ty->line, NULL); errorfl(ttmp, "Multiple definitions of `%s'", ty->name); return -1; } /* NOTREACHED */ return 0; } #if 0 /* * The stuff below handles a particularly ugly part of the C language; * local external declarations, particularly for functions, are * permitted! This sucks big time because some assemblers, such as * nasm, always require an explicit symbol declaration to get access * to an external object. This in turn means duplicate symbols will * cause problems, such that we have to check for them. On a per- * scope basis, multiple declarations have already been handled * neatly for some time. However, e.g. local declarations overriden by * subsequent global definitions in the same module, or just * subsequent declarations, caused errors. * * */ struct local_extern_decls { struct decl *dec; struct local_func_decl *next; }; struct local_func_decl *local_func_decl_list; static struct local_func_decl *local_func_decl_list_tail; put_local_func_decl(dec[i]); #endif /* * Stores declaration referenced by ``d'' in current scope (curscope) */ void store_decl_scope(struct scope *s, struct decl **dec) { struct type *t; struct dec_block *destdec = NULL; int i; for (i = 0; dec[i] != NULL; ++i) { t = dec[i]->dtype; if (t->storage == TOK_KEY_TYPEDEF) { /* Store dtype */ if (s != &global_scope) { destdec = &s->typedef_decls; } else { if (!s->typedef_hash.used) { /* Global scope - need large table! */ new_make_hash_table(&s->typedef_hash, SYM_HTAB_GLOBAL_SCOPE); #if 0 s->typedef_hash = n_xmalloc(64 * sizeof *s->typedef_hash); memset(s->typedef_hash, 0, 64 * sizeof *s->typedef_hash); s->n_typedef_slots = 63; #endif } #if 0 put_hash_table(s->typedef_hash, s->n_typedef_slots, make_sym_entry(dec[i])); #endif new_put_hash_table(&s->typedef_hash, make_sym_entry(dec[i])); } #ifdef DEBUG2 printf("storing typedef %s\n", t->name ? t->name : "unnamed typedef"); #endif } else { /* This is a ``real'' declaration */ if (t->name != NULL) { /* * Not anonymous - protect against * redefinition */ int need_def; int rc = check_redef(s, t, &need_def); if (rc == 1) { /* * Multiple tentative definitions - * OK */ /* XXX deal with initializers! */ if (!need_def) { /* Need not be allocated */ continue; } else { dec[i]->has_def = 1; } } else if (rc == -1) { /* Redefinition - bad */ return; } } if (s->type == SCOPE_CODE && (t->storage == TOK_KEY_EXTERN || (t->is_func && t->storage != TOK_KEY_STATIC && t->tlist->type == TN_FUNCTION))) { /* * XXX 07/22/07: * Local extern declarations are terrible to * deal with.. for now we always put them * into the global scope to avoid * redeclarations of symbols * * 08/22/07: This was missing the TN_FUNCTION * check; without function pointers don't * work */ s = &global_scope; } if (s == &global_scope) { /* Global or file scope declaration */ #ifdef DEBUG2 printf("Storing global declaration %s\n", t->name); #endif if (t->storage == TOK_KEY_AUTO) { errorfl(dec[i]->tok /*XXX*/, "Bogus storage class specifier in global variable declaration"); return; } else if (t->storage == TOK_KEY_EXTERN || (t->is_func && !t->is_def && t->storage != TOK_KEY_STATIC)) { t->storage = TOK_KEY_EXTERN; destdec = &s->extern_decls; } else if (t->storage == 0) { /* * Need this to distinguish between * global and local static variables. */ t->storage = TOK_KEY_EXTERN; destdec = &s->static_decls; } else if (t->storage == TOK_KEY_STATIC) { destdec = &s->static_decls; } else { printf("BAD STORAGE CLASS FOR %s " "- %d\n", t->name, t->storage); abort(); } } else { /* * Local declaration (static or * automatic or register) */ #ifdef DEBUG2 printf("Storing local declaration %s\n", t->name? t->name: "unknown declaration"); #endif if (t->storage == TOK_KEY_EXTERN) { destdec = &s->extern_decls; } else if (t->storage == TOK_KEY_STATIC) { destdec = &s->static_decls; } else if (t->storage == TOK_KEY_REGISTER) { /* destdec = &s->register_decls; */ destdec = &s->automatic_decls; } else if (t->is_func && t->tlist->type == TN_FUNCTION) { /* XXX not working */ t->storage = TOK_KEY_EXTERN; destdec = &s->extern_decls; } else { /* * 08/21/07: Removed because this is * never used */ /* t->storage = TOK_KEY_AUTO;*/ destdec = &s->automatic_decls; } if (t->is_func && t->tlist->type == TN_FUNCTION) { /* Record local function declaration */ #if 0 printf("putting local fdec `%s'\n", t->name); put_local_func_decl(dec[i]); #endif } } if (destdec == &s->extern_decls) { append_symlist(NULL, &extern_vars, &extern_vars_tail, dec[i]); } if (destdec == &s->static_decls && !dec[i]->dtype->is_func) { if (dec[i]->init != NULL) { append_decl( &static_init_vars, &siv_tail, dec[i]); } else { append_decl( &static_uninit_vars, &siuv_tail, dec[i]); } } /* * NOTE: A declaration need not have a name to be * valid! (e.g. anonymous unions and bitfields) */ append_symlist(s, &s->slist, &s->slist_tail, dec[i]); } if (destdec) { if (dec[i]->dtype->storage == TOK_KEY_STATIC && !dec[i]->dtype->is_func && dec[i]->asmname == NULL) { char *newname; size_t len = strlen(dec[i]->dtype->name); /* * Change variable name to avoid name clashes * with other static variables. The old name * will be kept in the symbol list. And since * all lookups go through this list, the * identifier is still visible as what it was * originally called */ len += sizeof "_Static_ " + 8; newname = n_xmalloc(len+sizeof "_Static_"+8); sprintf(newname, "_Static_%s%lu", dec[i]->dtype->name, s->scopeno); dec[i]->dtype->name = newname; } else if (dec[i]->dtype->storage == TOK_KEY_EXTERN || dec[i]->dtype->storage == TOK_KEY_STATIC || dec[i]->dtype->is_func) { if (dec[i]->asmname != NULL) { dec[i]->dtype->name = dec[i]->asmname; } } do_store_decl(s, destdec, dec[i]); } } } void complete_type(struct ty_struct *oldts, struct ty_struct *ts) { struct ty_struct *oldlink; int oldrefs; oldlink = oldts->next; oldrefs = oldts->references; if (ts->incomplete) { return; } memcpy(oldts, ts, sizeof *ts); oldts->next = oldlink; oldts->incomplete = 0; oldts->references = oldrefs + ts->references; } /* * Stores structure or enum definition (not instance!) in current scope. * If ts is not a null pointer, it will be stored. Otherwise, te will be * stored */ void store_def_scope(struct scope *sc, struct ty_struct *ts, struct ty_enum *te, struct token *tok) { int i; char *p; struct sd *s = NULL; struct ed *e = NULL; if (ts != NULL) { /* * We must first check whether we are completing an incomplete * structure, i.e. one that was originally declared using e.g. * ``struct foo;'' or ``typedef struct foo bar;'' */ struct scope *destscope = curscope; while (destscope->type == SCOPE_STRUCT) { destscope = destscope->parent; } ts->parentscope = destscope; if (ts->tag != NULL) { struct ty_struct *oldts; if ((oldts = lookup_struct(destscope, ts->tag, 1)) != NULL) { if (oldts->incomplete) { complete_type(oldts, ts); return; } else if (destscope == oldts->parentscope) { if (!ts->incomplete) { errorfl(tok->prev, "Multiple definitions " "of structure or " "union `%s'", ts->tag); } return; } } } /* Completely new structure type */ s = &destscope->struct_defs; if (s->head == NULL) { s->head = s->tail = ts; } else { s->tail->next = ts; s->tail = s->tail->next; } ++s->ndecls; #ifdef DEBUG2 printf("stored structure with tag %s\n", ts->tag); #endif } else if (te == NULL) { puts("Fatal error in store_def_scope - te = 0"); abort(); } else { struct scope *destscope = curscope; while (destscope->type == SCOPE_STRUCT) { destscope = destscope->parent; } e = & /*sc*/ destscope->enum_defs; if (te->tag != NULL) { /* * Check whether we have a multiple definition error. * It is not necessary to check for incomplete types, * as with structures, because enum forward references * are invalid (XXX might be implemented as extension * later) */ for (i = 0; i < e->ndecls; ++i) { p = e->data[i]->tag; if (p && strcmp(p, te->tag) == 0) { errorfl(tok, "Multiple definitions of " "enum `%s'", p); return; } } if (lookup_struct(sc, te->tag, 0) != NULL) { errorfl(tok, "Multiple definitions of structure `%s'", te->tag); } } if (e->ndecls >= e->nslots) { if (e->nslots == 0) { e->nslots = 1; } e->nslots *= 2; e->data = n_xrealloc(e->data, e->nslots * sizeof *e->data); } e->data[ e->ndecls++ ] = te; } } struct decl * lookup_symbol(struct scope *s, const char *name, int nested) { struct sym_entry *se; se = lookup_symbol_se(s, name, nested); if (se == NULL) { return NULL; } return se->dec; } struct /*decl*/ sym_entry * lookup_symbol_se(struct scope *s, const char *name, int nested) { size_t len; len = strlen(name); do { struct sym_entry *se; if (!s->sym_hash.used) { /* Linear scan */ for (se = s->slist; se != NULL; se = se->next) { if (se->namelen == len) { if (se->name[0] == name[0]) { if (strcmp(&se->name[1], &name[1]) == 0) { return se /*->dec*/; } } } } } else { /* Hash lookup (key is length) */ #if 0 se = lookup_hash(s->sym_hash, s->n_hash_slots, name, len); #endif se = new_lookup_hash(&s->sym_hash, name, len); if (se != NULL) { return se /*->dec*/; } } if (nested == 0) { break; } } while ((s = s->parent) != NULL); return NULL; } struct decl * access_symbol(struct scope *s, const char *name, int nested) { struct decl *ret; if ((ret = lookup_symbol(s, name, nested)) != NULL) { if (ret->is_alias) { ret = ret->is_alias; } ++ret->references; if (ret->dtype->tstruc != NULL) { ++ret->dtype->tstruc->references; } } return ret; } struct decl * put_implicit(const char *name) { struct decl *d[2]; struct type *ty; struct type_node *tnode; d[0] = alloc_decl(); ty = alloc_type(); ty->name = (char *)name; ty->code = TY_INT; ty->storage = TOK_KEY_EXTERN; ty->is_func = 1; ty->implicit = 1; ty->sign = TOK_KEY_SIGNED; tnode = alloc_type_node(); tnode->type = TN_FUNCTION; tnode->tfunc = alloc_ty_func(); tnode->tfunc->scope = NULL; tnode->tfunc->nargs = -1; tnode->tfunc->ret = NULL; tnode->tfunc->type = FDTYPE_ISO; ty->tlist = tnode; d[0]->dtype = ty; d[0]->references = 1; d[1] = NULL; store_decl_scope(&global_scope, d); return d[0]; }