This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
define and use STATIC_ASSERT_STMT for compile-time invariants
authorLukas Mai <l.mai@web.de>
Sat, 29 Nov 2014 11:17:05 +0000 (12:17 +0100)
committerFather Chrysostomos <sprout@cpan.org>
Sat, 29 Nov 2014 14:11:08 +0000 (06:11 -0800)
op.c
perl.h
pp.c
pp_hot.c
pp_sys.c
sv.c
toke.c

diff --git a/op.c b/op.c
index 30410b6..523fd5c 100644 (file)
--- a/op.c
+++ b/op.c
@@ -9627,7 +9627,7 @@ Perl_ck_rvconst(pTHX_ OP *o)
            SvREFCNT_dec(kid->op_sv);
 #ifdef USE_ITHREADS
            /* XXX hack: dependence on sizeof(PADOP) <= sizeof(SVOP) */
-           assert (sizeof(PADOP) <= sizeof(SVOP));
+           STATIC_ASSERT_STMT(sizeof(PADOP) <= sizeof(SVOP));
            kPADOP->op_padix = pad_alloc(OP_GV, SVf_READONLY);
            SvREFCNT_dec(PAD_SVl(kPADOP->op_padix));
            PAD_SETSV(kPADOP->op_padix, MUTABLE_SV(SvREFCNT_inc_simple_NN(gv)));
diff --git a/perl.h b/perl.h
index f36157c..8a930de 100644 (file)
--- a/perl.h
+++ b/perl.h
@@ -3467,7 +3467,10 @@ typedef pthread_key_t    perl_key;
 #  define __attribute__warn_unused_result__
 #endif
 
-#if defined(DEBUGGING) && defined(I_ASSERT)
+#ifdef I_ASSERT
+#  if !defined(DEBUGGING) && !defined(NDEBUG)
+#    define NDEBUG 1
+#  endif
 #  include <assert.h>
 #endif
 
@@ -3498,6 +3501,27 @@ typedef pthread_key_t    perl_key;
 /* placeholder */
 #endif
 
+#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)
+#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__)
+#endif
+/* We need this wrapper even in C11 because 'case X: static_assert(...);' is an
+   error (static_assert is a declaration, and only statements can have labels).
+*/
+#define STATIC_ASSERT_STMT(COND)      do { STATIC_ASSERT_GLOBAL(COND); } while (0)
 
 #ifndef __has_builtin
 #  define __has_builtin(x) 0 /* not a clang style compiler */
diff --git a/pp.c b/pp.c
index fdd2335..6d575f7 100644 (file)
--- a/pp.c
+++ b/pp.c
@@ -2956,7 +2956,7 @@ PP(pp_length)
     /* simplest case shortcut */
     /* turn off SVf_UTF8 in tmp flags if HINT_BYTES on*/
     U32 svflags = (SvFLAGS(sv) ^ (in_bytes << 26)) & (SVf_POK|SVs_GMG|SVf_UTF8);
-    assert(HINT_BYTES == 0x00000008 && SVf_UTF8 == 0x20000000 && (SVf_UTF8 == HINT_BYTES << 26));
+    STATIC_ASSERT_STMT(HINT_BYTES == 0x00000008 && SVf_UTF8 == 0x20000000 && (SVf_UTF8 == HINT_BYTES << 26));
     SETs(TARG);
 
     if(LIKELY(svflags == SVf_POK))
index 846c1af..48cc1cb 100644 (file)
--- a/pp_hot.c
+++ b/pp_hot.c
@@ -386,7 +386,7 @@ PP(pp_padrange)
                       (base << (OPpPADRANGE_COUNTSHIFT + SAVE_TIGHT_SHIFT))
                     | (count << SAVE_TIGHT_SHIFT)
                     | SAVEt_CLEARPADRANGE);
