linux-sparse.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Lance Richardson <lrichard@redhat.com>
To: linux-sparse@vger.kernel.org
Subject: [PATCH v5] sparse: add support for _Static_assert
Date: Thu,  4 May 2017 12:00:38 -0400	[thread overview]
Message-ID: <20170504160038.5328-1-lrichard@redhat.com> (raw)

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

diff --git a/ident-list.h b/ident-list.h
index b65b667..4732ecc 100644
--- a/ident-list.h
+++ b/ident-list.h
@@ -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);
diff --git a/parse.c b/parse.c
index b52c6ab..73fc5a3 100644
--- a/parse.c
+++ b/parse.c
@@ -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 = &register_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)
diff --git a/validation/static_assert.c b/validation/static_assert.c
new file mode 100644
index 0000000..c7a5ec5
--- /dev/null
+++ b/validation/static_assert.c
@@ -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
+ */
+
-- 
2.9.3


             reply	other threads:[~2017-05-04 16:00 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-05-04 16:00 Lance Richardson [this message]
2017-05-04 19:18 ` [PATCH v5] sparse: add support for _Static_assert Luc Van Oostenryck
2017-05-06 20:22 ` Christopher Li
2017-05-06 23:11   ` Luc Van Oostenryck
2017-05-07  0:24     ` Christopher Li
2017-05-07  3:51       ` Luc Van Oostenryck
2017-05-07 15:57         ` Lance Richardson
2017-05-07 17:34           ` Christopher Li

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20170504160038.5328-1-lrichard@redhat.com \
    --to=lrichard@redhat.com \
    --cc=linux-sparse@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).