/*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#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];
}
syntax highlighted by Code2HTML, v. 0.9.1