cproc

Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.nihaljere.xyz/cproc
Log | Files | Refs | Submodules | README | LICENSE

commit 8bae8a47d5a6674b401da6429a3475b284699871
parent fb4efb47d1469d1b0212f04eba8f4e5ed328117f
Author: Michael Forney <mforney@mforney.org>
Date:   Sun, 11 Aug 2019 16:53:38 -0700

decl: Allow enumerator values up to UINT_MAX

Diffstat:
Mdecl.c | 12++++++++++--
Mdoc/extensions.md | 24++++++++++++++++++++++++
Atest/enum-large-value.c | 4++++
Atest/enum-large-value.qbe | 1+
4 files changed, 39 insertions(+), 2 deletions(-)

diff --git a/decl.c b/decl.c @@ -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 @@ -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 @@ -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 @@ -0,0 +1 @@ +export data $x = align 4 { w 0, }