develooper Front page | perl.perl5.porters | Postings from November 2014

[perl #123327] [PATCH] define and use STATIC_ASSERT for compile-time invariants

Thread Next
From:
Father Chrysostomos via RT
Date:
November 29, 2014 14:17
Subject:
[perl #123327] [PATCH] define and use STATIC_ASSERT for compile-time invariants
Message ID:
rt-4.0.18-18147-1417270625-681.123327-15-0@perl.org
On Sat Nov 29 05:40:38 2014, mauke- wrote:
> This is a bug report for perl from l.mai@web.de,
> generated with the help of perlbug 1.40 running under perl 5.20.1.
> 
> 
> -----------------------------------------------------------------
> [Please describe your issue here]
> 
> Hi,
> 
> while reading commit de06ff5a50f29 I spotted the statement
> 'assert(SVt_NULL == 0);', i.e. using a runtime check to verify a
> compile-time
> property. I thought it would be better if we could make compilation
> fail if the
> condition doesn't hold (instead of triggering a runtime error in
> DEBUGGING
> builds).
> 
> The attached patch does this by defining and using
> STATIC_ASSERT_GLOBAL (at
> global scope) and STATIC_ASSERT_STMT (at function scope).
> 
> Things to note:
> 
> 1) perl.h now always includes <assert.h>.
> Previously it would only include <assert.h> in DEBUGGING builds and
> define
> assert(x) away otherwise. I changed it to always include <assert.h>
> but define
> NDEBUG if DEBUGGING is not set, which also makes assert(x) a no-op.
> Reason: Modern <assert.h>s provide not just assert but also a
> static_assert
> macro that I want to use where available.
> 
> 2) perl.h defines STATIC_ASSERT_GLOBAL.
> This can be used to enforce compile-time invariants outside of a
> function. A
> fallback implementation is provided in case static_assert isn't
> defined.
> 
> 3) perl.h defines STATIC_ASSERT_STMT.
> Like STATIC_ASSERT_GLOBAL, but can be used inside functions. In theory
> you can
> also use STATIC_ASSERT_GLOBAL in a function, but it expands to a
> declaration,
> so in C90 builds it has to occur at the beginning of a block (before
> any
> statements). And even in C99+ you can't attach a label to a
> declaration, so
> something like 'case FOO: STATIC_ASSERT_GLOBAL(1);' is a syntax error.
> 
> 
> Aside:
> Some compile-time invariants can be checked with
> #if !COND
> #  error "bad"
> #endif
> 
> But this only works for things the preprocessor knows about. In
> particular, it
> can't be used with 'sizeof' or enum values (such as SVt_NULL, my
> motivating
> example).

Two questions, because I’m not a C expert:

+#if defined(static_assert) || (defined(__cplusplus) && __cplusplus >= 201103L)
+/* static_assert is a macro defined in <assert.h> in C11 or a compiler
+   builtin in C++11.
+*/
+#  define STATIC_ASSERT_GLOBAL(COND) static_assert(COND, #COND)

What does the # do in #COND?

+#else
+/* We use a bit-field instead of an array because gcc accepts
+   'typedef char x[n]' where n is not a compile-time constant.
+   We want to enforce constantness.
+*/
+#  define STATIC_ASSERT_2(COND, SUFFIX) \
+    typedef struct { \
+        unsigned int _static_assertion_failed_##SUFFIX : (COND) ? 1 : -1; \
+    } _static_assertion_failed_##SUFFIX PERL_UNUSED_DECL
+#  define STATIC_ASSERT_1(COND, SUFFIX) STATIC_ASSERT_2(COND, SUFFIX)
+#  define STATIC_ASSERT_GLOBAL(COND)    STATIC_ASSERT_1(COND, __LINE__)

Why the layer of indirection?  Why not just 

+#  define STATIC_ASSERT_GLOBAL(COND)    STATIC_ASSERT_2(COND, __LINE__)

?

Also, would it be a good idea to do ASSUME() in STATIC_ASSERT_STMT,
for the sake of non-debugging builds?

-- 

Father Chrysostomos


---
via perlbug:  queue: perl5 status: new
https://rt.perl.org/Ticket/Display.html?id=123327

Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About