-        assert(OPpPADRANGE_COUNTMASK + 1 == (1 <<OPpPADRANGE_COUNTSHIFT));
+        STATIC_ASSERT_STMT(OPpPADRANGE_COUNTMASK + 1 == (1 << OPpPADRANGE_COUNTSHIFT));
         assert((payload >> (OPpPADRANGE_COUNTSHIFT+SAVE_TIGHT_SHIFT)) == base);
         {
             dSS_ADD;
index 0bc1aa1..8f6c753 100644 (file)
--- a/pp_sys.c
+++ b/pp_sys.c
@@ -534,9 +534,9 @@ Perl_tied_method(pTHX_ SV *methname, SV **sp, SV *const sv,
     PERL_ARGS_ASSERT_TIED_METHOD;
 
     /* Ensure that our flag bits do not overlap.  */
-    assert((TIED_METHOD_MORTALIZE_NOT_NEEDED & G_WANT) == 0);
-    assert((TIED_METHOD_ARGUMENTS_ON_STACK & G_WANT) == 0);
-    assert((TIED_METHOD_SAY & G_WANT) == 0);
+    STATIC_ASSERT_STMT((TIED_METHOD_MORTALIZE_NOT_NEEDED & G_WANT) == 0);
+    STATIC_ASSERT_STMT((TIED_METHOD_ARGUMENTS_ON_STACK & G_WANT) == 0);
+    STATIC_ASSERT_STMT((TIED_METHOD_SAY & G_WANT) == 0);
 
     PUTBACK; /* sp is at *foot* of args, so this pops args from old stack */
     PUSHSTACKi(PERLSI_MAGIC);
diff --git a/sv.c b/sv.c
index 40b614e..3cbcbf8 100644 (file)
--- a/sv.c
+++ b/sv.c
@@ -1320,8 +1320,8 @@ Perl_sv_upgrade(pTHX_ SV *const sv, svtype new_type)
        break;
     case SVt_PV:
        assert(new_type > SVt_PV);
-       assert(SVt_IV < SVt_PV);
-       assert(SVt_NV < SVt_PV);
+       STATIC_ASSERT_STMT(SVt_IV < SVt_PV);
+       STATIC_ASSERT_STMT(SVt_NV < SVt_PV);
        break;
     case SVt_PVIV:
        break;
@@ -4311,7 +4311,7 @@ Perl_sv_setsv_flags(pTHX_ SV *dstr, SV* sstr, const I32 flags)
                /* We're starting from SVt_NULL, so provided that's
                 * actual 0, we don't have to unset any SV type flags
                 * to promote to SVt_IV. */
-               assert(SVt_NULL == 0);
+               STATIC_ASSERT_STMT(SVt_NULL == 0);
                SET_SVANY_FOR_BODYLESS_IV(dstr);
                SvFLAGS(dstr) |= SVt_IV;
                break;
@@ -9366,7 +9366,7 @@ Perl_newSViv(pTHX_ const IV i)
     /* We're starting from SVt_FIRST, so provided that's
      * actual 0, we don't have to unset any SV type flags
      * to promote to SVt_IV. */
-    assert(SVt_FIRST == 0);
+    STATIC_ASSERT_STMT(SVt_FIRST == 0);
 
     SET_SVANY_FOR_BODYLESS_IV(sv);
     SvFLAGS(sv) |= SVt_IV;
diff --git a/toke.c b/toke.c
index 236acd5..2433f1f 100644 (file)
--- a/toke.c
+++ b/toke.c
@@ -730,7 +730,7 @@ Perl_lex_start(pTHX_ SV *line, PerlIO *rsfp, U32 flags)
     parser->bufend = parser->bufptr + SvCUR(parser->linestr);
     parser->last_lop = parser->last_uni = NULL;
 
-    assert(FITS_IN_8_BITS(LEX_IGNORE_UTF8_HINTS|LEX_EVALBYTES
+    STATIC_ASSERT_STMT(FITS_IN_8_BITS(LEX_IGNORE_UTF8_HINTS|LEX_EVALBYTES
                                                         |LEX_DONT_CLOSE_RSFP));
     parser->lex_flags = (U8) (flags & (LEX_IGNORE_UTF8_HINTS|LEX_EVALBYTES
                                                         |LEX_DONT_CLOSE_RSFP));