Hi,

I won't be surprised if Peter (CC'd) already has a patch for this in
his C11 incubation branch, but if not, maybe this will be useful.

While experimenting with threads and atomics on various systems, I
didn't like the way our macros failed in the ancient fallback code on
Visual Studio.  That only seems to be necessary for
StaticAssertExpr(), the rarest case.  That led to some observations:

* the meson script defines HAVE__STATIC_ASSERT if you have GCC
statement expressions, so why not just say so with
HAVE_STATEMENT_EXPRESSIONS, and keep ye olde fallback only for
StaticAssertExpr()?

* the configure script is different, tautological and wrong for
(evidently hypothetical) non-GCC-compatible C11 compiler given the
above interpretation of the macro

* to my knowledge we haven't written down which C++ standard our
headers conform to anywhere, but it surely can't be older than C++11
(I could elaborate), so I don't think we need the two __cplusplus
implementations

Here's an attempt to tidy that up.

We could also consider allowing ourselves to use standard
static_assert() directly in new code, in scopes that accept
declarations (eg file top-level, inside structs, inside functions as
allowed by our self-imposed -Wno-declaration-after-statement rule),
but that's essentially an independent question and I can also see that
the inconsistency might be annoying, cf size_t/Size, uint64_t/uint64,
etc debates with different outcomes.  For reference, we currently have
84 "...Decl", 10 "...Expr", and 18 "...Stmt" occurrences in the tree,
and if that has any predictive power, the vast majority of new code
would likely just use static_assert().

. o O { I wonder if it's possible to write a C23/C++17-compatible
single-argument static_assert() macro, or if you'd get stuck in a loop
and need to use a different name... the message argument is so often
boring/redundant... }
From 3fbd14e37d02a3fdbc8fe9e599af447973cdfd23 Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
Date: Fri, 14 Nov 2025 14:10:14 +1300
Subject: [PATCH] Refactor static_assert() support.

In meson, HAVE__STATIC_ASSERT was really a test for GCC
statement expressions, as needed for StaticAssertExpr() now that
_Static_assert could be assumed to be available through our C11
requirement.  This artificially prevented Visual Studio from being able
to use static_assert() in other contexts.

The configure script only tested for _Static_assert, which was
tautological (we require C11), but also incorrectly triggered the
assumption about statement expressions in c.h.

Instead, make a new test for HAVE_STATEMENT_EXPRESSIONS, and use that
to control only whether StaticAssertExpr() uses fallback code, not the
other variants.  This improves the quality of failure messages in the
(much more common) other variants under Visual Studio.

Also get rid of the two separate implementations for C++, since the C
implementation is also also valid as C++11.  While it is a stretch to
apply HAVE_STATEMENT_EXPRESSIONS tested with $CC to a C++ compiler, the
previous C++ coding assumed that the C++ compiler had them
unconditionally, so it isn't a new stretch.  In practice, the C and C++
compilers are very likely to agree, and if a combination is ever
reported that falsifies this assumption we can always reconsider that.
---
 config/c-compiler.m4          | 29 +++++++++++-------------
 configure                     | 24 ++++++++++----------
 configure.ac                  |  2 +-
 meson.build                   | 10 +++------
 src/include/c.h               | 42 +++++++++--------------------------
 src/include/pg_config.h.in    |  4 ++--
 src/include/regex/regcustom.h |  1 +
 7 files changed, 42 insertions(+), 70 deletions(-)

diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index 236a59e8536..95c76761c0b 100644
--- a/config/c-compiler.m4
+++ b/config/c-compiler.m4
@@ -114,23 +114,20 @@ fi])# PGAC_TYPE_128BIT_INT
 
 
 
-# PGAC_C_STATIC_ASSERT
-# --------------------
-# Check if the C compiler understands _Static_assert(),
-# and define HAVE__STATIC_ASSERT if so.
-#
-# We actually check the syntax ({ _Static_assert(...) }), because we need
-# gcc-style compound expressions to be able to wrap the thing into macros.
-AC_DEFUN([PGAC_C_STATIC_ASSERT],
-[AC_CACHE_CHECK(for _Static_assert, pgac_cv__static_assert,
-[AC_LINK_IFELSE([AC_LANG_PROGRAM([],
-[({ _Static_assert(1, "foo"); })])],
-[pgac_cv__static_assert=yes],
-[pgac_cv__static_assert=no])])
+# PGAC_C_STATEMENT_EXPRESSIONS
+# ----------------------------
+# Check if the C compiler understands GCC statement expressions.
+AC_DEFUN([PGAC_C_STATEMENT_EXPRESSIONS],
+[AC_CACHE_CHECK(for statement expressions, pgac_cv__statement_expressions,
+[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],
+[[({ _Static_assert(1, "foo"); });]]
+)],
+[pgac_cv__statement_expressions=yes],
+[pgac_cv__statement_expressions=no])])
 if test x"$pgac_cv__static_assert" = xyes ; then
