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, } | 
