]> granicus.if.org Git - postgresql/commitdiff
Provide some static-assertion functionality on all compilers.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 1 Oct 2012 02:46:29 +0000 (22:46 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 1 Oct 2012 02:46:29 +0000 (22:46 -0400)
On reflection (especially after noticing how many buildfarm critters have
__builtin_types_compatible_p but not _Static_assert), it seems like we
ought to try a bit harder to make these macros do something everywhere.
The initial cut at it would have been no help to code that is compiled only
on platforms without _Static_assert, for instance; and in any case not all
our contributors do their initial coding on the latest gcc version.

Some googling about static assertions turns up quite a bit of prior art
for making it work in compilers that lack _Static_assert.  The method
that seems closest to our needs involves defining a struct with a bit-field
that has negative width if the assertion condition fails.  There seems no
reliable way to get the error message string to be output, but throwing a
compile error with a confusing message is better than missing the problem
altogether.

In the same spirit, if we don't have __builtin_types_compatible_p we can at
least insist that the variable have the same width as the type.  This won't
catch errors such as "wrong pointer type", but it's far better than
nothing.

In addition to changing the macro definitions, adjust a
compile-time-constant Assert in contrib/hstore to use StaticAssertStmt,
so we can get some buildfarm coverage on whether that macro behaves sanely
or not.  There's surely more places that could be converted, but this is
the first one I came across.

contrib/hstore/hstore_compat.c
src/include/c.h

index 88764b1b698947e3a5c8903e3be53c652282c029..6327a8e8bb58a5c6e32e8884690ee5b5ece53417 100644 (file)
@@ -94,7 +94,7 @@
  * etc. are compatible.
  *
  * If the above statement isn't true on some bizarre platform, we're
- * a bit hosed (see Assert in hstoreValidOldFormat).
+ * a bit hosed (see StaticAssertStmt in hstoreValidOldFormat).
  */
 typedef struct
 {
@@ -180,7 +180,8 @@ hstoreValidOldFormat(HStore *hs)
                return 0;
 
        /* New format uses an HEntry for key and another for value */
-       Assert(sizeof(HOldEntry) == (2 * sizeof(HEntry)));
+       StaticAssertStmt(sizeof(HOldEntry) == 2 * sizeof(HEntry),
+                                        "old hstore format is not upward-compatible");
 
        if (count == 0)
                return 2;
index 06f689d35937b13178f263a6d5db6752e8b96a07..bec1eb3da46528f9d0167c4c954c5a34eefb284a 100644 (file)
@@ -690,14 +690,18 @@ typedef NameData *Name;
 
 
 /*
- * Macros to support compile-time assertion checks, if the compiler has them.
+ * Macros to support compile-time assertion checks.
  *
  * If the "condition" (a compile-time-constant expression) evaluates to false,
  * throw a compile error using the "errmessage" (a string literal).
  *
- * gcc 4.6 and up supports _Static_assert(), but it has bizarre syntactic
+ * gcc 4.6 and up supports _Static_assert(), but there are bizarre syntactic
  * placement restrictions.  These macros make it safe to use as a statement
  * or in an expression, respectively.
+ *
+ * Otherwise we fall back on a kluge that assumes the compiler will complain
+ * about a negative width for a struct bit-field.  This will not include a
+ * helpful error message, but it beats not getting an error at all.
  */
 #ifdef HAVE__STATIC_ASSERT
 #define StaticAssertStmt(condition, errmessage) \
@@ -705,8 +709,10 @@ typedef NameData *Name;
 #define StaticAssertExpr(condition, errmessage) \
        ({ StaticAssertStmt(condition, errmessage); true; })
 #else /* !HAVE__STATIC_ASSERT */
-#define StaticAssertStmt(condition, errmessage)
-#define StaticAssertExpr(condition, errmessage) ((void) true)
+#define StaticAssertStmt(condition, errmessage) \
+       ((void) sizeof(struct { int static_assert_failure : (condition) ? 1 : -1; }))
+#define StaticAssertExpr(condition, errmessage) \
+       StaticAssertStmt(condition, errmessage)
 #endif /* HAVE__STATIC_ASSERT */
 
 
@@ -716,6 +722,10 @@ typedef NameData *Name;
  * AssertVariableIsOfType() can be used as a statement.
  * AssertVariableIsOfTypeMacro() is intended for use in macros, eg
  *             #define foo(x) (AssertVariableIsOfTypeMacro(x, int), bar(x))
+ *
+ * If we don't have __builtin_types_compatible_p, we can still assert that
+ * the types have the same size.  This is far from ideal (especially on 32-bit
+ * platforms) but it provides at least some coverage.
  */
 #ifdef HAVE__BUILTIN_TYPES_COMPATIBLE_P
 #define AssertVariableIsOfType(varname, typename) \
@@ -725,8 +735,12 @@ typedef NameData *Name;
        StaticAssertExpr(__builtin_types_compatible_p(__typeof__(varname), typename), \
        CppAsString(varname) " does not have type " CppAsString(typename))
 #else /* !HAVE__BUILTIN_TYPES_COMPATIBLE_P */
-#define AssertVariableIsOfType(varname, typename)
-#define AssertVariableIsOfTypeMacro(varname, typename) ((void) true)
+#define AssertVariableIsOfType(varname, typename) \
+       StaticAssertStmt(sizeof(varname) == sizeof(typename), \
+       CppAsString(varname) " does not have type " CppAsString(typename))
+#define AssertVariableIsOfTypeMacro(varname, typename) \
+       StaticAssertExpr(sizeof(varname) == sizeof(typename), \
+       CppAsString(varname) " does not have type " CppAsString(typename))
 #endif /* HAVE__BUILTIN_TYPES_COMPATIBLE_P */