-AC_DEFINE(HAVE__STATIC_ASSERT, 1,
-          [Define to 1 if your compiler understands _Static_assert.])
-fi])# PGAC_C_STATIC_ASSERT
+AC_DEFINE(HAVE__STATEMENT_EXPRESSIONS, 1,
+          [Define to 1 if your compiler understands statement expressions.])
+fi])# PGAC_C_STATEMENT_EXPRESSIONS
 
 
 
diff --git a/configure b/configure
index 3a0ed11fa8e..664f498e429 100755
--- a/configure
+++ b/configure
@@ -14714,9 +14714,9 @@ cat >>confdefs.h <<_ACEOF
 _ACEOF
 
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _Static_assert" >&5
-$as_echo_n "checking for _Static_assert... " >&6; }
-if ${pgac_cv__static_assert+:} false; then :
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for statement expressions" >&5
+$as_echo_n "checking for statement expressions... " >&6; }
+if ${pgac_cv__statement_expressions+:} false; then :
   $as_echo_n "(cached) " >&6
 else
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -14725,24 +14725,24 @@ else
 int
 main ()
 {
-({ _Static_assert(1, "foo"); })
+({ _Static_assert(1, "foo"); });
+
   ;
   return 0;
 }
 _ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
-  pgac_cv__static_assert=yes
+if ac_fn_c_try_compile "$LINENO"; then :
+  pgac_cv__statement_expressions=yes
 else
-  pgac_cv__static_assert=no
+  pgac_cv__statement_expressions=no
 fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv__static_assert" >&5
-$as_echo "$pgac_cv__static_assert" >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv__statement_expressions" >&5
+$as_echo "$pgac_cv__statement_expressions" >&6; }
 if test x"$pgac_cv__static_assert" = xyes ; then
 
-$as_echo "#define HAVE__STATIC_ASSERT 1" >>confdefs.h
+$as_echo "#define HAVE__STATEMENT_EXPRESSIONS 1" >>confdefs.h
 
 fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for typeof" >&5
diff --git a/configure.ac b/configure.ac
index c2413720a18..04d7377ebd2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1674,7 +1674,7 @@ m4_defun([AC_PROG_CC_STDC], []) dnl We don't want that.
 AC_C_BIGENDIAN
 AC_C_INLINE
 PGAC_PRINTF_ARCHETYPE
-PGAC_C_STATIC_ASSERT
+PGAC_C_STATEMENT_EXPRESSIONS
 PGAC_C_TYPEOF
 PGAC_C_TYPES_COMPATIBLE
 PGAC_C_BUILTIN_CONSTANT_P
