475 lines
11 KiB
C
475 lines
11 KiB
C
/*
|
|
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
|
|
* See the copyright notice in the ACK home directory, in the file "Copyright".
|
|
*/
|
|
/* $Header$ */
|
|
/* P R O T O T Y P E F I D D L I N G */
|
|
|
|
#include "lint.h"
|
|
#include "debug.h"
|
|
#include "idfsize.h"
|
|
#include "nparams.h"
|
|
#include "botch_free.h"
|
|
#include <alloc.h>
|
|
#include "Lpars.h"
|
|
#include "level.h"
|
|
#include <flt_arith.h>
|
|
#include "arith.h"
|
|
#include "align.h"
|
|
#include "stack.h"
|
|
#include "idf.h"
|
|
#include "def.h"
|
|
#include "type.h"
|
|
#include "struct.h"
|
|
#include "label.h"
|
|
#include "expr.h"
|
|
#include "declar.h"
|
|
#include "decspecs.h"
|
|
#include "proto.h"
|
|
#include "assert.h"
|
|
|
|
extern char options[];
|
|
|
|
check_for_void(pl)
|
|
register struct proto *pl;
|
|
{
|
|
register int errcnt = 0;
|
|
|
|
if (!pl) return;
|
|
if ((pl->pl_flag & PL_VOID) && !(pl->next)) return;
|
|
|
|
while (pl) {
|
|
if (pl->pl_flag & PL_VOID) {
|
|
if (!errcnt && !(pl->pl_flag & PL_ERRGIVEN))
|
|
error("illegal use of void in argument list");
|
|
pl->pl_flag |= PL_ERRGIVEN;
|
|
errcnt++;
|
|
}
|
|
pl = pl->next;
|
|
}
|
|
}
|
|
|
|
add_proto(pl, ds, dc, lvl)
|
|
struct proto *pl;
|
|
struct decspecs *ds;
|
|
struct declarator *dc;
|
|
int lvl;
|
|
{
|
|
/* The full typed identifier or abstract type, described
|
|
by the structures decspecs and declarator are turned
|
|
a into parameter type list structure.
|
|
The parameters will be declared at level L_FORMAL2,
|
|
later on it's decided whether they were prototypes
|
|
or actual declarations.
|
|
*/
|
|
register struct idf *idf = dc->dc_idf;
|
|
register struct def *def = idf ? idf->id_def : (struct def *)0;
|
|
register int sc = ds->ds_sc;
|
|
register struct type *type;
|
|
char formal_array = 0;
|
|
|
|
ASSERT(ds->ds_type != (struct type *)0);
|
|
|
|
pl->pl_flag = PL_FORMAL;
|
|
type = declare_type(ds->ds_type, dc);
|
|
if (type->tp_size < (arith)0 && actual_declaration(sc, type)) {
|
|
extern char *symbol2str();
|
|
if (type->tp_fund != VOID)
|
|
error("unknown %s-type", symbol2str(type->tp_fund));
|
|
else {
|
|
if (idf != (struct idf *)0
|
|
|| ds->ds_sc_given
|
|
|| ds->ds_typequal) {
|
|
error("illegal use of void in argument list");
|
|
pl->pl_flag |= PL_ERRGIVEN;
|
|
}
|
|
/* set PL_VOID anyway */
|
|
pl->pl_flag |= PL_VOID;
|
|
}
|
|
}
|
|
if (ds->ds_sc_given && ds->ds_sc != REGISTER) {
|
|
if (!(pl->pl_flag & PL_ERRGIVEN)) {
|
|
if (ds->ds_sc != AUTO) {
|
|
error("illegal storage class in parameter declaration");
|
|
} else {
|
|
warning("illegal storage class in parameter declaration");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Perform some special conversions for parameters.
|
|
*/
|
|
if (type->tp_fund == FUNCTION) {
|
|
type = construct_type(POINTER, type, 0, (arith) 0, NO_PROTO);
|
|
} else if (type->tp_fund == ARRAY) {
|
|
type = construct_type(POINTER, type->tp_up, 0, (arith) 0, NO_PROTO);
|
|
formal_array = 1;
|
|
}
|
|
|
|
/* According to the standard we should ignore the storage
|
|
class of a parameter, unless it's part of a function
|
|
definition.
|
|
However, in the routine declare_protos we don't know decspecs,
|
|
and therefore we can't complain up there. So we build up the
|
|
storage class, and keep quiet until we reach declare_protos.
|
|
*/
|
|
sc = (ds->ds_sc_given && ds->ds_sc != REGISTER) ?
|
|
0 : sc == 0 ? FORMAL : REGISTER;
|
|
|
|
if (def && (def->df_level == lvl /* || def->df_level < L_PROTO */ )) {
|
|
/* redeclaration at the same level */
|
|
error("parameter %s redeclared", idf->id_text);
|
|
} else if (idf != (struct idf *)0) {
|
|
/* New definition, redefinition hides earlier one
|
|
*/
|
|
register struct def *newdef = new_def();
|
|
|
|
newdef->next = def;
|
|
newdef->df_level = lvl;
|
|
newdef->df_sc = sc;
|
|
newdef->df_type = type;
|
|
newdef->df_formal_array = formal_array;
|
|
newdef->df_file = idf->id_file;
|
|
newdef->df_line = idf->id_line;
|
|
#ifdef LINT
|
|
newdef->df_set = (type->tp_fund == ARRAY);
|
|
/* newdef->df_firstbrace = 0; */
|
|
#endif
|
|
/* We can't put the idf onto the stack, since these kinds
|
|
of declaration may occurs at any level, and the idf
|
|
does not necessarily go at this level. E.g.
|
|
|
|
f() {
|
|
...
|
|
{ int func(int a, int b);
|
|
...
|
|
}
|
|
}
|
|
|
|
The idf's a and b declared in the prototype declaration
|
|
do not go at any level, they are simply ignored.
|
|
However, in
|
|
|
|
f(int a, int b) {
|
|
...
|
|
}
|
|
|
|
They should go at level L_FORMAL2. But at this stage
|
|
we don't know whether we have a prototype or function
|
|
definition. So, this process is postponed.
|
|
*/
|
|
idf->id_def = newdef;
|
|
update_ahead(idf);
|
|
}
|
|
|
|
pl->pl_idf = idf;
|
|
pl->pl_type = type;
|
|
}
|
|
|
|
struct tag *
|
|
gettag(tp, idpp)
|
|
struct type *tp;
|
|
struct idf **idpp;
|
|
{
|
|
struct tag *tg = (struct tag *)0;
|
|
register int fund = tp->tp_fund;
|
|
|
|
while (fund == FIELD || fund == POINTER
|
|
|| fund == ARRAY || fund == FUNCTION) {
|
|
tp = tp->tp_up;
|
|
fund = tp->tp_fund;
|
|
}
|
|
*idpp = tp->tp_idf;
|
|
switch(tp->tp_fund) {
|
|
case ENUM:
|
|
case UNION:
|
|
case STRUCT: tg = tp->tp_idf->id_tag; break;
|
|
}
|
|
return tg;
|
|
}
|
|
|
|
|
|
declare_protos(dc)
|
|
register struct declarator *dc;
|
|
{
|
|
/* At this points we know that the idf's in protolist are formal
|
|
parameters. So it's time to declare them at level L_FORMAL2.
|
|
*/
|
|
struct stack_level *stl = stack_level_of(L_FORMAL1);
|
|
register struct decl_unary *du;
|
|
register struct type *type;
|
|
register struct proto *pl;
|
|
register struct def *def;
|
|
|
|
#ifdef DEBUG
|
|
if (options['t'])
|
|
dumpidftab("start declare_protos", 0);
|
|
#endif /* DEBUG */
|
|
du = dc->dc_decl_unary;
|
|
while (du) {
|
|
if (du->du_fund == FUNCTION) {
|
|
if (du->next != (struct decl_unary *) 0) {
|
|
remove_proto_idfs(du->du_proto);
|
|
du->du_proto = 0;
|
|
} else break;
|
|
}
|
|
du = du->next;
|
|
}
|
|
pl = du ? du->du_proto : NO_PROTO;
|
|
if (pl) {
|
|
#if 0 /* the id_proto member is deleted (???) */
|
|
idf->id_proto = 0;
|
|
#endif /* 0 */
|
|
do {
|
|
struct tag *tg;
|
|
struct idf *idp = 0;
|
|
|
|
type = pl->pl_type;
|
|
|
|
/* `...' only for type checking */
|
|
if (pl->pl_flag & PL_ELLIPSIS) {
|
|
pl = pl->next;
|
|
continue;
|
|
}
|
|
|
|
/* special case: int f(void) { ; } */
|
|
if (type->tp_fund == VOID)
|
|
break;
|
|
|
|
if (!pl->pl_idf || !(def = pl->pl_idf->id_def)) {
|
|
error("no parameter identifier supplied");
|
|
pl = pl->next;
|
|
continue;
|
|
}
|
|
|
|
/* Postponed storage class checking.
|
|
*/
|
|
if (def->df_sc == 0)
|
|
error("illegal storage class in parameter declaration");
|
|
|
|
def->df_level = L_FORMAL2;
|
|
stack_idf(pl->pl_idf, stl);
|
|
pl = pl->next;
|
|
|
|
tg = gettag(type, &idp);
|
|
if (tg && tg->tg_level <= L_PROTO) {
|
|
tg->tg_level = L_FORMAL2;
|
|
stack_idf(idp, stl);
|
|
}
|
|
} while (pl);
|
|
}
|
|
#ifdef DEBUG
|
|
if (options['t'])
|
|
dumpidftab("end declare_protos", 0);
|
|
#endif /* DEBUG */
|
|
}
|
|
|
|
|
|
update_proto(tp, otp)
|
|
register struct type *tp, *otp;
|
|
{
|
|
/* This routine performs the proto type updates.
|
|
Consider the following code:
|
|
|
|
int f(double g());
|
|
int f(double g(int f(), int));
|
|
int f(double g(int f(long double), int));
|
|
|
|
The most accurate definition is the third line.
|
|
This routine will silently update all lists,
|
|
and removes the redundant occupied space.
|
|
*/
|
|
register struct proto *pl, *opl;
|
|
|
|
if (tp == otp) return;
|
|
if (!tp || !otp) return;
|
|
|
|
while (tp->tp_fund != FUNCTION) {
|
|
if (tp->tp_fund != POINTER && tp->tp_fund != ARRAY) return;
|
|
tp = tp->tp_up;
|
|
otp = otp->tp_up;
|
|
if (!tp) return;
|
|
}
|
|
|
|
pl = tp->tp_proto;
|
|
opl = otp->tp_proto;
|
|
if (pl && opl) {
|
|
/* both have prototypes */
|
|
while (pl && opl) {
|
|
update_proto(pl->pl_type, opl->pl_type);
|
|
pl = pl->next;
|
|
opl = opl->next;
|
|
}
|
|
/* Do not free the old prototype list. It might be part of
|
|
* a typedef.
|
|
*/
|
|
otp->tp_proto = tp->tp_proto;
|
|
} else if (opl) {
|
|
/* old decl has type */
|
|
} else if (pl) {
|
|
otp->tp_proto = pl;
|
|
}
|
|
|
|
update_proto(tp->tp_up, otp->tp_up);
|
|
}
|
|
|
|
/* struct/union and enum tags can be declared inside prototypes
|
|
* remove them from the symbol-table
|
|
*/
|
|
remove_proto_tag(tp)
|
|
struct type *tp;
|
|
{
|
|
register struct idf *ident;
|
|
register struct tag *tgp, **tgpp;
|
|
register int fund = tp->tp_fund;
|
|
|
|
while (fund == FIELD || fund == POINTER
|
|
|| fund == ARRAY || fund == FUNCTION) {
|
|
tp = tp->tp_up;
|
|
fund = tp->tp_fund;
|
|
}
|
|
|
|
ident = tp->tp_idf;
|
|
switch (tp->tp_fund) {
|
|
case ENUM:
|
|
case STRUCT:
|
|
case UNION: tgpp = &(ident->id_tag); break;
|
|
default: return;
|
|
}
|
|
|
|
while((*tgpp) && (*tgpp)->tg_type != tp) {
|
|
tgpp = &((*tgpp)->next);
|
|
}
|
|
if (!*tgpp) return;
|
|
|
|
tgp = *tgpp;
|
|
if (tgp->tg_level > L_PROTO) return;
|
|
|
|
#ifdef DEBUG
|
|
if (options['t'])
|
|
print("Removing idf %s from list\n",
|
|
ident->id_text);
|
|
#endif
|
|
|
|
(*tgpp) = tgp->next;
|
|
free_tag(tgp);
|
|
}
|
|
|
|
remove_proto_idfs(pl)
|
|
register struct proto *pl;
|
|
{
|
|
/* Remove all the identifier definitions from the
|
|
prototype list.
|
|
*/
|
|
register struct def *def;
|
|
|
|
while (pl) {
|
|
if (pl->pl_idf) {
|
|
#ifdef DEBUG
|
|
if (options['t'])
|
|
print("Removing idf %s from list\n",
|
|
pl->pl_idf->id_text);
|
|
#endif
|
|
def = pl->pl_idf->id_def;
|
|
if (def && def->df_level <= L_PROTO) {
|
|
pl->pl_idf->id_def = def->next;
|
|
free_def(def);
|
|
}
|
|
pl->pl_idf = (struct idf *) 0;
|
|
}
|
|
if (pl->pl_type) {
|
|
remove_proto_tag(pl->pl_type);
|
|
}
|
|
pl = pl->next;
|
|
}
|
|
}
|
|
|
|
call_proto(expp)
|
|
register struct expr **expp;
|
|
{
|
|
/* If the function specified by (*expp)->OP_LEFT has a prototype,
|
|
the parameters are converted according the rules specified in
|
|
par. 3.3.2.2. E.i. the parameters are converted to the prototype
|
|
counter parts as if by assignment. For the parameters falling
|
|
under ellipsis clause the old parameters conversion stuff
|
|
applies.
|
|
*/
|
|
register struct expr *left = (*expp)->OP_LEFT;
|
|
register struct expr *right = (*expp)->OP_RIGHT;
|
|
register struct proto *pl = NO_PROTO;
|
|
static struct proto ellipsis = { 0, 0, 0, PL_ELLIPSIS };
|
|
|
|
if (left != NILEXPR) { /* in case of an error */
|
|
register struct type *tp = left->ex_type;
|
|
|
|
while (tp && tp->tp_fund != FUNCTION)
|
|
tp = tp->tp_up;
|
|
if (tp && tp->tp_proto)
|
|
pl = tp->tp_proto;
|
|
}
|
|
|
|
if (right != NILEXPR) { /* function call with parameters */
|
|
register struct expr **ep = &((*expp)->OP_RIGHT);
|
|
register int ecnt = 0, pcnt = 0;
|
|
struct expr **estack[NPARAMS];
|
|
struct proto *pstack[NPARAMS];
|
|
|
|
/* stack up the parameter expressions */
|
|
while (right->ex_class == Oper && right->OP_OPER == PARCOMMA) {
|
|
if (ecnt == STDC_NPARAMS)
|
|
expr_strict(right, "number of parameters exceeds ANSI limit");
|
|
if (ecnt >= NPARAMS-1) {
|
|
expr_error(right, "too many parameters");
|
|
return;
|
|
}
|
|
estack[ecnt++] = &(right->OP_RIGHT);
|
|
ep = &(right->OP_LEFT);
|
|
right = right->OP_LEFT;
|
|
}
|
|
estack[ecnt] = ep;
|
|
|
|
/* Declarations like int f(void) do not expect any
|
|
parameters.
|
|
*/
|
|
if (pl && pl->pl_flag & PL_VOID) {
|
|
expr_strict(*expp, "no parameters expected");
|
|
pl = NO_PROTO;
|
|
}
|
|
|
|
/* stack up the prototypes */
|
|
if (pl) {
|
|
pcnt--;
|
|
do {
|
|
/* stack prototypes */
|
|
pstack[++pcnt] = pl;
|
|
pl = pl->next;
|
|
} while (pl);
|
|
}
|
|
else {
|
|
pstack[0] = &ellipsis;
|
|
}
|
|
|
|
for (ecnt; ecnt >= 0; ecnt--) {
|
|
/* Only the parameters specified in the prototype
|
|
are checked and converted. The parameters that
|
|
fall under the ellipsis clause are neither
|
|
checked nor converted !
|
|
*/
|
|
if (pcnt < 0) {
|
|
expr_error(*expp, "more parameters than specified in prototype");
|
|
break;
|
|
}
|
|
else if (!(pstack[pcnt]->pl_flag & PL_ELLIPSIS)) {
|
|
ch3cast(estack[ecnt],CASTAB,pstack[pcnt]->pl_type);
|
|
pcnt--;
|
|
} else
|
|
any2parameter(estack[ecnt]);
|
|
}
|
|
if (pcnt > 0 || (pcnt == 0 && !(pstack[0]->pl_flag & PL_ELLIPSIS)))
|
|
expr_error(*expp, "fewer parameters than specified in prototype");
|
|
} else {
|
|
if (pl && !(pl->pl_flag & PL_VOID))
|
|
expr_error(*expp, "fewer parameters than specified in prototype");
|
|
}
|
|
}
|