diff options
-rw-r--r-- | decl.c | 12 | ||||
-rw-r--r-- | doc/extensions.md | 24 | ||||
-rw-r--r-- | test/enum-large-value.c | 4 | ||||
-rw-r--r-- | test/enum-large-value.qbe | 1 |
4 files changed, 39 insertions, 2 deletions
@@ -156,6 +156,7 @@ tagspec(struct scope *s) struct expr *e; struct structbuilder b; uint64_t i; + bool large; switch (tok.kind) { case TSTRUCT: kind = TYPESTRUCT; break; @@ -216,6 +217,7 @@ tagspec(struct scope *s) t->incomplete = false; break; case TYPEENUM: + large = false; for (i = 0; tok.kind == TIDENT; ++i) { name = tok.lit; next(); @@ -228,15 +230,21 @@ tagspec(struct scope *s) if (i < -1ull << 31) goto invalid; t->basic.issigned = true; - } else if (i >= 1ull << 31) { + } else if (i >= 1ull << 32) { goto invalid; } - } else if (i == 1ull << 31) { + } else if (i == 1ull << 32) { invalid: error(&tok.loc, "enumerator '%s' value cannot be represented as 'int'", name); } 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; diff --git a/doc/extensions.md b/doc/extensions.md index 9a7b81d..9d07972 100644 --- a/doc/extensions.md +++ b/doc/extensions.md @@ -32,6 +32,30 @@ rules. The name may contain characters not allowed in regular identifiers. - **`__builtin_va_list`**: Built-in suitable for implementing the `va_list` type. - **`__builtin_va_start`**: Built-in suitable for implementing the `va_start` macro. +### Enumerator values outside the range of `int` + +ISO C requires that enumerator values be in the range of `int`. GNU C +allows any integer value, at the cost of enumerator constants possibly +having different types inside and outside the `enum` specifier. + +In the following example, `A` has type `unsigned` inside the `enum` +specifier, and `long` outside, so both of the assertions fail. + +```c +enum E { + A = 0x80000000, + B = sizeof(A), + C = A < -1, + D = -1, +}; +_Static_assert(B == sizeof(A), "sizeof(A) changed"); +_Static_assert(C == A < -1, "signedness of typeof(A) changed"); +``` + +As a compromise, we allow enumerator values larger than `INT_MAX` and +less than or equal to `UINT_MAX`, but only if no other enumerators have +a negative value. This way, enumerator constants have a fixed type. + ## Missing ### Statement expressions diff --git a/test/enum-large-value.c b/test/enum-large-value.c new file mode 100644 index 0000000..3532062 --- /dev/null +++ b/test/enum-large-value.c @@ -0,0 +1,4 @@ +enum { + A = 0x80000000, +}; +int x = A < 0; diff --git a/test/enum-large-value.qbe b/test/enum-large-value.qbe new file mode 100644 index 0000000..a67781d --- /dev/null +++ b/test/enum-large-value.qbe @@ -0,0 +1 @@ +export data $x = align 4 { w 0, } |