[Libguestfs] [nbdkit PATCH 6/9] common/include: extract STATIC_ASSERT() macro

Laszlo Ersek lersek at redhat.com
Fri Mar 3 07:51:42 UTC 2023


We already have two use cases for static assertions (and soon we'll have
yet another). Namely:

- STATIC_ASSERT_UNSIGNED_INT() in "checked-overflow.h". Here, we use our
  own trick, based on a negative-sized array typedef that's named with
  NBDKIT_UNIQUE_NAME.

- static_assert() in "test-array-size.c". This uses the C11 macro called
  static_assert() from <assert.h>, which wraps the C11 _Static_assert().
  This is not really great: our baseline is C99, not C11 (per commit
  762f7c9e5166, "tests: Set minimum compiler to ISO C99.", 2021-04-08) --
  which is why the same assertions are repeated in the code as normal
  runtime assert() calls, in case static_assert() is not defined.

Factor out our own STATIC_ASSERT(), from STATIC_ASSERT_UNSIGNED_INT().

Put it to use in "test-array-size.c", replacing both the runtime assert()s
and the compile-time static_assert()s. Note that for the latter, in order
to remain consistent with STATIC_ASSERT_UNSIGNED_INT(), we no longer
provide the *complaint* that we want the compiler to emit upon assertion
failure, but an identifier that stands for the predicate that we *expect*.

When uncommenting the negative test case in "test-array-size.c", the
resultant wall of compiler diagnostics includes the following entry:

> test-array-size.c:83:39: error: size of array
> ‘_array_size_macro_is_applied_to_array13’ is negative

This patch will have to be ported to nbdkit. (IMO we can implement the
change in libnbd first -- the "common" subdir is meant to be common, so no
particular precedence should be assumed.)

Signed-off-by: Laszlo Ersek <lersek at redhat.com>
(cherry picked from libnbd commit c593baab3c9c8b17317daece0694ec8b5fc6fb46)
---
 common/include/Makefile.am        |  1 +
 common/include/checked-overflow.h |  8 ++--
 common/include/static-assert.h    | 48 ++++++++++++++++++++
 common/include/test-array-size.c  | 45 ++++++------------
 4 files changed, 67 insertions(+), 35 deletions(-)

diff --git a/common/include/Makefile.am b/common/include/Makefile.am
index a9933e819908..bbf00641cd5c 100644
--- a/common/include/Makefile.am
+++ b/common/include/Makefile.am
@@ -49,6 +49,7 @@ EXTRA_DIST = \
 	nextnonzero.h \
 	random.h \
 	rounding.h \
+	static-assert.h \
 	tvdiff.h \
 	unique-name.h \
 	unix-path-max.h \
diff --git a/common/include/checked-overflow.h b/common/include/checked-overflow.h
index a1852adcdc7a..4ec72387b332 100644
--- a/common/include/checked-overflow.h
+++ b/common/include/checked-overflow.h
@@ -53,6 +53,7 @@
 #include <stdbool.h>
 #include <stdint.h>
 
+#include "static-assert.h"
 #include "unique-name.h"
 
 /* Add "a" and "b", both of (possibly different) unsigned integer types, and
@@ -173,11 +174,8 @@
  *
  * The expression "x" is not evaluated, unless it has variably modified type.
  */
-#define STATIC_ASSERT_UNSIGNED_INT(x)                                               \
-  do {                                                                              \
-    typedef char NBDKIT_UNIQUE_NAME (_x_has_uint_type)[(typeof (x))-1 > 0 ? 1 : -1] \
-      __attribute__ ((__unused__));                                                 \
-  } while (0)
+#define STATIC_ASSERT_UNSIGNED_INT(x) \
+  STATIC_ASSERT ((typeof (x))-1 > 0, _x_has_uint_type)
 
 /* Assign the sum "a + b" to "*r", using uintmax_t modular arithmetic.
  *
diff --git a/common/include/static-assert.h b/common/include/static-assert.h
new file mode 100644
index 000000000000..5a564f8946e5
--- /dev/null
+++ b/common/include/static-assert.h
@@ -0,0 +1,48 @@
+/* nbdkit
+ * Copyright (C) 2013-2023 Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Red Hat nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef NBDKIT_STATIC_ASSERT_H
+#define NBDKIT_STATIC_ASSERT_H
+
+#include "unique-name.h"
+
+/* Assert "expression" at compile time. If "expression" evaluates to zero (at
+ * compile time), produce a compiler error message that includes
+ * "expectation_id".
+ */
+#define STATIC_ASSERT(expression, expectation_id)                           \
+  do {                                                                      \
+    typedef char NBDKIT_UNIQUE_NAME (expectation_id)[(expression) ? 1 : -1] \
+      __attribute__ ((__unused__));                                         \
+  } while (0)
+
+#endif /* NBDKIT_STATIC_ASSERT_H */
diff --git a/common/include/test-array-size.c b/common/include/test-array-size.c
index 8b0972aaabe1..6244ec1e7d9e 100644
--- a/common/include/test-array-size.c
+++ b/common/include/test-array-size.c
@@ -38,6 +38,7 @@
 #include <assert.h>
 
 #include "array-size.h"
