From 3df6c383c0b9c387ef5d9be8f6fe479ce714c51f Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Sun, 26 Jul 2020 22:16:47 +0200 Subject: [PATCH] Add support for static_assert Add C11's _Static_assert and static_assert macro. This is like #error, but is handled at a later stage of translation, so it is possible to check sizes of types, values of enums, etc. https://en.cppreference.com/w/c/language/_Static_assert https://port70.net/~nsz/c/c11/n1570.html#6.7.10 --- include/assert.h | 6 +++ src/cc65/compile.c | 7 +++ src/cc65/declare.c | 8 ++++ src/cc65/locals.c | 8 ++++ src/cc65/scanner.c | 1 + src/cc65/scanner.h | 1 + src/cc65/staticassert.c | 96 +++++++++++++++++++++++++++++++++++++++++ src/cc65/staticassert.h | 51 ++++++++++++++++++++++ test/err/staticassert.c | 26 +++++++++++ test/val/staticassert.c | 70 ++++++++++++++++++++++++++++++ 10 files changed, 274 insertions(+) create mode 100644 src/cc65/staticassert.c create mode 100644 src/cc65/staticassert.h create mode 100644 test/err/staticassert.c create mode 100644 test/val/staticassert.c diff --git a/include/assert.h b/include/assert.h index 504964dc2..3f8b46ac0 100644 --- a/include/assert.h +++ b/include/assert.h @@ -46,6 +46,12 @@ extern void __fastcall__ _afailed (const char*, unsigned); # define assert(expr) ((expr)? (void)0 : _afailed(__FILE__, __LINE__)) #endif +/* +** TODO: Guard with #if __STDC_VERSION__ >= 201112L or similar when there +** is a C11 mode. +*/ +#define static_assert _Static_assert + /* End of assert.h */ diff --git a/src/cc65/compile.c b/src/cc65/compile.c index f7e4bd35d..3d2f82c51 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -61,6 +61,7 @@ #include "pragma.h" #include "preproc.h" #include "standard.h" +#include "staticassert.h" #include "symtab.h" @@ -108,6 +109,12 @@ static void Parse (void) continue; } + /* Check for a _Static_assert */ + if (CurTok.Tok == TOK_STATIC_ASSERT) { + ParseStaticAssert (); + continue; + } + /* Read variable defs and functions */ ParseDeclSpec (&Spec, SC_EXTERN | SC_STATIC, T_INT); diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 9e77ea63e..ada3ddaae 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -58,6 +58,7 @@ #include "pragma.h" #include "scanner.h" #include "standard.h" +#include "staticassert.h" #include "symtab.h" #include "wrappedcall.h" #include "typeconv.h" @@ -935,6 +936,13 @@ static SymEntry* ParseStructDecl (const char* Name) /* Get the type of the entry */ DeclSpec Spec; + + /* Check for a _Static_assert */ + if (CurTok.Tok == TOK_STATIC_ASSERT) { + ParseStaticAssert (); + continue; + } + InitDeclSpec (&Spec); ParseTypeSpec (&Spec, -1, T_QUAL_NONE); diff --git a/src/cc65/locals.c b/src/cc65/locals.c index 0007879d8..3fa26021f 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -50,6 +50,7 @@ #include "locals.h" #include "stackptr.h" #include "standard.h" +#include "staticassert.h" #include "symtab.h" #include "typeconv.h" #include "input.h" @@ -511,6 +512,13 @@ void DeclareLocals (void) ** declarations. */ DeclSpec Spec; + + /* Check for a _Static_assert */ + if (CurTok.Tok == TOK_STATIC_ASSERT) { + ParseStaticAssert (); + continue; + } + ParseDeclSpec (&Spec, SC_AUTO, T_INT); if ((Spec.Flags & DS_DEF_STORAGE) != 0 && /* No storage spec */ (Spec.Flags & DS_DEF_TYPE) != 0 && /* No type given */ diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index 5040cdf08..70203d027 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -86,6 +86,7 @@ static const struct Keyword { unsigned char Std; /* Token supported in which standards? */ } Keywords [] = { { "_Pragma", TOK_PRAGMA, TT_C89 | TT_C99 | TT_CC65 }, /* !! */ + { "_Static_assert", TOK_STATIC_ASSERT, TT_CC65 }, /* C11 */ { "__AX__", TOK_AX, TT_C89 | TT_C99 | TT_CC65 }, { "__A__", TOK_A, TT_C89 | TT_C99 | TT_CC65 }, { "__EAX__", TOK_EAX, TT_C89 | TT_C99 | TT_CC65 }, diff --git a/src/cc65/scanner.h b/src/cc65/scanner.h index 1242d78fd..1c95b3d33 100644 --- a/src/cc65/scanner.h +++ b/src/cc65/scanner.h @@ -173,6 +173,7 @@ typedef enum token_t { TOK_WCSCONST, TOK_ATTRIBUTE, + TOK_STATIC_ASSERT, TOK_FAR, TOK_NEAR, TOK_A, diff --git a/src/cc65/staticassert.c b/src/cc65/staticassert.c new file mode 100644 index 000000000..d7e608b06 --- /dev/null +++ b/src/cc65/staticassert.c @@ -0,0 +1,96 @@ +/*****************************************************************************/ +/* */ +/* staticassert.h */ +/* */ +/* _Static_assert handling for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* Copyright 2020 Google LLC */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +/* cc65 */ +#include "error.h" +#include "expr.h" +#include "litpool.h" +#include "scanner.h" +#include "staticassert.h" + + + +/*****************************************************************************/ +/* _Static_assert handling functions */ +/*****************************************************************************/ + + + +void ParseStaticAssert () +{ + /* + ** static_assert-declaration ::= + ** _Static_assert ( constant-expression , string-literal ) ; + */ + ExprDesc Expr; + int failed; + + /* Skip the _Static_assert token itself */ + CHECK (CurTok.Tok == TOK_STATIC_ASSERT); + NextToken (); + + /* We expect an opening paren */ + if (!ConsumeLParen ()) { + return; + } + + /* Parse assertion condition */ + ConstAbsIntExpr (hie1, &Expr); + failed = !Expr.IVal; + + /* We expect a comma */ + if (!ConsumeComma ()) { + return; + } + + /* String literal */ + if (CurTok.Tok != TOK_SCONST) { + Error ("String literal expected for static_assert message"); + return; + } + + /* Issue an error including the message if the static_assert failed. */ + if (failed) { + Error ("static_assert failed '%s'", GetLiteralStr (CurTok.SVal)); + } + + /* Consume the string constant, now that we don't need it anymore. + ** This should never fail since we checked the token type above. + */ + if (!Consume (TOK_SCONST, "String literal expected")) { + return; + } + + /* Closing paren and semi-colon needed */ + ConsumeRParen (); + ConsumeSemi (); +} diff --git a/src/cc65/staticassert.h b/src/cc65/staticassert.h new file mode 100644 index 000000000..955255bfe --- /dev/null +++ b/src/cc65/staticassert.h @@ -0,0 +1,51 @@ +/*****************************************************************************/ +/* */ +/* staticassert.h */ +/* */ +/* _Static_assert handling for the cc65 C compiler */ +/* */ +/* */ +/* */ +/* Copyright 2020 Google LLC */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef STATICASSERT_H +#define STATICASSERT_H + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +void ParseStaticAssert (void); +/* Handle _Static_assert. These are a C11 feature. */ + + + +/* End of staticassert.h */ + +#endif diff --git a/test/err/staticassert.c b/test/err/staticassert.c new file mode 100644 index 000000000..df9bab6d8 --- /dev/null +++ b/test/err/staticassert.c @@ -0,0 +1,26 @@ +/* + Copyright 2020 Google LLC + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* +** Test of failing _Static_assert. +**/ + + +_Static_assert(0, "0 should be false."); diff --git a/test/val/staticassert.c b/test/val/staticassert.c new file mode 100644 index 000000000..a02ba27e8 --- /dev/null +++ b/test/val/staticassert.c @@ -0,0 +1,70 @@ +/* + Copyright 2020 Google LLC + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* +** Tests of passing _Static_asserts. +**/ + + + +#include + +_Static_assert (1, "1 should be true."); +_Static_assert (!0, "!0 should be true."); +_Static_assert (1 == 1, "1 == 1 should be true."); +_Static_assert (1 == 1L, "1 == 1L should be true."); +_Static_assert (1 != 0, "1 != 0 should be true."); +_Static_assert (sizeof (char) == 1, "sizeof (char) should be 1."); +_Static_assert (sizeof (int) == 2, "sizeof (int) should be 2."); + +/* Make sure we can also do structs. */ +struct sc { char a; }; +_Static_assert (sizeof (struct sc) == 1, "sizeof (struct sc) should be 1."); +struct si { int a; }; +_Static_assert (sizeof (struct si) == 2, "sizeof (struct si) should be 2."); + +/* Try enums. */ +enum { k = 1 }; +_Static_assert (k == 1, "k should be 1."); + +/* Just test the macro version once. */ +static_assert (1, "1 should be true."); + +/* _Static_assert can appear anywhere a declaration can. */ +void f (void) +{ + _Static_assert (1, "1 should still be true."); + if (1) { + _Static_assert (1, "1 should still be true."); + } +} + +/* _Static_assert can also appear in structs. */ +struct S { + int a; + _Static_assert (1, "1 should still be true."); + int b; +}; + + +int main (void) +{ + return 0; +}