scc

Simple C99 Compiler
Log | Files | Refs | README | LICENSE

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:
cc1/TODO | 3++-
cc1/cc1.h | 25+++++++++++++------------
cc1/code.c | 6++++++
cc1/cpp.c | 10+++++-----
cc1/decl.c | 353+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
cc1/error.c | 1+
cc1/expr.c | 4++--
cc1/stmt.c | 4++--
cc1/symbol.c | 19++++++++++---------
cc1/types.c | 4+++-
inc/cc.h | 1+
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);