aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorLuc Van Oostenryck <luc.vanoostenryck@gmail.com>2019-11-03 21:49:15 +0100
committerLuc Van Oostenryck <luc.vanoostenryck@gmail.com>2020-03-24 10:51:35 +0100
commit79f7ac984473d031dfb9cef00119c2d542d0d4a6 (patch)
tree5af6e6713ee40d83b572e96897b644e1938b4c5e
parentbff9b106e8fc02ab89da5fc7bf6d5e05b676da13 (diff)
downloadsparse-79f7ac984473d031dfb9cef00119c2d542d0d4a6.tar.gz
add support for GCC's __auto_type
Despite the similarity with typeof, the approach taken here is relatively different. A specific symbol type (SYM_TYPEOF) is not used, instead a new flag is added to decl_state, another one in the declared symbol and a new internal type is used: 'autotype_ctype'. It's this new internal type that will be resolved to the definitive type at evalution time. It seems to be working pretty well, maybe because it hasn't been tested well enough. Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
-rw-r--r--parse.c38
-rw-r--r--symbol.c19
-rw-r--r--symbol.h2
-rw-r--r--validation/autotype-ko.c45
-rw-r--r--validation/autotype.c55
5 files changed, 159 insertions, 0 deletions
diff --git a/parse.c b/parse.c
index 0e6f66a8..a29c67c8 100644
--- a/parse.c
+++ b/parse.c
@@ -60,6 +60,7 @@ static declarator_t
thread_specifier, const_qualifier, volatile_qualifier;
static declarator_t restrict_qualifier;
static declarator_t atomic_qualifier;
+static declarator_t autotype_specifier;
static struct token *parse_if_statement(struct token *token, struct statement *stmt);
static struct token *parse_return_statement(struct token *token, struct statement *stmt);
@@ -213,6 +214,13 @@ static struct symbol_op typeof_op = {
.set = Set_S|Set_T,
};
+static struct symbol_op autotype_op = {
+ .type = KW_SPECIFIER,
+ .declarator = autotype_specifier,
+ .test = Set_Any,
+ .set = Set_S|Set_T,
+};
+
static struct symbol_op attribute_op = {
.type = KW_ATTRIBUTE,
.declarator = attribute_specifier,
@@ -505,6 +513,7 @@ static struct init_keyword {
{ "typeof", NS_TYPEDEF, .op = &typeof_op },
{ "__typeof", NS_TYPEDEF, .op = &typeof_op },
{ "__typeof__", NS_TYPEDEF, .op = &typeof_op },
+ { "__auto_type",NS_TYPEDEF, .op = &autotype_op },
{ "__attribute", NS_TYPEDEF, .op = &attribute_op },
{ "__attribute__", NS_TYPEDEF, .op = &attribute_op },
@@ -1078,6 +1087,13 @@ static struct token *typeof_specifier(struct token *token, struct decl_state *ct
return expect(token, ')', "after typeof");
}
+static struct token *autotype_specifier(struct token *token, struct decl_state *ctx)
+{
+ ctx->ctype.base_type = &autotype_ctype;
+ ctx->autotype = 1;
+ return token;
+}
+
static struct token *ignore_attribute(struct token *token, struct symbol *attr, struct decl_state *ctx)
{
struct expression *expr = NULL;
@@ -2985,6 +3001,11 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
}
}
} else if (base_type && base_type->type == SYM_FN) {
+ if (base_type->ctype.base_type == &autotype_ctype) {
+ sparse_error(decl->pos, "'%s()' has __auto_type return type",
+ show_ident(decl->ident));
+ base_type->ctype.base_type = &int_ctype;
+ }
if (base_type->ctype.base_type == &incomplete_ctype) {
warning(decl->pos, "'%s()' has implicit return type",
show_ident(decl->ident));
@@ -3037,6 +3058,23 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
}
}
+ if (ctx.autotype) {
+ const char *msg = NULL;
+ if (decl->ctype.base_type != &autotype_ctype)
+ msg = "on non-identifier";
+ else if (match_op(token, ','))
+ msg = "on declaration list";
+ else if (!decl->initializer)
+ msg = "without initializer";
+ else if (decl->initializer->type == EXPR_SYMBOL &&
+ decl->initializer->symbol == decl)
+ msg = "on self-init var";
+ if (msg) {
+ sparse_error(decl->pos, "__auto_type %s", msg);
+ decl->ctype.base_type = &bad_ctype;
+ }
+ }
+
if (!match_op(token, ','))
break;
diff --git a/symbol.c b/symbol.c
index ab6e9841..c2e6f0b4 100644
--- a/symbol.c
+++ b/symbol.c
@@ -192,6 +192,10 @@ static struct symbol * examine_struct_union_type(struct symbol *sym, int advance
fn = advance ? lay_out_struct : lay_out_union;
FOR_EACH_PTR(sym->symbol_list, member) {
+ if (member->ctype.base_type == &autotype_ctype) {
+ sparse_error(member->pos, "member '%s' has __auto_type", show_ident(member->ident));
+ member->ctype.base_type = &incomplete_ctype;
+ }
fn(member, &info);
} END_FOR_EACH_PTR(member);
@@ -210,6 +214,19 @@ static struct symbol *examine_base_type(struct symbol *sym)
{
struct symbol *base_type;
+ if (sym->ctype.base_type == &autotype_ctype) {
+ struct symbol *type = evaluate_expression(sym->initializer);
+ if (!type)
+ type = &bad_ctype;
+ if (is_bitfield_type(type)) {
+ warning(sym->pos, "__auto_type on bitfield");
+ if (type->type == SYM_NODE)
+ type = type->ctype.base_type;
+ type = type->ctype.base_type;
+ }
+ sym->ctype.base_type = type;
+ }
+
/* Check the base type */
base_type = examine_symbol_type(sym->ctype.base_type);
if (!base_type || base_type->type == SYM_PTR)
@@ -734,6 +751,7 @@ struct symbol bool_ctype, void_ctype, type_ctype,
string_ctype, ptr_ctype, lazy_ptr_ctype,
incomplete_ctype, label_ctype, bad_ctype,
null_ctype;
+struct symbol autotype_ctype;
struct symbol int_ptr_ctype, uint_ptr_ctype;
struct symbol long_ptr_ctype, ulong_ptr_ctype;
struct symbol llong_ptr_ctype, ullong_ptr_ctype;
@@ -792,6 +810,7 @@ static const struct ctype_declare {
{ &void_ctype, T_BASETYPE },
{ &type_ctype, T_BASETYPE },
{ &incomplete_ctype, T_BASETYPE },
+ { &autotype_ctype, T_BASETYPE },
{ &bad_ctype, T_BASETYPE },
{ &char_ctype, T__INT(-2, char) },
diff --git a/symbol.h b/symbol.h
index 270ae098..c86dfb33 100644
--- a/symbol.h
+++ b/symbol.h
@@ -110,6 +110,7 @@ struct decl_state {
unsigned long f_modifiers; // function attributes
unsigned char prefer_abstract, is_inline, storage_class, is_tls;
unsigned char is_ext_visible;
+ unsigned char autotype;
};
struct pseudo;
@@ -281,6 +282,7 @@ extern struct symbol bool_ctype, void_ctype, type_ctype,
string_ctype, ptr_ctype, lazy_ptr_ctype,
incomplete_ctype, label_ctype, bad_ctype,
null_ctype;
+extern struct symbol autotype_ctype;
extern struct symbol int_ptr_ctype, uint_ptr_ctype;
extern struct symbol long_ptr_ctype, ulong_ptr_ctype;
extern struct symbol llong_ptr_ctype, ullong_ptr_ctype;
diff --git a/validation/autotype-ko.c b/validation/autotype-ko.c
new file mode 100644
index 00000000..5b6cd708
--- /dev/null
+++ b/validation/autotype-ko.c
@@ -0,0 +1,45 @@
+__auto_type u; // KO: no initializer
+__auto_type r[2] = { 0, 1 }; // KO: not a plain identifier
+__auto_type foo(void) { } // KO: not a plain identifier
+__auto_type v = 0, w = 1; // KO: in list
+struct { __auto_type x; } s; // KO: not valid for struct/union
+__auto_type self = self; // KO: self-declared
+__auto_type undc = this; // KO: undeclared
+
+int i = 1;
+double f = 1.0;
+__auto_type i = 2; // KO: redecl, same type
+__auto_type f = 2.0f; // KO: redecl, diff type
+
+
+static int foo(int a, const int *ptr)
+{
+ __auto_type i = a;
+ __auto_type c = *ptr;
+
+ c += 1;
+ return i;
+}
+
+/*
+ * check-name: autotype-ko
+ * check-command: sparse -Wno-decl $file
+ *
+ * check-error-start
+autotype-ko.c:1:13: error: __auto_type without initializer
+autotype-ko.c:2:13: error: __auto_type on non-identifier
+autotype-ko.c:3:13: error: 'foo()' has __auto_type return type
+autotype-ko.c:4:13: error: __auto_type on declaration list
+autotype-ko.c:6:13: error: __auto_type on self-init var
+autotype-ko.c:2:20: error: invalid initializer
+autotype-ko.c:5:22: error: member 'x' has __auto_type
+autotype-ko.c:7:20: error: undefined identifier 'this'
+autotype-ko.c:11:13: error: symbol 'i' has multiple initializers (originally initialized at autotype-ko.c:9)
+autotype-ko.c:12:13: error: symbol 'f' has multiple initializers (originally initialized at autotype-ko.c:10)
+autotype-ko.c:12:13: error: symbol 'f' redeclared with different type (different type sizes):
+autotype-ko.c:12:13: float [addressable] [toplevel] f
+autotype-ko.c:10:8: note: previously declared as:
+autotype-ko.c:10:8: double [addressable] [toplevel] f
+autotype-ko.c:20:9: error: assignment to const expression
+ * check-error-end
+ */
diff --git a/validation/autotype.c b/validation/autotype.c
new file mode 100644
index 00000000..98683c93
--- /dev/null
+++ b/validation/autotype.c
@@ -0,0 +1,55 @@
+#ifdef __CHECKER__
+#define is_type(X, T) _Static_assert([typeof(X)] == [T], "")
+#else
+#define is_type(X, T) _Static_assert(1, "")
+#endif
+
+struct s {
+ int x;
+ int bf:3;
+};
+
+extern char ch;
+extern const int ci;
+
+__auto_type i = 0; is_type(i, int);
+__auto_type m = 1UL; is_type(m, unsigned long);
+__auto_type l = (int)0L; is_type(l, int);
+__auto_type c = (char)'\n'; is_type(c, char);
+__auto_type p = &i; is_type(p, int *);
+__auto_type f = 0.0; is_type(f, double);
+__auto_type s = (struct s){0}; is_type(s, struct s);
+__auto_type pci = &ci; is_type(pci, const int *);
+
+// ~~: not valid for bitfield
+__auto_type b = (struct s){0}.bf; is_type(b, int);
+
+static __auto_type si = 0; is_type(si, int);
+const __auto_type ci = 0; is_type(ci, const int);
+__auto_type ch = (char) '\n'; is_type(ch, char);
+
+static int foo(int a)
+{
+ __auto_type i = a; is_type(i, int);
+ __auto_type c = ch; is_type(c, char);
+ __auto_type ct = ci; is_type(&ct, const int *);
+
+ return ct += i + c;
+}
+
+
+
+#define __as __attribute__((address_space(42)))
+extern int __as aa;
+
+__auto_type pa = &aa; is_type(pa, int __as *);
+
+/*
+ * check-name: autotype
+ * check-command: sparse -Wno-decl $file
+ *
+ * check-error-start
+autotype.c:25:13: warning: __auto_type on bitfield
+autotype.c:37:16: error: assignment to const expression
+ * check-error-end
+ */