shithub: scc

Download patch

ref: b45fa07261e9c67a83dd910dfcfc44a3d8c41d1e
parent: 5c6da024b4f87d1a4d91cfa818f8963ec7e03595
author: Roberto E. Vargas Caballero <k0ga@shike2.com>
date: Mon Aug 10 13:34:04 EDT 2015

Second rewritten of decl.c

This rewritten add the semantic for symbol linkage.

--- 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.
--- a/cc1/cc1.h
+++ b/cc1/cc1.h
@@ -109,7 +109,7 @@
 	FTN = 1,
 	PTR,
 	ARY,
-	PARS,
+	PARS
 };
 
 /* namespaces */
@@ -125,17 +125,18 @@
 
 /* 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
 };
 
 
--- a/cc1/code.c
+++ b/cc1/code.c
@@ -163,6 +163,8 @@
 		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 @@
 
 	if (tp->printed)
 		return;
+	tp->printed = 1;
 
 	switch (tp->op) {
 	case ARY:
@@ -253,6 +256,8 @@
 {
 	Symbol *sym = arg;
 
+	if (sym->flags & ISEMITTED)
+		return;
 	emittype(sym->type);
 	emitvar(sym);
 	putchar('\t');
@@ -259,6 +264,7 @@
 	emitletter(sym->type);
 	if (op != OFUN)
 		putchar('\n');
+	sym->flags |= ISEMITTED;
 }
 
 static void
--- a/cc1/cpp.c
+++ b/cc1/cpp.c
@@ -30,7 +30,7 @@
 
 	strcpy(yytext, s);
 	sym = lookup(NS_CPP);
-	sym->flags |= ISDEFINED;
+	sym->flags |= ISDECLARED;
 	return sym;
 }
 
@@ -326,13 +326,13 @@
 	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 @@
 		}
 		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 @@
 		return;
 	}
 	sym = lookup(NS_CPP);
-	sym->flags &= ~ISDEFINED;
+	sym->flags &= ~ISDECLARED;
 	next();
 }
 
--- a/cc1/decl.c
+++ b/cc1/decl.c
@@ -2,6 +2,7 @@
 #include <inttypes.h>
 #include <setjmp.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 
 #include "../inc/sizes.h"
@@ -8,6 +9,9 @@
 #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 @@
 	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 @@
 		}
 	}
 
-	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 @@
 }
 
 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 @@
 			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 @@
 	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,27 +431,39 @@
 	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");
@@ -456,57 +470,136 @@
 	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 == 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 (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 (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;
@@ -513,7 +606,7 @@
 	int sclass;
 
 	if ((base = specifier(&sclass)) == NULL) {
-		if (curctx != 0)
+		if (curctx != GLOBALCTX)
 			unexpected();
 		warn("type defaults to 'int' in declaration");
 		base = inttype;
@@ -520,33 +613,10 @@
 	}
 
 	do {
-		sym = declarator(base, ns, sclass);
-		tp = sym->type;
+		sym = declarator(base, ns, &tp);
+		sym = (*fun)(sym, tp, ns, sclass, data);
+	} while (rep && accept(','));
 
-		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(','));
-
 	return sym;
 }
 
@@ -553,14 +623,45 @@
 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
--- a/cc1/error.c
+++ b/cc1/error.c
@@ -51,6 +51,7 @@
 	va_start(va, fmt);
 	warn_helper(-1, fmt, va);
 	va_end(va);
+	exit(1);
 	discard();
 }
 
--- a/cc1/expr.c
+++ b/cc1/expr.c
@@ -721,9 +721,9 @@
 		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;
--- a/cc1/stmt.c
+++ b/cc1/stmt.c
@@ -28,9 +28,9 @@
 		 * 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(':');
--- a/cc1/symbol.c
+++ b/cc1/symbol.c
@@ -67,7 +67,7 @@
 		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 @@
 		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 @@
 	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 @@
 	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 @@
 			return sym;
 	}
 	new = newsym(ns);
-	new->flags &= ~ISDEFINED;
+	new->flags &= ~ISDECLARED;
 	new->name = xstrdup(yytext);
 	new->hash = sym->hash;
 	return sym->hash = new;
@@ -188,9 +189,9 @@
 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;
@@ -197,7 +198,7 @@
 
 		sym = newsym(ns);
 		sym->name = xstrdup(name);
-		h = &htab[hash(yytext)];
+		h = &htab[hash(name)];
 		sym->hash = *h;
 		*h = sym;
 	}
--- a/cc1/types.c
+++ b/cc1/types.c
@@ -283,6 +283,7 @@
 
 	type.type = tp;
 	type.op = op;
+	type.printed = 0;
 	type.letter = letters[op];
 	type.pars = pars;
 	type.n.elem = nelem;
@@ -351,7 +352,8 @@
 		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);
--- a/inc/cc.h
+++ b/inc/cc.h
@@ -65,6 +65,7 @@
 #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);