diff options
author | Luc Van Oostenryck <luc.vanoostenryck@gmail.com> | 2018-10-05 03:31:53 +0200 |
---|---|---|
committer | Luc Van Oostenryck <luc.vanoostenryck@gmail.com> | 2018-10-05 03:31:53 +0200 |
commit | 3a53a9e392904b67680b8d4cd47acaf7934bfd16 (patch) | |
tree | e9cabb53f05f8f7dd212ed2dcf8d600fbeb5954e | |
parent | 220d83b94922e67ec912931c94e6346e742c5b8f (diff) | |
parent | 4686a668048fad7023a26d29b5a139c11c9a2848 (diff) | |
download | sparse-3a53a9e392904b67680b8d4cd47acaf7934bfd16.tar.gz |
Merge branch 'fix-enum-type' into tip
-rw-r--r-- | parse.c | 156 | ||||
-rw-r--r-- | symbol.h | 5 | ||||
-rw-r--r-- | validation/bug-rshift-ub.c | 16 | ||||
-rw-r--r-- | validation/builtin-overflow.c | 2 | ||||
-rw-r--r-- | validation/enum-base-type.c | 29 | ||||
-rw-r--r-- | validation/enum-bitwise-bad.c | 20 | ||||
-rw-r--r-- | validation/enum-bitwise-mixed.c | 29 | ||||
-rw-r--r-- | validation/enum-bitwise.c | 19 | ||||
-rw-r--r-- | validation/enum-bounds.c | 24 | ||||
-rw-r--r-- | validation/enum-init-constness.c | 9 | ||||
-rw-r--r-- | validation/enum-invalid.c | 11 | ||||
-rw-r--r-- | validation/enum-min-size.c | 29 | ||||
-rw-r--r-- | validation/enum-mismatch.c | 4 | ||||
-rw-r--r-- | validation/enum-same-type.c | 14 | ||||
-rw-r--r-- | validation/enum-sign-gcc.c | 63 | ||||
-rw-r--r-- | validation/enum-typecheck.c | 39 |
16 files changed, 405 insertions, 64 deletions
@@ -775,73 +775,103 @@ static struct token *union_specifier(struct token *token, struct decl_state *ctx return struct_union_enum_specifier(SYM_UNION, token, ctx, parse_union_declaration); } +/// +// safe right shift +// +// This allow to use a shift amount as big (or bigger) +// than the width of the value to be shifted, in which case +// the result is, of course, 0. +static unsigned long long rshift(unsigned long long val, unsigned int n) +{ + if (n >= (sizeof(val) * 8)) + return 0; + return val >> n; +} -typedef struct { - int x; - unsigned long long y; -} Num; +struct range { + long long neg; + unsigned long long pos; +}; -static void upper_boundary(Num *n, Num *v) +static void update_range(struct range *range, unsigned long long uval, struct symbol *vtype) { - if (n->x > v->x) - return; - if (n->x < v->x) { - *n = *v; - return; - } - if (n->y < v->y) - n->y = v->y; -} + long long sval = uval; -static void lower_boundary(Num *n, Num *v) -{ - if (n->x < v->x) - return; - if (n->x > v->x) { - *n = *v; - return; + if (is_signed_type(vtype) && (sval < 0)) { + if (sval < range->neg) + range->neg = sval; + } else { + if (uval > range->pos) + range->pos = uval; } - if (n->y > v->y) - n->y = v->y; } -static int type_is_ok(struct symbol *type, Num *upper, Num *lower) +static int type_is_ok(struct symbol *type, struct range range) { int shift = type->bit_size; int is_unsigned = type->ctype.modifiers & MOD_UNSIGNED; if (!is_unsigned) shift--; - if (upper->x == 0 && upper->y >> shift) + if (rshift(range.pos, shift)) return 0; - if (lower->x == 0 || (!is_unsigned && (~lower->y >> shift) == 0)) + if (range.neg == 0) return 1; - return 0; + if (is_unsigned) + return 0; + if (rshift(~range.neg, shift)) + return 0; + return 1; } -static struct symbol *bigger_enum_type(struct symbol *s1, struct symbol *s2) +static struct range type_range(struct symbol *type) { - if (s1->bit_size < s2->bit_size) { - s1 = s2; - } else if (s1->bit_size == s2->bit_size) { - if (s2->ctype.modifiers & MOD_UNSIGNED) - s1 = s2; + struct range range; + unsigned int size = type->bit_size; + unsigned long long max; + long long min; + + if (is_signed_type(type)) { + min = sign_bit(size); + max = min - 1; + } else { + min = 0; + max = bits_mask(size); } - if (s1->bit_size < bits_in_int) - return &int_ctype; - return s1; + + range.pos = max; + range.neg = min; + return range; +} + +static int val_in_range(struct range *range, long long sval, struct symbol *vtype) +{ + unsigned long long uval = sval; + + if (is_signed_type(vtype) && (sval < 0)) + return range->neg <= sval; + else + return uval <= range->pos; } static void cast_enum_list(struct symbol_list *list, struct symbol *base_type) { + struct range irange = type_range(&int_ctype); struct symbol *sym; FOR_EACH_PTR(list, sym) { struct expression *expr = sym->initializer; struct symbol *ctype; + long long val; if (expr->type != EXPR_VALUE) continue; ctype = expr->ctype; + val = get_expression_value(expr); + if (is_int_type(ctype) && val_in_range(&irange, val, ctype)) { + expr->ctype = &int_ctype; + continue; + } + expr->ctype = base_type; if (ctype->bit_size == base_type->bit_size) continue; cast_value(expr, base_type, expr, ctype); @@ -852,7 +882,8 @@ static struct token *parse_enum_declaration(struct token *token, struct symbol * { unsigned long long lastval = 0; struct symbol *ctype = NULL, *base_type = NULL; - Num upper = {-1, 0}, lower = {1, 0}; + struct range range = { }; + int mix_bitwise = 0; parent->examined = 1; parent->ctype.base_type = &int_ctype; @@ -908,26 +939,29 @@ static struct token *parse_enum_declaration(struct token *token, struct symbol * * base type is at least "int_ctype". * - otherwise the base_type is "bad_ctype". */ - if (!base_type) { + if (!base_type || ctype == &bad_ctype) { base_type = ctype; } else if (ctype == base_type) { /* nothing */ } else if (is_int_type(base_type) && is_int_type(ctype)) { - base_type = bigger_enum_type(base_type, ctype); - } else + base_type = &int_ctype; + } else if (is_restricted_type(base_type) != is_restricted_type(ctype)) { + if (!mix_bitwise++) { + warning(expr->pos, "mixed bitwiseness"); + } + } else if (is_restricted_type(base_type) && base_type != ctype) { + sparse_error(expr->pos, "incompatible restricted type"); + info(expr->pos, " expected: %s", show_typename(base_type)); + info(expr->pos, " got: %s", show_typename(ctype)); base_type = &bad_ctype; + } else if (base_type != &bad_ctype) { + sparse_error(token->pos, "bad enum definition"); + base_type = &bad_ctype; + } parent->ctype.base_type = base_type; } if (is_int_type(base_type)) { - Num v = {.y = lastval}; - if (ctype->ctype.modifiers & MOD_UNSIGNED) - v.x = 0; - else if ((long long)lastval >= 0) - v.x = 0; - else - v.x = -1; - upper_boundary(&upper, &v); - lower_boundary(&lower, &v); + update_range(&range, lastval, ctype); } token = next; @@ -938,31 +972,31 @@ static struct token *parse_enum_declaration(struct token *token, struct symbol * token = token->next; } if (!base_type) { - sparse_error(token->pos, "bad enum definition"); + sparse_error(token->pos, "empty enum definition"); base_type = &bad_ctype; } else if (!is_int_type(base_type)) base_type = base_type; - else if (type_is_ok(base_type, &upper, &lower)) - base_type = base_type; - else if (type_is_ok(&int_ctype, &upper, &lower)) - base_type = &int_ctype; - else if (type_is_ok(&uint_ctype, &upper, &lower)) + else if (type_is_ok(&uint_ctype, range)) base_type = &uint_ctype; - else if (type_is_ok(&long_ctype, &upper, &lower)) - base_type = &long_ctype; - else if (type_is_ok(&ulong_ctype, &upper, &lower)) + else if (type_is_ok(&int_ctype, range)) + base_type = &int_ctype; + else if (type_is_ok(&ulong_ctype, range)) base_type = &ulong_ctype; - else if (type_is_ok(&llong_ctype, &upper, &lower)) - base_type = &llong_ctype; - else if (type_is_ok(&ullong_ctype, &upper, &lower)) + else if (type_is_ok(&long_ctype, range)) + base_type = &long_ctype; + else if (type_is_ok(&ullong_ctype, range)) base_type = &ullong_ctype; + else if (type_is_ok(&llong_ctype, range)) + base_type = &llong_ctype; else base_type = &bad_ctype; parent->ctype.base_type = base_type; parent->ctype.modifiers |= (base_type->ctype.modifiers & MOD_UNSIGNED); parent->examined = 0; + if (mix_bitwise) + return token; cast_enum_list(parent->symbol_list, base_type); return token; @@ -335,6 +335,11 @@ static inline struct symbol *get_base_type(const struct symbol *sym) return examine_symbol_type(sym->ctype.base_type); } +/// +// test if type is an integer type +// +// @return: ``1`` for plain integer type, enums & bitfields +// but ``0`` for bitwise types! static inline int is_int_type(const struct symbol *type) { if (type->type == SYM_NODE) diff --git a/validation/bug-rshift-ub.c b/validation/bug-rshift-ub.c new file mode 100644 index 00000000..7654abbd --- /dev/null +++ b/validation/bug-rshift-ub.c @@ -0,0 +1,16 @@ +enum a { + A = ~0ULL, +}; + +static enum a a = A; + +/* + * check-name: bug-rshift-ub + * check-description: + * This test trigger(ed) a bug on x86 caused by a + * full width shift (which is UB), expecting to get + * 0 but giving the unshifted value and as result + * the type is invalid: + * warning: incorrect type in initializer (invalid types) + * expected bad type enum a static [toplevel] a + */ diff --git a/validation/builtin-overflow.c b/validation/builtin-overflow.c index 867eb42f..a3dacc26 100644 --- a/validation/builtin-overflow.c +++ b/validation/builtin-overflow.c @@ -1,4 +1,4 @@ -enum e { OK }; +enum e { OK, KO = -1 }; typedef _Bool bool; static int test(int i, long l, long long ll, enum e e, bool b, void *p) diff --git a/validation/enum-base-type.c b/validation/enum-base-type.c new file mode 100644 index 00000000..cae801b1 --- /dev/null +++ b/validation/enum-base-type.c @@ -0,0 +1,29 @@ +enum n { + NA, + NB = 1L, + NC = 1UL, + ND = 1LL, + NE = 1ULL, + NF = -1, + NG = -1L, + NH = -1LL, +}; +_Static_assert(sizeof(enum n) == sizeof(int), "+-1"); + +enum m { + MA = 0L, + MB = 1L, + MG = -1L, +}; +_Static_assert(sizeof(enum m) == sizeof(int), "+-1L"); + +enum p { + PA = 0UL, + PB = 1UL, +}; +_Static_assert(sizeof(enum p) == sizeof(int), "UL"); + +/* + * check-name: enum-base-type + * check-command: sparse -m64 $file + */ diff --git a/validation/enum-bitwise-bad.c b/validation/enum-bitwise-bad.c new file mode 100644 index 00000000..6d31ca38 --- /dev/null +++ b/validation/enum-bitwise-bad.c @@ -0,0 +1,20 @@ +#define __bitwise __attribute__((bitwise)) +#define __force __attribute__((force)) + +typedef int __bitwise apple_t; +typedef int __bitwise orange_t; + +enum fruit { + A = (__force apple_t) 0, + B = (__force orange_t) 1, +}; + +/* + * check-name: enum-bitwise-bad + * + * check-error-start +enum-bitwise-bad.c:9:14: error: incompatible restricted type +enum-bitwise-bad.c:9:14: expected: restricted apple_t +enum-bitwise-bad.c:9:14: got: restricted orange_t + * check-error-end + */ diff --git a/validation/enum-bitwise-mixed.c b/validation/enum-bitwise-mixed.c new file mode 100644 index 00000000..07d77176 --- /dev/null +++ b/validation/enum-bitwise-mixed.c @@ -0,0 +1,29 @@ +#define __bitwise __attribute__((bitwise)) +#define __force __attribute__((force)) + +typedef long long __bitwise bits; + +enum a { + AR = (__force bits) 0, + AP = 0, + AS = (__force bits) 1, + AQ = 1, +}; +_Static_assert(sizeof(AP) == sizeof(int), "is bad?"); + +enum b { + BP = 0, + BR = (__force bits) 0, + BQ = 1, + BS = (__force bits) 1, +}; +_Static_assert(sizeof(BP) == sizeof(int), "is bad?"); + +/* + * check-name: enum-bitwise-mixed + * + * check-error-start +enum-bitwise-mixed.c:8:14: warning: mixed bitwiseness +enum-bitwise-mixed.c:16:15: warning: mixed bitwiseness + * check-error-end + */ diff --git a/validation/enum-bitwise.c b/validation/enum-bitwise.c new file mode 100644 index 00000000..fcdb8d7a --- /dev/null +++ b/validation/enum-bitwise.c @@ -0,0 +1,19 @@ +#define __bitwise __attribute__((bitwise)) +#define __force __attribute__((force)) + +typedef long long __bitwise bits; + +enum r { + RZ = (__force bits) 0, + RO = (__force bits) 1, + RM = (__force bits) -1, +}; + +_Static_assert([typeof(RZ)] == [bits], "RZ"); +_Static_assert([typeof(RO)] == [bits], "RO"); +_Static_assert([typeof(RM)] == [bits], "RM"); +_Static_assert(sizeof(enum r) == sizeof(bits), "bits"); + +/* + * check-name: enum-bitwise + */ diff --git a/validation/enum-bounds.c b/validation/enum-bounds.c new file mode 100644 index 00000000..64ecd8bb --- /dev/null +++ b/validation/enum-bounds.c @@ -0,0 +1,24 @@ +enum bound_int_max { + IMAX = __INT_MAX__, +}; +_Static_assert([typeof(IMAX)] == [int], ""); + +enum bound_int_maxp1 { + IMP1 = __INT_MAX__ + 1L, +}; +_Static_assert([typeof(IMP1)] == [unsigned int], ""); + +enum bound_int_maxm1 { + IMM1 = -__INT_MAX__ - 1L, +}; +_Static_assert([typeof(IMM1)] == [int], ""); + +enum bound_int_maxm2 { + IMM2 = -__INT_MAX__ - 2L, +}; +_Static_assert([typeof(IMM2)] == [long], ""); + +/* + * check-name: enum-bounds + * check-command: sparse -m64 $file + */ diff --git a/validation/enum-init-constness.c b/validation/enum-init-constness.c new file mode 100644 index 00000000..5b95bc06 --- /dev/null +++ b/validation/enum-init-constness.c @@ -0,0 +1,9 @@ +extern int invalid; + +enum e { + E = 1 ? 1 : invalid +}; + +/* + * check-name: enum-init-constness + */ diff --git a/validation/enum-invalid.c b/validation/enum-invalid.c new file mode 100644 index 00000000..62f8864d --- /dev/null +++ b/validation/enum-invalid.c @@ -0,0 +1,11 @@ +enum e { }; +enum f { F = 0.1 }; + +/* + * check-name: enum-invalid + * + * check-error-start +enum-invalid.c:1:10: error: empty enum definition +enum-invalid.c:2:14: error: bad constant expression + * check-error-end + */ diff --git a/validation/enum-min-size.c b/validation/enum-min-size.c new file mode 100644 index 00000000..e8bd9fb1 --- /dev/null +++ b/validation/enum-min-size.c @@ -0,0 +1,29 @@ +enum i { I = 1 }; +_Static_assert(sizeof(enum i) == sizeof(int), "int"); +enum u { U = 1U }; +_Static_assert(sizeof(enum u) == sizeof(int), "uint"); + +enum l { L = 1L }; +_Static_assert(sizeof(enum l) == sizeof(int), "long"); +enum m { M = 1UL }; +_Static_assert(sizeof(enum m) == sizeof(int), "ulong"); + +enum n { N = 1LL }; +_Static_assert(sizeof(enum n) == sizeof(int), "llong"); +enum o { O = 1ULL }; +_Static_assert(sizeof(enum o) == sizeof(int), "ullong"); + + +enum mi { MI = -1 }; +_Static_assert(sizeof(enum i) == sizeof(int), "int"); + +enum ml { ML = -1L }; +_Static_assert(sizeof(enum l) == sizeof(int), "long"); + +enum mn { MN = -1LL }; +_Static_assert(sizeof(enum n) == sizeof(int), "llong"); + + +/* + * check-name: enum-min-size + */ diff --git a/validation/enum-mismatch.c b/validation/enum-mismatch.c index 9a929d24..f698c016 100644 --- a/validation/enum-mismatch.c +++ b/validation/enum-mismatch.c @@ -13,7 +13,7 @@ static enum eb foo(enum ea a) * * check-error-start enum-mismatch.c:7:16: warning: mixing different enum types -enum-mismatch.c:7:16: int enum ea versus -enum-mismatch.c:7:16: int enum eb +enum-mismatch.c:7:16: unsigned int enum ea versus +enum-mismatch.c:7:16: unsigned int enum eb * check-error-end */ diff --git a/validation/enum-same-type.c b/validation/enum-same-type.c new file mode 100644 index 00000000..0ff49cb4 --- /dev/null +++ b/validation/enum-same-type.c @@ -0,0 +1,14 @@ +enum num { + NEG = -1, + NIL = 0, + ONE = 1U, + DUO = 2LL, +}; + +_Static_assert([typeof(NIL)] == [typeof(NEG)], "enum same type"); +_Static_assert([typeof(ONE)] == [typeof(NEG)], "enum same type"); +_Static_assert([typeof(DUO)] == [typeof(NEG)], "enum same type"); + +/* + * check-name: enum-same-type + */ diff --git a/validation/enum-sign-gcc.c b/validation/enum-sign-gcc.c new file mode 100644 index 00000000..5aa7983c --- /dev/null +++ b/validation/enum-sign-gcc.c @@ -0,0 +1,63 @@ +// For enum's underlying/compatible type: +// std C: unspecified +// GCC: 'unsigned int' if no negative values, +// otherwise 'int' (see GCC manul 4.9). +// But also accept ulong, long +// For the type of the enumerators: +// std C: 'int' +// GCC: 'int' if the value fit in a 'int' +// otherwise same as the enum underlying type? +// +// The following tests match GCC's choices + +#define is_unsigned(X) ((typeof(X))-1 > 0) + +enum u { + U = 1U, // fit in 'int' + // no negatives +}; +_Static_assert(sizeof(enum u) == sizeof(int), "size"); +_Static_assert(is_unsigned(enum u), "enum u"); +_Static_assert(is_unsigned(U) == 0, "value U"); // fail + +enum v { + V = __INT_MAX__ + 1U, // doesn't fit in 'int' + // no negatives +}; +_Static_assert(sizeof(enum v) == sizeof(int), "size"); +_Static_assert(is_unsigned(enum v), "enum v"); +_Static_assert(is_unsigned(V) == 1, "value V"); + +enum w { + W = __LONG_MAX__ + 1UL, // doesn't fit in 'long' +}; +_Static_assert(sizeof(enum w) == sizeof(long), "size"); +_Static_assert(is_unsigned(enum w), "enum w"); +_Static_assert(is_unsigned(W) == 1, "value W"); + +enum x { + A = 1, // fit in 'int' + B = 0x100000000UL, // doesn't fit in int +}; +_Static_assert(sizeof(enum x) == sizeof(long), "size"); +_Static_assert(is_unsigned(enum x), "enum x"); +_Static_assert(sizeof(A) == sizeof(int), "size A"); // fail +_Static_assert(is_unsigned(A) == 0, "enum A"); // fail +_Static_assert(sizeof(B) == sizeof(long), "size B"); +_Static_assert(is_unsigned(B) == 1, "enum B"); + +enum y { + C = 1, // fit in 'int' + D = 0x100000000L, // doesn't fit in int +}; +_Static_assert(sizeof(enum y) == sizeof(long), "size"); +_Static_assert(is_unsigned(enum y), "enum y"); +_Static_assert(sizeof(C) == sizeof(int), "size C"); // fail +_Static_assert(is_unsigned(C) == 0, "enum C"); // fail +_Static_assert(sizeof(D) == sizeof(long), "size D"); +_Static_assert(is_unsigned(D) == 1, "enum D"); + +/* + * check-name: enum-sign-gcc + * check-command: sparse -m64 $file + */ diff --git a/validation/enum-typecheck.c b/validation/enum-typecheck.c new file mode 100644 index 00000000..77b77b47 --- /dev/null +++ b/validation/enum-typecheck.c @@ -0,0 +1,39 @@ +enum good { G, }; +enum bad { B, }; +enum good g; + +enum good compat_int(void) { return 1; } + +void parg(enum good); +void parg(enum bad); + +void farg(enum good a); +void farg(enum bad a) { } + +enum good pret(void); +enum bad pret(void); + +enum good fret(void); +enum bad fret(void) { return 0; } + + +enum good *ptr; +enum bad *ptr; + +enum good *gptr = &g; +enum bad *bptr = &g; + +/* + * check-name: enum-typecheck + * check-command: sparse -Wno-decl $file + * check-known-to-fail + * + * check-error-start +enum-typecheck.c:8:6: error: symbol 'parg' redeclared with different type +enum-typecheck.c:11:6: error: symbol 'farg' redeclared with different type +enum-typecheck.c:14:11: error: symbol 'pret' redeclared with different type +enum-typecheck.c:17:11: error: symbol 'fret' redeclared with different type +enum-typecheck.c:21:12: error: symbol 'ptr' redeclared with different type +enum-typecheck.c:24:20: warning: incorrect type in initializer (different type sizes) + * check-error-end + */ |