@@ -16,6 +16,7 @@ IDENT_RESERVED(for);
IDENT_RESERVED(while);
IDENT_RESERVED(do);
IDENT_RESERVED(goto);
+IDENT_RESERVED(_Static_assert);
/* C typenames. They get marked as reserved when initialized */
IDENT(struct);
@@ -73,6 +73,7 @@ static struct token *parse_context_statement(struct token *token, struct stateme
static struct token *parse_range_statement(struct token *token, struct statement *stmt);
static struct token *parse_asm_statement(struct token *token, struct statement *stmt);
static struct token *toplevel_asm_declaration(struct token *token, struct symbol_list **list);
+static struct token *parse_static_assert(struct token *token, struct symbol_list **unused);
typedef struct token *attr_t(struct token *, struct symbol *,
struct decl_state *);
@@ -308,6 +309,10 @@ static struct symbol_op asm_op = {
.toplevel = toplevel_asm_declaration,
};
+static struct symbol_op static_assert_op = {
+ .toplevel = parse_static_assert,
+};
+
static struct symbol_op packed_op = {
.attribute = attribute_packed,
};
@@ -437,6 +442,9 @@ static struct init_keyword {
{ "__restrict", NS_TYPEDEF, .op = &restrict_op},
{ "__restrict__", NS_TYPEDEF, .op = &restrict_op},
+ /* Static assertion */
+ { "_Static_assert", NS_KEYWORD, .op = &static_assert_op },
+
/* Storage class */
{ "auto", NS_TYPEDEF, .op = &auto_op },
{ "register", NS_TYPEDEF, .op = ®ister_op },
@@ -660,6 +668,11 @@ static int SENTINEL_ATTR match_idents(struct token *token, ...)
return next && token->ident == next;
}
+static int match_static_assert(struct token *token)
+{
+ return token_type(token) == TOKEN_IDENT &&
+ token->ident == &_Static_assert_ident;
+}
struct statement *alloc_statement(struct position pos, int type)
{
@@ -1864,13 +1877,17 @@ static struct token *declaration_list(struct token *token, struct symbol_list **
static struct token *struct_declaration_list(struct token *token, struct symbol_list **list)
{
while (!match_op(token, '}')) {
- if (!match_op(token, ';'))
- token = declaration_list(token, list);
- if (!match_op(token, ';')) {
- sparse_error(token->pos, "expected ; at end of declaration");
- break;
+ if (match_static_assert(token))
+ token = parse_static_assert(token, NULL);
+ else {
+ if (!match_op(token, ';'))
+ token = declaration_list(token, list);
+ if (!match_op(token, ';')) {
+ sparse_error(token->pos, "expected ; at end of declaration");
+ break;
+ }
+ token = token->next;
}
- token = token->next;
}
return token;
}
@@ -2012,6 +2029,36 @@ static struct token *parse_asm_declarator(struct token *token, struct decl_state
return token;
}
+
+static struct token *parse_static_assert(struct token *token, struct symbol_list **unused)
+{
+ struct expression *expr1 = NULL, *expr2 = NULL;
+ int val;
+
+ token = expect(token->next, '(', "after _Static_assert");
+ token = constant_expression(token, &expr1);
+ token = expect(token, ',', "after first argument of _Static_assert");
+ token = parse_expression(token, &expr2);
+ token = expect(token, ')', "after second argument of _Static_assert");
+
+ token = expect(token, ';', "after _Static_assert()");
+
+ if (!expr1 || !expr2)
+ return token;
+
+ val = const_expression_value(expr1);
+
+ if (expr2->type != EXPR_STRING)
+ sparse_error(expr2->pos, "bad string literal");
+ else if (expr1 && (expr1->type == EXPR_VALUE)) {
+ if (!val)
+ sparse_error(expr1->pos, "static assertion failed: %s",
+ show_string(expr2->string));
+ }
+
+ return token;
+}
+
/* Make a statement out of an expression */
static struct statement *make_statement(struct expression *expr)
{
@@ -2389,11 +2436,14 @@ static struct token * statement_list(struct token *token, struct statement_list
}
stmt = alloc_statement(token->pos, STMT_DECLARATION);
token = external_declaration(token, &stmt->declaration);
+ add_statement(list, stmt);
+ } else if (match_static_assert(token)) {
+ token = parse_static_assert(token, NULL);
} else {
seen_statement = Wdeclarationafterstatement;
token = statement(token, &stmt);
+ add_statement(list, stmt);
}
- add_statement(list, stmt);
}
return token;
}
@@ -2726,7 +2776,7 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
unsigned long mod;
int is_typedef;
- /* Top-level inline asm? */
+ /* Top-level inline asm or static assertion? */
if (token_type(token) == TOKEN_IDENT) {
struct symbol *s = lookup_keyword(token->ident, NS_KEYWORD);
if (s && s->op->toplevel)
new file mode 100644
@@ -0,0 +1,64 @@
+_Static_assert(1, "global ok");
+
+struct foo {
+ _Static_assert(1, "struct ok");
+};
+
+void bar(void)
+{
+ _Static_assert(1, " func1 ok");
+ int i;
+ i = 0;
+ _Static_assert(1, " func2 ok");
+
+ if (1) {
+ _Static_assert(1, " func3 ok");
+ }
+}
+
+_Static_assert(0, "expected assertion failure");
+
+static int f;
+_Static_assert(f, "non-constant expression");
+
+static int *p;
+_Static_assert(p, "non-integer expression");
+
+_Static_assert(0.1, "float expression");
+
+_Static_assert(!0 == 1, "non-trivial expression");
+
+static char array[4];
+_Static_assert(sizeof(array) == 4, "sizeof expression");
+
+static const char non_literal_string[] = "non literal string";
+_Static_assert(0, non_literal_string);
+
+_Static_assert(1 / 0, "invalid expression: should not show up?");
+
+struct s {
+ char arr[16];
+ _Static_assert(1, "inside struct");
+};
+
+union u {
+ char c;
+ int i;
+ _Static_assert(1, "inside union");
+};
+
+_Static_assert(sizeof(struct s) == 16, "sizeof assertion");
+
+/*
+ * check-name: static assertion
+ *
+ * check-error-start
+static_assert.c:19:16: error: static assertion failed: "expected assertion failure"
+static_assert.c:22:16: error: bad constant expression
+static_assert.c:25:16: error: bad constant expression
+static_assert.c:27:16: error: bad constant expression
+static_assert.c:35:19: error: bad string literal
+static_assert.c:37:18: error: bad constant expression
+ * check-error-end
+ */
+
This patch introduces support for the C11 _Static_assert() construct. Per the N1539 draft standard, the syntax changes for this construct include: declaration: <declaration-specifiers> <init-declarator-list>[opt] ; <static_assert-declaration> struct-declaration: <specifier-qualifier-list> <struct-declarator-list>[opt] ; <static_assert-declaration> static_assert-declaration: _Static_assert ( <constant-expression> , <string-literal> ) ; Signed-off-by: Lance Richardson <lrichard@redhat.com> --- v5: Incorporated feedback from Christopher Li and Luc van Oostenryck: - Made _Static_assert a reserved identifier - Simplified check for _Static_assert keyword, consolidated into a common function. - Improved the "static assert within a function body" test case by adding a static assertion intermingled with code and adding a static assertion within a compound statement block. - Fixed use of initialized stmt variable. Tested by using sparse on entire kernel tree and a similarly-sized code tree which makes use of _Static_assert(). v4: Addressed feedback, simplified and restructured to better model description in draft standard. v3: - Removed bogus test case introduced in v2 (static assertion on sizeof a structure within the definition of the structure). v2: - Added additional test cases. - Added additional validation for parameters to _Static_assert(). - Reworked implementation to avoid impacting struct/union definition handling ( the v1 implementation, which treated _Static_assert() as an NS_TYPEDEF term, had the unfortunate side-effect of leaving an unnamed field with unknown size attached to structure definitions when a static assert was inside a structure definition). ident-list.h | 1 + parse.c | 66 ++++++++++++++++++++++++++++++++++++++++------ validation/static_assert.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 8 deletions(-) create mode 100644 validation/static_assert.c