aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Forney <mforney@mforney.org>2020-06-03 03:22:11 -0700
committerMichael Forney <mforney@mforney.org>2020-06-03 03:22:11 -0700
commit38b1d570ac9f35af9b58e7ff33d61698b83384ff (patch)
tree2a8a03923d5e786205658fdae923f77ce4c8b835
parent662a5dd71ed37fd8938f0da4f64149bcf07ba154 (diff)
Revert "decl: Allow out-of-range enum constants when they don't change type"
This reverts commit 6229709b8ae21d7722fef48ad8a9f2f10b900030. I still don't understand how out-of-range enum constants are supposed to work.
-rw-r--r--cc.h2
-rw-r--r--decl.c85
-rw-r--r--type.c17
3 files changed, 45 insertions, 59 deletions
diff --git a/cc.h b/cc.h
index 32169df..b9df86c 100644
--- a/cc.h
+++ b/cc.h
@@ -147,6 +147,7 @@ enum typekind {
TYPECHAR,
TYPESHORT,
TYPEINT,
+ TYPEENUM,
TYPELONG,
TYPELLONG,
TYPEFLOAT,
@@ -171,7 +172,6 @@ enum typeprop {
PROPAGGR = 1<<6,
PROPDERIVED = 1<<7,
PROPFLOAT = 1<<8,
- PROPENUM = 1<<9,
};
struct param {
diff --git a/decl.c b/decl.c
index bcea7f7..94e4b15 100644
--- a/decl.c
+++ b/decl.c
@@ -1,5 +1,4 @@
#include <assert.h>
-#include <limits.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stddef.h>
@@ -144,35 +143,22 @@ funcspec(enum funcspec *fs)
static void structdecl(struct scope *, struct structbuilder *);
-static unsigned long long
-intmax(struct type *t)
-{
- return -1ull >> sizeof(unsigned long long) * CHAR_BIT - t->size * 8 + t->basic.issigned;
-}
-
-static bool
-inrange(struct type *t, unsigned long long v, bool sign)
-{
- if (sign && v >= 1ull << 63)
- return t->basic.issigned && v >= -1ull << t->size * 8 - 1;
- return v <= intmax(t);
-}
-
static struct type *
tagspec(struct scope *s)
{
- struct type *t, *et, *ct;
+ struct type *t;
char *tag, *name;
enum typekind kind;
struct decl *d;
struct expr *e;
struct structbuilder b;
uint64_t i;
+ bool large;
switch (tok.kind) {
case TSTRUCT: kind = TYPESTRUCT; break;
case TUNION: kind = TYPEUNION; break;
- case TENUM: kind = TYPEINT; break;
+ case TENUM: kind = TYPEENUM; break;
default: fatal("internal error: unknown tag kind");
}
next();
@@ -182,28 +168,30 @@ tagspec(struct scope *s)
} else {
tag = expect(TIDENT, "or '{' after struct/union");
t = scopegettag(s, tag, false);
- if (s->parent && !t && tok.kind != TLBRACE && (kind == TYPEINT || tok.kind != TSEMICOLON))
+ if (s->parent && !t && tok.kind != TLBRACE && (kind == TYPEENUM || tok.kind != TSEMICOLON))
t = scopegettag(s->parent, tag, true);
}
- if (!t) {
- t = mktype(kind, PROPOBJECT);
- t->size = 0;
- t->align = 0;
- if (kind == TYPEINT) {
- t->prop |= PROPENUM;
- t->basic.issigned = false;
+ if (t) {
+ if (t->kind != kind)
+ error(&tok.loc, "redeclaration of tag '%s' with different kind", tag);
+ } else {
+ if (kind == TYPEENUM) {
+ t = xmalloc(sizeof(*t));
+ *t = typeuint;
+ t->kind = kind;
} else {
+ t = mktype(kind, PROPOBJECT);
if (kind == TYPESTRUCT)
t->prop |= PROPAGGR;
t->repr = &i64; // XXX
+ t->size = 0;
+ t->align = 0;
t->structunion.tag = tag;
t->structunion.members = NULL;
}
t->incomplete = true;
if (tag)
scopeputtag(s, tag, t);
- } else if (t->kind != kind && (kind != TYPEINT || t->prop & PROPENUM)) {
- error(&tok.loc, "redeclaration of tag '%s' with different kind", tag);
}
if (tok.kind != TLBRACE)
return t;
@@ -222,9 +210,8 @@ tagspec(struct scope *s)
t->size = ALIGNUP(t->size, t->align);
t->incomplete = false;
break;
- case TYPEINT:
- et = NULL;
- ct = &typeint;
+ case TYPEENUM:
+ large = false;
for (i = 0; tok.kind == TIDENT; ++i) {
name = tok.lit;
next();
@@ -233,31 +220,31 @@ tagspec(struct scope *s)
if (e->kind != EXPRCONST || !(e->type->prop & PROPINT))
error(&tok.loc, "expected integer constant expression");
i = e->constant.i;
- t->basic.issigned |= e->type->basic.issigned && i >= 1ull << 63;
- if (inrange(&typeint, i, e->type->basic.issigned))
- ct = &typeint;
- else if (et && !typecompatible(et, e->type))
- error(&tok.loc, "enumerator '%s' value is outside the range of 'int'", name);
- else
- ct = et = e->type;
- } else if (i - 1 == intmax(ct)) {
- /* the constant is outside the range of the type of the previous enumerator */
- error(&tok.loc, "enumerator '%s' value overflow", name);
+ if (e->type->basic.issigned && i >= 1ull << 63) {
+ if (i < -1ull << 31)
+ goto invalid;
+ t->basic.issigned = true;
+ } else if (i >= 1ull << 32) {
+ goto invalid;
+ }
+ } else if (i == 1ull << 32) {
+ invalid:
+ error(&tok.loc, "enumerator '%s' value cannot be represented as 'int' or 'unsigned int'", name);
}
- d = mkdecl(DECLCONST, ct, QUALNONE, LINKNONE);
- d->value = mkintconst(ct->repr, i);
+ d = mkdecl(DECLCONST, &typeint, QUALNONE, LINKNONE);
+ d->value = mkintconst(t->repr, i);
+ if (i >= 1ull << 31 && i < 1ull << 63) {
+ large = true;
+ d->type = &typeuint;
+ }
+ if (large && t->basic.issigned)
+ error(&tok.loc, "neither 'int' nor 'unsigned' can represent all enumerator values");
scopeputdecl(s, name, d);
if (!consume(TCOMMA))
break;
}
expect(TRBRACE, "to close enum specifier");
- if (!et)
- et = t->basic.issigned ? &typeint : &typeuint;
- else if (t->basic.issigned && !et->basic.issigned)
- error(&tok.loc, "enumerator with range outside of 'int' is not compatible with enum type");
- *t = *et;
- t->prop |= PROPENUM;
- break;
+ t->incomplete = false;
}
return t;
diff --git a/type.c b/type.c
index 14ac033..87f8094 100644
--- a/type.c
+++ b/type.c
@@ -106,6 +106,7 @@ typerank(struct type *t)
case TYPEBOOL: return 1;
case TYPECHAR: return 2;
case TYPESHORT: return 3;
+ case TYPEENUM:
case TYPEINT: return 4;
case TYPELONG: return 5;
case TYPELLONG: return 6;
@@ -122,16 +123,14 @@ typecompatible(struct type *t1, struct type *t2)
if (t1 == t2)
return true;
- if (t1->kind != t2->kind)
- return false;
- switch (t1->kind) {
- case TYPEINT:
- case TYPELONG:
- case TYPELLONG:
- /* enum types are compatible with some basic integer
- type, but not with other enum types */
- return (t1->prop & PROPENUM) != (t2->prop & PROPENUM) &&
+ if (t1->kind != t2->kind) {
+ /* enum types are compatible with 'int', but not with
+ each other (unless they are the same type) */
+ return (t1->kind == TYPEENUM && t2->kind == TYPEINT ||
+ t1->kind == TYPEINT && t2->kind == TYPEENUM) &&
t1->basic.issigned == t2->basic.issigned;
+ }
+ switch (t1->kind) {
case TYPEPOINTER:
goto derived;
case TYPEARRAY: