commit b45fa07261e9c67a83dd910dfcfc44a3d8c41d1e
parent 5c6da024b4f87d1a4d91cfa818f8963ec7e03595
Author: Roberto E. Vargas Caballero <k0ga@shike2.com>
Date: Mon, 10 Aug 2015 17:34:04 +0200
Second rewritten of decl.c
This rewritten add the semantic for symbol linkage.
Diffstat:
11 files changed, 272 insertions(+), 158 deletions(-)
diff --git a/cc1/TODO b/cc1/TODO
@@ -1,7 +1,6 @@
* Verify correctness in initializators
* emit initializators
* emit structures definition
-* Allow external declarations of incomplete array types
* Implement bitfields
* Define data structure shared between cc1 and cc2 with the type
information
@@ -11,3 +10,5 @@
* Parse correctly all integer and float constants
* Add C99 features (almost all the new features of C99 are missed)
* Add correct emit for any kind of constant
+* Add warnings in overflow of contant operations
+* Add warning when some ANSI limit is violated.
diff --git a/cc1/cc1.h b/cc1/cc1.h
@@ -109,7 +109,7 @@ enum {
FTN = 1,
PTR,
ARY,
- PARS,
+ PARS
};
/* namespaces */
@@ -125,17 +125,18 @@ enum {
/* symbol flags */
enum {
- ISSTATIC = 1,
- ISAUTO = 2,
- ISREGISTER = 4,
- ISDEFINED = 8,
- ISFIELD = 16,
- ISEXTERN = 32,
- ISUSED = 64,
- ISCONSTANT = 128,
- ISGLOBAL = 256,
- ISPRIVATE = 512,
- ISLOCAL = 1024
+ ISAUTO = 1,
+ ISREGISTER = 2,
+ ISDECLARED = 4,
+ ISFIELD = 8,
+ ISEXTERN = 16,
+ ISUSED = 32,
+ ISCONSTANT = 64,
+ ISGLOBAL = 128,
+ ISPRIVATE = 256,
+ ISLOCAL = 512,
+ ISEMITTED = 1024,
+ ISDEFINED = 2048
};
diff --git a/cc1/code.c b/cc1/code.c
@@ -163,6 +163,8 @@ emitvar(Symbol *sym)
c = L_REGISTER;
else if (flags & ISFIELD)
c = L_FIELD;
+ else if (flags & ISEXTERN)
+ c = L_EXTERN;
else
c = L_AUTO;
printf("%c%d", c, sym->id);
@@ -222,6 +224,7 @@ emittype(Type *tp)
if (tp->printed)
return;
+ tp->printed = 1;
switch (tp->op) {
case ARY:
@@ -253,12 +256,15 @@ emitdcl(unsigned op, void *arg)
{
Symbol *sym = arg;
+ if (sym->flags & ISEMITTED)
+ return;
emittype(sym->type);
emitvar(sym);
putchar('\t');
emitletter(sym->type);
if (op != OFUN)
putchar('\n');
+ sym->flags |= ISEMITTED;
}
static void
diff --git a/cc1/cpp.c b/cc1/cpp.c
@@ -30,7 +30,7 @@ defmacro(char *s)
strcpy(yytext, s);
sym = lookup(NS_CPP);
- sym->flags |= ISDEFINED;
+ sym->flags |= ISDECLARED;
return sym;
}
@@ -326,13 +326,13 @@ define(void)
if (yytoken != IDEN)
error("macro names must be identifiers");
sym = yylval.sym;
- if ((sym->flags & ISDEFINED) && sym->ns == NS_CPP) {
+ if ((sym->flags & ISDECLARED) && sym->ns == NS_CPP) {
warn("'%s' redefined", yytext);
free(sym->u.s);
} else if (sym->ns != NS_CPP) {
sym = lookup(NS_CPP);
}
- sym->flags |= ISDEFINED;
+ sym->flags |= ISDECLARED;
pushctx();
@@ -469,7 +469,7 @@ ifclause(int negate, int isifdef)
}
sym = lookup(NS_CPP);
next();
- status = (sym->flags & ISDEFINED) != 0;
+ status = (sym->flags & ISDECLARED) != 0;
} else {
if ((expr = iconstexpr()) == NULL)
error("parameter of #if is not an integer constant expression");
@@ -540,7 +540,7 @@ undef(void)
return;
}
sym = lookup(NS_CPP);
- sym->flags &= ~ISDEFINED;
+ sym->flags &= ~ISDECLARED;
next();
}
diff --git a/cc1/decl.c b/cc1/decl.c
@@ -2,12 +2,16 @@
#include <inttypes.h>
#include <setjmp.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include "../inc/sizes.h"
#include "../inc/cc.h"
#include "cc1.h"
+#define GLOBALCTX 0
+#define NOSCLASS 0
+
struct dcldata {
unsigned char op;
unsigned short nelem;
@@ -49,40 +53,65 @@ arydcl(struct dcldata *dp)
return queue(dp, ARY, n, NULL);
}
-static void
-parameter(Symbol *sym, int sclass, Type *data)
+static Symbol *
+parameter(Symbol *sym, Type *tp, unsigned ns, int sclass, Type *data)
{
- Type *tp = sym->type, *funtp = data;
+ Type *funtp = data;
size_t n = funtp->n.elem;
+ char *name = sym->name;
- if (tp == voidtype) {
- if (n != 0)
- error("incorrect void parameter");
- funtp->n.elem = -1;
- return;
- }
+ sym->type = tp;
if (n == -1)
error("'void' must be the only parameter");
- tp = sym->type;
- if (tp->op == FTN)
- error("incorrect function type for a function parameter");
- if (tp->op == ARY)
- tp = mktype(tp->type, PTR, 0, NULL);
- if (sym->flags & (ISSTATIC|ISEXTERN|ISAUTO))
+
+ switch (sclass) {
+ case STATIC:
+ case EXTERN:
+ case AUTO:
error("bad storage class in function parameter");
- if (!sclass)
+ case REGISTER:
+ sym->flags |= ISREGISTER;
+ break;
+ case NOSCLASS:
sym->flags |= ISAUTO;
+ break;
+ }
+
+ switch (tp->op) {
+ case VOID:
+ if (n != 0)
+ error("incorrect void parameter");
+ if (sclass)
+ error("void as unique parameter may not be qualified");
+ funtp->n.elem = -1;
+ return NULL;
+ case ARY:
+ tp = mktype(tp->type, PTR, 0, NULL);
+ break;
+ case FTN:
+ error("incorrect function type for a function parameter");
+ }
+
+ if (name) {
+ if ((sym = install(NS_IDEN, sym)) == NULL)
+ error("redefinition of parameter '%s'", name);
+ }
+ sym->type = tp;
+
if (n++ == NR_FUNPARAM)
error("too much parameters in function definition");
funtp->pars = xrealloc(funtp->pars, n * sizeof(Type *));
funtp->pars[n-1] = tp;
funtp->n.elem = n;
+
+ return sym;
}
static Symbol *dodcl(int rep,
- void (*fun)(Symbol *, int, Type *),
- uint8_t ns, Type *type);
+ Symbol *(*fun)(Symbol *, Type *, unsigned, int, Type *),
+ unsigned ns,
+ Type *type);
static struct dcldata *
fundcl(struct dcldata *dp)
@@ -114,18 +143,6 @@ fundcl(struct dcldata *dp)
}
}
- switch (yytoken) {
- default:
- /* This is not a function */
- popctx();
- case '{':
- case TYPEIDEN:
- case TYPE:
- case TQUALIFIER:
- case SCLASS:
- /* This can be a function (K&R included) */
- break;
- }
return dp;
}
@@ -177,7 +194,7 @@ declarator0(struct dcldata *dp, unsigned ns)
}
static Symbol *
-declarator(Type *tp, unsigned ns, int sclass)
+declarator(Type *tp, unsigned ns, Type **otp)
{
struct dcldata data[NR_DECLARATORS+1];
struct dcldata *bp;
@@ -198,26 +215,11 @@ declarator(Type *tp, unsigned ns, int sclass)
break;
}
}
-
- if ((name = sym->name) == NULL) {
- sym->type = tp;
- } else {
- short flags;
-
- if ((sym = install(ns, osym = sym)) == NULL) {
- if (!eqtype(osym->type, tp))
- error("conflicting types for '%s'", name);
- sym = osym;
- } else {
- sym->u.pars = pars;
- sym->type = tp;
- }
- if (!tp->defined && sclass != EXTERN) {
- error("declared variable '%s' of incomplete type",
- name);
- }
- }
-
+ /*
+ * FIXME: This assignation can destroy pars of a previous definition
+ */
+ sym->u.pars = pars;
+ *otp = tp;
return sym;
}
@@ -343,7 +345,7 @@ newtag(void)
case IDEN:
case TYPEIDEN:
sym = yylval.sym;
- if ((sym->flags & ISDEFINED) == 0)
+ if ((sym->flags & ISDECLARED) == 0)
install(NS_TAG, yylval.sym);
next();
break;
@@ -429,123 +431,191 @@ enumdcl(void)
return tp;
}
-static void
-type(Symbol *sym, int sclass, Type *data)
+static Symbol *
+type(Symbol *sym, Type *tp, unsigned ns, int sclass, Type *data)
{
if (sclass)
error("class storage in type name");
if (sym->name)
error("unexpected identifier in type name");
+ sym->type = tp;
+
+ return sym;
}
-static void
-field(Symbol *sym, int sclass, Type *data)
+static Symbol *
+field(Symbol *sym, Type *tp, unsigned ns, int sclass, Type *data)
{
- Type *tp = sym->type, *funtp = data;
+ Type *funtp = data;
size_t n = funtp->n.elem;
+ char *name = sym->name;
if (sclass)
error("storage class in struct/union field");
if (!sym->name)
error("missed identifier in field declaration");
+
if (tp->op == FTN)
error("invalid type in struct/union");
+ if (!tp->defined)
+ error("field '%s' has incomplete type", name);
+
+ if ((sym = install(ns, sym)) == NULL)
+ error("duplicated member '%s'", name);
+ sym->type = tp;
+
sym->flags |= ISFIELD;
if (n++ == NR_FUNPARAM)
error("too much fields in struct/union");
funtp->pars = xrealloc(funtp->pars, n);
funtp->pars[n-1] = tp;
funtp->n.elem = n;
+
+ return sym;
}
-static void
-internal(Symbol *sym, int sclass, Type *data)
+static Symbol *
+identifier(Symbol *sym, Type *tp, unsigned ns, int sclass, Type *data)
{
- if (!sym->name) {
+ char *name = sym->name;
+ short flags;
+ Symbol *osym;
+
+ if (!name) {
+ sym->type = tp;
warn("empty declaration");
- return;
+ return sym;
}
- if (sym->type->op != FTN) {
- if (!sclass)
- sym->flags |= ISAUTO;
- if (accept('='))
- initializer(sym);
- /* TODO: check if the variable is extern and has initializer */
- }
- if (sym->flags & ISSTATIC)
- sym->flags |= ISLOCAL;
- emit(ODECL, sym);
-}
-static void
-external(Symbol *sym, int sclass, Type *data)
-{
- if (!sym->name) {
- warn("empty declaration");
- return;
+ /* TODO: Add warning about ANSI limits */
+ if (!tp->defined && sclass != EXTERN)
+ error("declared variable '%s' of incomplete type", name);
+
+ if (tp->op == FTN) {
+ if (sclass == NOSCLASS)
+ sclass = EXTERN;
+ /*
+ * FIXME: Ugly workaround to solve function declarations.
+ * A new context is added for the parameters,
+ * so at this point curctx is incremented by
+ * one when sym was parsed.
+ * It can destroy the order of the hash when
+ * there is a previous declaration in an outer contex.
+ */
+ --curctx;
+ sym = install(NS_IDEN, sym);
+ ++curctx;
+ } else {
+ sym = install(NS_IDEN, osym = sym);
}
- if (sym->flags & (ISREGISTER|ISAUTO))
- error("incorrect storage class for file-scope declaration");
- sym->flags |= (sym->flags & ISSTATIC) ? ISPRIVATE : ISGLOBAL;
-
- if (sym->type->op == FTN && yytoken == '{') {
- if (sym->token == TYPEIDEN)
- error("function definition declared 'typedef'");
- curfun = sym;
- sym->flags |= ISDEFINED;
- emit(OFUN, sym);
- compound(NULL, NULL, NULL);
- emit(OEFUN, NULL);
- return;
+ if (sym == NULL) {
+ sym = osym;
+ flags = sym->flags;
+ if (!eqtype(sym->type, tp))
+ error("conflicting types for '%s'", name);
+ if (sym->token == TYPEIDEN && sclass != TYPEDEF ||
+ sym->token != TYPEIDEN && sclass == TYPEDEF)
+ goto redeclaration;
+
+ if (curctx != GLOBALCTX && tp->op != FTN) {
+ if (!(sym->flags & ISEXTERN) || sclass != EXTERN)
+ goto redeclaration;
+ } else {
+ switch (sclass) {
+ case REGISTER:
+ case AUTO:
+ goto bad_storage;
+ case NOSCLASS:
+ if (flags & ISPRIVATE)
+ goto non_after_static;
+ flags &= ~ISEXTERN;
+ flags |= ISGLOBAL;
+ case TYPEDEF:
+ case EXTERN:
+ break;
+ case STATIC:
+ if (flags & (ISGLOBAL|ISEXTERN))
+ goto static_after_non;
+ flags |= ISPRIVATE;
+ break;
+ }
+ }
+ } else {
+ flags = sym->flags;
+ switch (sclass) {
+ case REGISTER:
+ case AUTO:
+ if (curctx == GLOBALCTX || tp->op == FTN)
+ goto bad_storage;
+ flags |= (sclass == REGISTER) ? ISREGISTER : ISAUTO;
+ break;
+ case NOSCLASS:
+ flags |= (curctx == GLOBALCTX) ? ISGLOBAL : ISAUTO;
+ break;
+ case EXTERN:
+ flags |= ISEXTERN;
+ break;
+ case STATIC:
+ flags |= (curctx == GLOBALCTX) ? ISPRIVATE : ISLOCAL;
+ break;
+ case TYPEDEF:
+ sym->token = TYPEIDEN;
+ break;
+ }
}
+
+ sym->flags = flags;
+ sym->type = tp;
+
if (accept('='))
initializer(sym);
+ /* TODO: disallow initializators in functions */
+ /* TODO: check if typedef has initializer */
/* TODO: check if the variable is extern and has initializer */
- emit(ODECL, sym);
+ if (sym->token == IDEN)
+ emit(ODECL, sym);
+ return sym;
+
+redeclaration:
+ error("redeclaration of '%s'", name);
+
+bad_storage:
+ if (tp->op != FTN)
+ error("incorrect storage class for file-scope declaration");
+bad_function:
+ error("invalid storage class for function '%s'", name);
+
+non_after_static:
+ error("non-static declaration of '%s' follows static declaration",
+ name);
+
+static_after_non:
+ error("static declaration of '%s' follows non-static declaration",
+ name);
}
static Symbol *
-dodcl(int rep, void (*fun)(Symbol *, int, Type *), uint8_t ns, Type *type)
+dodcl(int rep,
+ Symbol *(*fun)(Symbol *, Type *, unsigned, int, Type *),
+ unsigned ns,
+ Type *data)
{
Symbol *sym;
Type *base, *tp;
int sclass;
if ((base = specifier(&sclass)) == NULL) {
- if (curctx != 0)
+ if (curctx != GLOBALCTX)
unexpected();
warn("type defaults to 'int' in declaration");
base = inttype;
}
do {
- sym = declarator(base, ns, sclass);
- tp = sym->type;
-
- switch (sclass) {
- case REGISTER:
- sym->flags |= ISREGISTER;
- break;
- case AUTO:
- sym->flags |= ISAUTO;
- break;
- case STATIC:
- sym->flags |= ISSTATIC;
- break;
- case EXTERN:
- sym->flags |= ISEXTERN;
- break;
- case TYPEDEF:
- sym->token = TYPEIDEN;
- break;
- }
- if (tp->op == FTN && (sym->flags & (ISREGISTER|ISAUTO))) {
- error("invalid storage class for function '%s'",
- sym->name);
- }
- (*fun)(sym, sclass, type);
- } while (rep && !curfun && accept(','));
+ sym = declarator(base, ns, &tp);
+ sym = (*fun)(sym, tp, ns, sclass, data);
+ } while (rep && accept(','));
return sym;
}
@@ -553,14 +623,45 @@ dodcl(int rep, void (*fun)(Symbol *, int, Type *), uint8_t ns, Type *type)
void
decl(void)
{
+ Symbol *sym;
+
if (accept(';'))
return;
- if (!dodcl(1, (curctx == 0) ? external : internal, NS_IDEN, NULL))
- return;
- if (curfun)
- curfun == NULL;
- else
- expect(';');
+ sym = dodcl(1, identifier, NS_IDEN, NULL);
+
+ /*
+ * Functions only can appear at global context,
+ * but due to parameter context, we have to check
+ * against GLOBALCTX+1
+ */
+ if (sym->type->op == FTN && curctx == GLOBALCTX+1) {
+ switch (yytoken) {
+ case '{':
+ case TYPEIDEN:
+ case TYPE:
+ case TQUALIFIER:
+ case SCLASS:
+ if (sym->token == TYPEIDEN)
+ error("function definition declared 'typedef'");
+ if (sym->flags & ISDEFINED)
+ error("redefinition of '%s'", sym->name);
+ if (sym->flags & ISEXTERN) {
+ sym->flags &= ~ISEXTERN;
+ sym->flags |= ISGLOBAL;
+ }
+ sym->flags |= ISDEFINED;
+ sym->flags &= ~ISEMITTED;
+ curfun = sym;
+ emit(OFUN, sym);
+ compound(NULL, NULL, NULL);
+ emit(OEFUN, NULL);
+ curfun = NULL;
+ return;
+ default:
+ popctx();
+ }
+ }
+ expect(';');
}
static void
diff --git a/cc1/error.c b/cc1/error.c
@@ -51,6 +51,7 @@ error(char *fmt, ...)
va_start(va, fmt);
warn_helper(-1, fmt, va);
va_end(va);
+ exit(1);
discard();
}
diff --git a/cc1/expr.c b/cc1/expr.c
@@ -721,9 +721,9 @@ primary(void)
next();
break;
case IDEN:
- if (!(yylval.sym->flags & ISDEFINED)) {
+ if (!(yylval.sym->flags & ISDECLARED)) {
yylval.sym->type = inttype;
- yylval.sym->flags |= ISDEFINED;
+ yylval.sym->flags |= ISDECLARED;
error("'%s' undeclared", yytext);
}
yylval.sym->flags |= ISUSED;
diff --git a/cc1/stmt.c b/cc1/stmt.c
@@ -28,9 +28,9 @@ label(void)
* an undefined symbol that is not going to be used ever.
*/
sym = lookup(NS_LABEL);
- if (sym->flags & ISDEFINED)
+ if (sym->flags & ISDECLARED)
error("label '%s' already defined", yytoken);
- sym->flags |= ISDEFINED;
+ sym->flags |= ISDECLARED;
emit(OLABEL, sym);
next();
expect(':');
diff --git a/cc1/symbol.c b/cc1/symbol.c
@@ -67,7 +67,7 @@ popctx(void)
case NS_LABEL:
if (curctx != 0)
goto save_symbol;
- if (sym->flags & ISDEFINED)
+ if (sym->flags & ISDECLARED)
break;
printerr("label '%s' is not defined", sym->name);
break;
@@ -87,10 +87,12 @@ popctx(void)
if (sym->name) {
short f = sym->flags;
htab[hash(sym->name)] = sym->hash;
- if ((f & (ISUSED|ISGLOBAL|ISDEFINED)) == ISDEFINED)
+ if ((f & (ISUSED|ISGLOBAL|ISDECLARED)) == ISDECLARED)
warn("'%s' defined but not used", sym->name);
}
free(sym->name);
+ if (sym->type && sym->type->op == FTN)
+ free(sym->u.pars);
free(sym);
}
hp->next = sym;
@@ -103,8 +105,7 @@ duptype(Type *base)
Type *tp = xmalloc(sizeof(*tp));
*tp = *base;
- if (tp->op == ARY)
- tp->id = (curctx) ? ++localcnt : ++globalcnt;
+ tp->id = (curctx) ? ++localcnt : ++globalcnt;
return tp;
}
@@ -118,7 +119,7 @@ newsym(unsigned ns)
sym->ns = ns;
sym->ctx = curctx;
sym->token = IDEN;
- sym->flags = ISDEFINED;
+ sym->flags = ISDECLARED;
sym->name = NULL;
sym->type = NULL;
sym->hash = NULL;
@@ -178,7 +179,7 @@ nextsym(Symbol *sym, unsigned ns)
return sym;
}
new = newsym(ns);
- new->flags &= ~ISDEFINED;
+ new->flags &= ~ISDECLARED;
new->name = xstrdup(yytext);
new->hash = sym->hash;
return sym->hash = new;
@@ -188,16 +189,16 @@ Symbol *
install(unsigned ns, Symbol *sym)
{
if (sym->ctx == curctx) {
- if (sym->flags & ISDEFINED)
+ if (sym->flags & ISDECLARED)
return NULL;
- sym->flags |= ISDEFINED;
+ sym->flags |= ISDECLARED;
} else {
char *name = sym->name;
Symbol **h;
sym = newsym(ns);
sym->name = xstrdup(name);
- h = &htab[hash(yytext)];
+ h = &htab[hash(name)];
sym->hash = *h;
*h = sym;
}
diff --git a/cc1/types.c b/cc1/types.c
@@ -283,6 +283,7 @@ mktype(Type *tp, unsigned op, short nelem, Type *pars[])
type.type = tp;
type.op = op;
+ type.printed = 0;
type.letter = letters[op];
type.pars = pars;
type.n.elem = nelem;
@@ -351,7 +352,8 @@ eqtype(Type *tp1, Type *tp2)
return 1;
case ENUM:
break;
- case INT: case FLOAT:
+ case INT:
+ case FLOAT:
return tp1->letter == tp2->letter;
default:
fputs("internal type error, aborting\n", stderr);
diff --git a/inc/cc.h b/inc/cc.h
@@ -65,6 +65,7 @@ typedef unsigned bool;
#define L_REGISTER 'R'
#define L_FIELD 'M'
#define L_AUTO 'A'
+#define L_EXTERN 'X'
extern void die(const char *fmt, ...);
extern void *xmalloc(size_t size);