+#include "static-assert.h"
 
 struct st { const char *s; int i; };
 
@@ -60,42 +61,26 @@ static struct st st4_0[4] __attribute__ ((__unused__));
 int
 main (void)
 {
-  assert (ARRAY_SIZE (s0) == 0);
-  assert (ARRAY_SIZE (s1) == 1);
-  assert (ARRAY_SIZE (s3) == 3);
-  assert (ARRAY_SIZE (s4) == 4);
-  assert (ARRAY_SIZE (i0) == 0);
-  assert (ARRAY_SIZE (i1) == 1);
-  assert (ARRAY_SIZE (i3) == 3);
-  assert (ARRAY_SIZE (i4) == 4);
-  assert (ARRAY_SIZE (st0) == 0);
-  assert (ARRAY_SIZE (st1) == 1);
-  assert (ARRAY_SIZE (st3) == 3);
-  assert (ARRAY_SIZE (st4) == 4);
-  assert (ARRAY_SIZE (st4_0) == 4);
-
-#ifdef static_assert
-  static_assert (ARRAY_SIZE (s0) == 0, "ARRAY_SIZE macro does not work");
-  static_assert (ARRAY_SIZE (s1) == 1, "ARRAY_SIZE macro does not work");
-  static_assert (ARRAY_SIZE (s3) == 3, "ARRAY_SIZE macro does not work");
-  static_assert (ARRAY_SIZE (s4) == 4, "ARRAY_SIZE macro does not work");
-  static_assert (ARRAY_SIZE (i0) == 0, "ARRAY_SIZE macro does not work");
-  static_assert (ARRAY_SIZE (i1) == 1, "ARRAY_SIZE macro does not work");
-  static_assert (ARRAY_SIZE (i3) == 3, "ARRAY_SIZE macro does not work");
-  static_assert (ARRAY_SIZE (i4) == 4, "ARRAY_SIZE macro does not work");
-  static_assert (ARRAY_SIZE (st0) == 0, "ARRAY_SIZE macro does not work");
-  static_assert (ARRAY_SIZE (st1) == 1, "ARRAY_SIZE macro does not work");
-  static_assert (ARRAY_SIZE (st3) == 3, "ARRAY_SIZE macro does not work");
-  static_assert (ARRAY_SIZE (st4) == 4, "ARRAY_SIZE macro does not work");
-  static_assert (ARRAY_SIZE (st4_0) == 4, "ARRAY_SIZE macro does not work");
-#endif
+  STATIC_ASSERT (ARRAY_SIZE (s0) == 0, _array_size_macro_works);
+  STATIC_ASSERT (ARRAY_SIZE (s1) == 1, _array_size_macro_works);
+  STATIC_ASSERT (ARRAY_SIZE (s3) == 3, _array_size_macro_works);
+  STATIC_ASSERT (ARRAY_SIZE (s4) == 4, _array_size_macro_works);
+  STATIC_ASSERT (ARRAY_SIZE (i0) == 0, _array_size_macro_works);
+  STATIC_ASSERT (ARRAY_SIZE (i1) == 1, _array_size_macro_works);
+  STATIC_ASSERT (ARRAY_SIZE (i3) == 3, _array_size_macro_works);
+  STATIC_ASSERT (ARRAY_SIZE (i4) == 4, _array_size_macro_works);
+  STATIC_ASSERT (ARRAY_SIZE (st0) == 0, _array_size_macro_works);
+  STATIC_ASSERT (ARRAY_SIZE (st1) == 1, _array_size_macro_works);
+  STATIC_ASSERT (ARRAY_SIZE (st3) == 3, _array_size_macro_works);
+  STATIC_ASSERT (ARRAY_SIZE (st4) == 4, _array_size_macro_works);
+  STATIC_ASSERT (ARRAY_SIZE (st4_0) == 4, _array_size_macro_works);
 
   /* You can uncomment this to test the negative case.  Unfortunately
    * it's difficult to automate this test.
    */
 #if 0
   int *p = i4;
-  assert (ARRAY_SIZE (p) == 4);
+  STATIC_ASSERT (ARRAY_SIZE (p) == 4, _array_size_macro_is_applied_to_array);
 #endif
 
   exit (EXIT_SUCCESS);



More information about the Libguestfs mailing list