diff --git a/meson.build b/meson.build
index c1e17aa3040..99f9e3aed37 100644
--- a/meson.build
+++ b/meson.build
@@ -1877,20 +1877,16 @@ if cc.compiles('''
 endif
 
 
-# Check if the C compiler understands _Static_assert(),
-# and define HAVE__STATIC_ASSERT if so.
-#
-# We actually check the syntax ({ _Static_assert(...) }), because we need
-# gcc-style compound expressions to be able to wrap the thing into macros.
+# Check if the C compiler understands GCC-style statement expressions.
 if cc.compiles('''
     int main(int arg, char **argv)
     {
         ({ _Static_assert(1, "foo"); });
     }
     ''',
-    name: '_Static_assert',
+    name: 'statement expressions',
     args: test_c_args)
-  cdata.set('HAVE__STATIC_ASSERT', 1)
+  cdata.set('HAVE_STATEMENT_EXPRESSIONS', 1)
 endif
 
 
diff --git a/src/include/c.h b/src/include/c.h
index 757dfff4782..16bfe7081ef 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -59,6 +59,7 @@
 #include "pg_config_os.h"		/* config from include/port/PORTNAME.h */
 
 /* System header files that should be available everywhere in Postgres */
+#include <assert.h>
 #include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -914,52 +915,29 @@ pg_noreturn extern void ExceptionalCondition(const char *conditionName,
  * If the "condition" (a compile-time-constant expression) evaluates to false,
  * throw a compile error using the "errmessage" (a string literal).
  *
- * C11 has _Static_assert(), and most C99 compilers already support that.  For
- * portability, we wrap it into StaticAssertDecl().  _Static_assert() is a
+ * We wrap static_assert() in StaticAssertDecl(), because it is a
  * "declaration", and so it must be placed where for example a variable
  * declaration would be valid.  As long as we compile with
  * -Wno-declaration-after-statement, that also means it cannot be placed after
  * statements in a function.  Macros StaticAssertStmt() and StaticAssertExpr()
  * make it safe to use as a statement or in an expression, respectively.
  *
- * For compilers without _Static_assert(), we fall back on a kluge that
- * assumes the compiler will complain about a negative width for a struct
+ * For compilers without GCC statement expressions, 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.
  */
-#ifndef __cplusplus
-#ifdef HAVE__STATIC_ASSERT
-#define StaticAssertDecl(condition, errmessage) \
-	_Static_assert(condition, errmessage)
-#define StaticAssertStmt(condition, errmessage) \
-	do { _Static_assert(condition, errmessage); } while(0)
-#define StaticAssertExpr(condition, errmessage) \
-	((void) ({ StaticAssertStmt(condition, errmessage); true; }))
-#else							/* !HAVE__STATIC_ASSERT */
-#define StaticAssertDecl(condition, errmessage) \
-	extern void static_assert_func(int static_assert_failure[(condition) ? 1 : -1])
-#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 */
-#else							/* C++ */
-#if defined(__cpp_static_assert) && __cpp_static_assert >= 200410
 #define StaticAssertDecl(condition, errmessage) \
 	static_assert(condition, errmessage)
 #define StaticAssertStmt(condition, errmessage) \
-	static_assert(condition, errmessage)
+	do { static_assert(condition, errmessage); } while(0)
+#ifdef HAVE_STATEMENT_EXPRESSIONS
 #define StaticAssertExpr(condition, errmessage) \
-	({ static_assert(condition, errmessage); })
-#else							/* !__cpp_static_assert */
-#define StaticAssertDecl(condition, errmessage) \
-	extern void static_assert_func(int static_assert_failure[(condition) ? 1 : -1])
-#define StaticAssertStmt(condition, errmessage) \
-	do { struct static_assert_struct { int static_assert_failure : (condition) ? 1 : -1; }; } while(0)
+	((void) ({ static_assert(condition, errmessage); true; }))
+#else
 #define StaticAssertExpr(condition, errmessage) \
-	((void) ({ StaticAssertStmt(condition, errmessage); }))
-#endif							/* __cpp_static_assert */
-#endif							/* C++ */
+	((void) sizeof(struct { int static_assert_failure : (condition) ? 1 : -1; }))
+#endif							/* HAVE_STATEMENT_EXPRESSIONS */
 
 
 /*
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index b0b0cfdaf79..a98fdbe479d 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -565,8 +565,8 @@
 /* Define to 1 if you have __get_cpuid_count. */
 #undef HAVE__GET_CPUID_COUNT
 
-/* Define to 1 if your compiler understands _Static_assert. */
-#undef HAVE__STATIC_ASSERT
+/* Define to 1 if your compiler understands statement expressions. */
+#undef HAVE__STATEMENT_EXPRESSIONS
 
 /* Define as the maximum alignment requirement of any C data type. */
 #undef MAXIMUM_ALIGNOF
diff --git a/src/include/regex/regcustom.h b/src/include/regex/regcustom.h
index 1c0e92f168f..4557e7a62c0 100644
--- a/src/include/regex/regcustom.h
+++ b/src/include/regex/regcustom.h
@@ -53,6 +53,7 @@
 #define FREE(p)			pfree(VS(p))
 #define REALLOC(p,n)	repalloc_extended(VS(p),(n), MCXT_ALLOC_NO_OOM)
 #define INTERRUPT(re)	CHECK_FOR_INTERRUPTS()
+#undef assert
 #define assert(x)		Assert(x)
 
 /* internal character type and related */
-- 
2.51.1

Reply via email to