summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorLuc Van Oostenryck <luc.vanoostenryck@gmail.com>2018-10-05 03:31:53 +0200
committerLuc Van Oostenryck <luc.vanoostenryck@gmail.com>2018-10-05 03:31:53 +0200
commit3a53a9e392904b67680b8d4cd47acaf7934bfd16 (patch)
treee9cabb53f05f8f7dd212ed2dcf8d600fbeb5954e
parent220d83b94922e67ec912931c94e6346e742c5b8f (diff)
parent4686a668048fad7023a26d29b5a139c11c9a2848 (diff)
downloadsparse-3a53a9e392904b67680b8d4cd47acaf7934bfd16.tar.gz
Merge branch 'fix-enum-type' into tip
-rw-r--r--parse.c156
-rw-r--r--symbol.h5
-rw-r--r--validation/bug-rshift-ub.c16
-rw-r--r--validation/builtin-overflow.c2
-rw-r--r--validation/enum-base-type.c29
-rw-r--r--validation/enum-bitwise-bad.c20
-rw-r--r--validation/enum-bitwise-mixed.c29
-rw-r--r--validation/enum-bitwise.c19
-rw-r--r--validation/enum-bounds.c24
-rw-r--r--validation/enum-init-constness.c9
-rw-r--r--validation/enum-invalid.c11
-rw-r--r--validation/enum-min-size.c29
-rw-r--r--validation/enum-mismatch.c4
-rw-r--r--validation/enum-same-type.c14
-rw-r--r--validation/enum-sign-gcc.c63
-rw-r--r--validation/enum-typecheck.c39
16 files changed, 405 insertions, 64 deletions
diff --git a/parse.c b/parse.c
index 00b8ebbd..d4886c41 100644
--- a/parse.c
+++ b/parse.c
@@ -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;
diff --git a/symbol.h b/symbol.h
index 81c185ee..5a3d7cef 100644
--- a/symbol.h
+++ b/symbol.h
@@ -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
+ */