Confused static assertion implementation

Started by Thomas Munroabout 2 months ago5 messages
#1Thomas Munro
thomas.munro@gmail.com
1 attachment(s)

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... }

Attachments:

0001-Refactor-static_assert-support.patchtext/x-patch; charset=US-ASCII; name=0001-Refactor-static_assert-support.patchDownload
From 3fbd14e37d02a3fdbc8fe9e599af447973cdfd23 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
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

#2Chao Li
li.evan.chao@gmail.com
In reply to: Thomas Munro (#1)
Re: Confused static assertion implementation

On Nov 14, 2025, at 10:13, Thomas Munro <thomas.munro@gmail.com> wrote:

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()?

+1, I think this is clearer.

. 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... }
<0001-Refactor-static_assert-support.patch>

A few small comments:

1
```
main ()
{
-({ _Static_assert(1, "foo"); })
+({ _Static_assert(1, "foo"); });
+
;
return 0;
}
```

As you added a semi-colon in the line, the one after the empty line can be deleted, though C allows empty statement, but unnecessary, and may lead to confusion for code readers.

2
```
-{ $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
```

You missed to replace this pgac_cv__static_assert with the new name.

Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/

#3Thomas Munro
thomas.munro@gmail.com
In reply to: Chao Li (#2)
1 attachment(s)
Re: Confused static assertion implementation

On Fri, Nov 14, 2025 at 6:18 PM Chao Li <li.evan.chao@gmail.com> wrote:

As you added a semi-colon in the line, the one after the empty line can be deleted, though C allows empty statement, but unnecessary, and may lead to confusion for code readers.

You missed to replace this pgac_cv__static_assert with the new name.

Ugh, yeah, the configure change was hopeless. It looked like it
worked in configure's stdout, which I mistook for success and posted
too soon, sorry about that. I have fixed those points and verified
that pg_config.h actually has the expected value.

Thanks for the review!

Attachments:

v2-0001-Refactor-static_assert-support.patchtext/x-patch; charset=US-ASCII; name=v2-0001-Refactor-static_assert-support.patchDownload
From de0dbdd898a1c938c39d5a327d9bf1494960baff Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Fri, 14 Nov 2025 14:10:14 +1300
Subject: [PATCH v2] 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.

Reviewed-by: Chao Li <li.evan.chao@gmail.com>
Discussion: https://postgr.es/m/CA%2BhUKGKvr0x_oGmQTUkx%3DODgSksT2EtgCA6LmGx_jQFG%3DsDUpg%40mail.gmail.com
---
 config/c-compiler.m4          | 26 +++++++++-------------
 configure                     | 18 +++++++--------
 configure.ac                  |  2 +-
 meson.build                   | 10 +++------
 src/include/c.h               | 42 +++++++++--------------------------
 src/include/pg_config.h.in    |  6 ++---
 src/include/regex/regcustom.h |  1 +
 7 files changed, 38 insertions(+), 67 deletions(-)

diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index 236a59e8536..f59bcdbfd01 100644
--- a/config/c-compiler.m4
+++ b/config/c-compiler.m4
@@ -114,23 +114,19 @@ 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,
+# 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_LINK_IFELSE([AC_LANG_PROGRAM([],
 [({ _Static_assert(1, "foo"); })])],
-[pgac_cv__static_assert=yes],
-[pgac_cv__static_assert=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
+[pgac_cv__statement_expressions=yes],
+[pgac_cv__statement_expressions=no])])
+if test x"$pgac_cv__statement_expressions" = xyes ; then
+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..73b2e20d002 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
@@ -14731,18 +14731,18 @@ main ()
 }
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
-  pgac_cv__static_assert=yes
+  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
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv__static_assert" >&5
-$as_echo "$pgac_cv__static_assert" >&6; }
-if test x"$pgac_cv__static_assert" = xyes ; then
+{ $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__statement_expressions" = 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..2c118228a84 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -385,6 +385,9 @@
 /* Define to 1 if you have the `SSL_CTX_set_num_tickets' function. */
 #undef HAVE_SSL_CTX_SET_NUM_TICKETS
 
+/* Define to 1 if your compiler understands statement expressions. */
+#undef HAVE_STATEMENT_EXPRESSIONS
+
 /* Define to 1 if you have the <stdint.h> header file. */
 #undef HAVE_STDINT_H
 
@@ -565,9 +568,6 @@
 /* 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 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

#4Peter Eisentraut
peter@eisentraut.org
In reply to: Thomas Munro (#1)
6 attachment(s)
Re: Confused static assertion implementation

On 14.11.25 03:13, Thomas Munro wrote:

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

There is a curious, now obsolete surprise here: MSVC without C11 mode
enabled does support _Static_assert, but it does not allow it at file
scope. Our overly restrictive HAVE__STATIC_ASSERT check fails on MSVC,
but if it passed our code would have failed to compile because of the
file-scope static assertions. So this one suboptimal check covered for
a lack of a check elsewhere.

This is now obsolete; with MSVC in C11 mode, _Static_assert appears to
behave correctly.

I have found that the C standard committee is actively researching
allowing static assertions in expressions:

https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3538.pdf
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3637.pdf
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3682.pdf
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3715.pdf

Those documents also contain some information about workarounds in use.

Btw., statement expressions are also in play:

https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3643.htm

But it looks like the most popular workaround is to put the
static_assert in a struct declaration.

On compiler explorer, I found that this works in MSVC but produces a
warning. But in my actual PostgreSQL patches it currently fails. But
anyway, since we're talking about it now, I'm attaching my patches here.
So only 0001 through 0004 work, the remaining two are WIP. Maybe
someone can stare at 0005 long enough to find what the problem is.

* 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

Per
</messages/by-id/01a69441-af54-4822-891b-ca28e05b215a@eisentraut.org&gt;
it's >= C++11.

. 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... }

That would be nice, but I haven't tried it yet.

Attachments:

0001-Rename-AssertVariableIsOfType-to-StaticAssertVariabl.patchtext/plain; charset=UTF-8; name=0001-Rename-AssertVariableIsOfType-to-StaticAssertVariabl.patchDownload
From 6b06890f74a0003abc33aabf130825ffe3eeb694 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Tue, 18 Nov 2025 08:43:13 +0100
Subject: [PATCH 1/6] Rename AssertVariableIsOfType to
 StaticAssertVariableIsOfType

This keeps run-time assertions and static assertions clearly separate.
---
 contrib/hstore_plperl/hstore_plperl.c     | 12 +++----
 contrib/hstore_plpython/hstore_plpython.c | 16 +++++-----
 contrib/jsonb_plpython/jsonb_plpython.c   |  8 ++---
 contrib/ltree_plpython/ltree_plpython.c   |  4 +--
 src/backend/executor/execParallel.c       |  2 +-
 src/backend/jit/llvm/llvmjit_types.c      | 10 +++---
 src/bin/pg_upgrade/check.c                | 26 ++++++++--------
 src/bin/pg_upgrade/function.c             |  2 +-
 src/bin/pg_upgrade/info.c                 |  6 ++--
 src/bin/pg_upgrade/version.c              |  2 +-
 src/include/access/xlogdefs.h             |  2 +-
 src/include/c.h                           | 14 ++++-----
 src/include/lib/ilist.h                   | 38 +++++++++++------------
 src/include/lib/pairingheap.h             |  8 ++---
 src/include/postgres.h                    |  4 +--
 src/include/storage/proclist.h            |  4 +--
 src/include/utils/freepage.h              |  2 +-
 src/include/utils/relptr.h                | 10 +++---
 18 files changed, 85 insertions(+), 85 deletions(-)

diff --git a/contrib/hstore_plperl/hstore_plperl.c b/contrib/hstore_plperl/hstore_plperl.c
index 31393b4fa50..1380a1b4367 100644
--- a/contrib/hstore_plperl/hstore_plperl.c
+++ b/contrib/hstore_plperl/hstore_plperl.c
@@ -28,24 +28,24 @@ static hstoreCheckValLen_t hstoreCheckValLen_p;
 void
 _PG_init(void)
 {
-	/* Asserts verify that typedefs above match original declarations */
-	AssertVariableIsOfType(&hstoreUpgrade, hstoreUpgrade_t);
+	/* Static asserts verify that typedefs above match original declarations */
+	StaticAssertVariableIsOfType(&hstoreUpgrade, hstoreUpgrade_t);
 	hstoreUpgrade_p = (hstoreUpgrade_t)
 		load_external_function("$libdir/hstore", "hstoreUpgrade",
 							   true, NULL);
-	AssertVariableIsOfType(&hstoreUniquePairs, hstoreUniquePairs_t);
+	StaticAssertVariableIsOfType(&hstoreUniquePairs, hstoreUniquePairs_t);
 	hstoreUniquePairs_p = (hstoreUniquePairs_t)
 		load_external_function("$libdir/hstore", "hstoreUniquePairs",
 							   true, NULL);
-	AssertVariableIsOfType(&hstorePairs, hstorePairs_t);
+	StaticAssertVariableIsOfType(&hstorePairs, hstorePairs_t);
 	hstorePairs_p = (hstorePairs_t)
 		load_external_function("$libdir/hstore", "hstorePairs",
 							   true, NULL);
-	AssertVariableIsOfType(&hstoreCheckKeyLen, hstoreCheckKeyLen_t);
+	StaticAssertVariableIsOfType(&hstoreCheckKeyLen, hstoreCheckKeyLen_t);
 	hstoreCheckKeyLen_p = (hstoreCheckKeyLen_t)
 		load_external_function("$libdir/hstore", "hstoreCheckKeyLen",
 							   true, NULL);
-	AssertVariableIsOfType(&hstoreCheckValLen, hstoreCheckValLen_t);
+	StaticAssertVariableIsOfType(&hstoreCheckValLen, hstoreCheckValLen_t);
 	hstoreCheckValLen_p = (hstoreCheckValLen_t)
 		load_external_function("$libdir/hstore", "hstoreCheckValLen",
 							   true, NULL);
diff --git a/contrib/hstore_plpython/hstore_plpython.c b/contrib/hstore_plpython/hstore_plpython.c
index e2bfc6da38e..3c8ada2a0dc 100644
--- a/contrib/hstore_plpython/hstore_plpython.c
+++ b/contrib/hstore_plpython/hstore_plpython.c
@@ -35,32 +35,32 @@ static hstoreCheckValLen_t hstoreCheckValLen_p;
 void
 _PG_init(void)
 {
-	/* Asserts verify that typedefs above match original declarations */
-	AssertVariableIsOfType(&PLyObject_AsString, PLyObject_AsString_t);
+	/* Static asserts verify that typedefs above match original declarations */
+	StaticAssertVariableIsOfType(&PLyObject_AsString, PLyObject_AsString_t);
 	PLyObject_AsString_p = (PLyObject_AsString_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyObject_AsString",
 							   true, NULL);
-	AssertVariableIsOfType(&PLyUnicode_FromStringAndSize, PLyUnicode_FromStringAndSize_t);
+	StaticAssertVariableIsOfType(&PLyUnicode_FromStringAndSize, PLyUnicode_FromStringAndSize_t);
 	PLyUnicode_FromStringAndSize_p = (PLyUnicode_FromStringAndSize_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyUnicode_FromStringAndSize",
 							   true, NULL);
-	AssertVariableIsOfType(&hstoreUpgrade, hstoreUpgrade_t);
+	StaticAssertVariableIsOfType(&hstoreUpgrade, hstoreUpgrade_t);
 	hstoreUpgrade_p = (hstoreUpgrade_t)
 		load_external_function("$libdir/hstore", "hstoreUpgrade",
 							   true, NULL);
-	AssertVariableIsOfType(&hstoreUniquePairs, hstoreUniquePairs_t);
+	StaticAssertVariableIsOfType(&hstoreUniquePairs, hstoreUniquePairs_t);
 	hstoreUniquePairs_p = (hstoreUniquePairs_t)
 		load_external_function("$libdir/hstore", "hstoreUniquePairs",
 							   true, NULL);
-	AssertVariableIsOfType(&hstorePairs, hstorePairs_t);
+	StaticAssertVariableIsOfType(&hstorePairs, hstorePairs_t);
 	hstorePairs_p = (hstorePairs_t)
 		load_external_function("$libdir/hstore", "hstorePairs",
 							   true, NULL);
-	AssertVariableIsOfType(&hstoreCheckKeyLen, hstoreCheckKeyLen_t);
+	StaticAssertVariableIsOfType(&hstoreCheckKeyLen, hstoreCheckKeyLen_t);
 	hstoreCheckKeyLen_p = (hstoreCheckKeyLen_t)
 		load_external_function("$libdir/hstore", "hstoreCheckKeyLen",
 							   true, NULL);
-	AssertVariableIsOfType(&hstoreCheckValLen, hstoreCheckValLen_t);
+	StaticAssertVariableIsOfType(&hstoreCheckValLen, hstoreCheckValLen_t);
 	hstoreCheckValLen_p = (hstoreCheckValLen_t)
 		load_external_function("$libdir/hstore", "hstoreCheckValLen",
 							   true, NULL);
diff --git a/contrib/jsonb_plpython/jsonb_plpython.c b/contrib/jsonb_plpython/jsonb_plpython.c
index 9383615abbf..5e6d62e5518 100644
--- a/contrib/jsonb_plpython/jsonb_plpython.c
+++ b/contrib/jsonb_plpython/jsonb_plpython.c
@@ -39,16 +39,16 @@ static PLyUnicode_FromStringAndSize_t PLyUnicode_FromStringAndSize_p;
 void
 _PG_init(void)
 {
-	/* Asserts verify that typedefs above match original declarations */
-	AssertVariableIsOfType(&PLyObject_AsString, PLyObject_AsString_t);
+	/* Static asserts verify that typedefs above match original declarations */
+	StaticAssertVariableIsOfType(&PLyObject_AsString, PLyObject_AsString_t);
 	PLyObject_AsString_p = (PLyObject_AsString_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyObject_AsString",
 							   true, NULL);
-	AssertVariableIsOfType(&PLyUnicode_FromStringAndSize, PLyUnicode_FromStringAndSize_t);
+	StaticAssertVariableIsOfType(&PLyUnicode_FromStringAndSize, PLyUnicode_FromStringAndSize_t);
 	PLyUnicode_FromStringAndSize_p = (PLyUnicode_FromStringAndSize_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyUnicode_FromStringAndSize",
 							   true, NULL);
-	AssertVariableIsOfType(&PLy_elog_impl, PLy_elog_impl_t);
+	StaticAssertVariableIsOfType(&PLy_elog_impl, PLy_elog_impl_t);
 	PLy_elog_impl_p = (PLy_elog_impl_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLy_elog_impl",
 							   true, NULL);
diff --git a/contrib/ltree_plpython/ltree_plpython.c b/contrib/ltree_plpython/ltree_plpython.c
index 0493aeb2423..a25fb5c5fa5 100644
--- a/contrib/ltree_plpython/ltree_plpython.c
+++ b/contrib/ltree_plpython/ltree_plpython.c
@@ -20,8 +20,8 @@ static PLyUnicode_FromStringAndSize_t PLyUnicode_FromStringAndSize_p;
 void
 _PG_init(void)
 {
-	/* Asserts verify that typedefs above match original declarations */
-	AssertVariableIsOfType(&PLyUnicode_FromStringAndSize, PLyUnicode_FromStringAndSize_t);
+	/* Static asserts verify that typedefs above match original declarations */
+	StaticAssertVariableIsOfType(&PLyUnicode_FromStringAndSize, PLyUnicode_FromStringAndSize_t);
 	PLyUnicode_FromStringAndSize_p = (PLyUnicode_FromStringAndSize_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyUnicode_FromStringAndSize",
 							   true, NULL);
diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c
index f098a5557cf..d4c622ca183 100644
--- a/src/backend/executor/execParallel.c
+++ b/src/backend/executor/execParallel.c
@@ -105,7 +105,7 @@ struct SharedExecutorInstrumentation
 	/* array of num_plan_nodes * num_workers Instrumentation objects follows */
 };
 #define GetInstrumentationArray(sei) \
-	(AssertVariableIsOfTypeMacro(sei, SharedExecutorInstrumentation *), \
+	(StaticAssertVariableIsOfTypeMacro(sei, SharedExecutorInstrumentation *), \
 	 (Instrumentation *) (((char *) sei) + sei->instrument_offset))
 
 /* Context object for ExecParallelEstimate. */
diff --git a/src/backend/jit/llvm/llvmjit_types.c b/src/backend/jit/llvm/llvmjit_types.c
index 167cd554b9c..053fb19f55f 100644
--- a/src/backend/jit/llvm/llvmjit_types.c
+++ b/src/backend/jit/llvm/llvmjit_types.c
@@ -81,7 +81,7 @@ extern Datum AttributeTemplate(PG_FUNCTION_ARGS);
 Datum
 AttributeTemplate(PG_FUNCTION_ARGS)
 {
-	AssertVariableIsOfType(&AttributeTemplate, PGFunction);
+	StaticAssertVariableIsOfType(&AttributeTemplate, PGFunction);
 
 	PG_RETURN_NULL();
 }
@@ -99,8 +99,8 @@ ExecEvalSubroutineTemplate(ExprState *state,
 						   struct ExprEvalStep *op,
 						   ExprContext *econtext)
 {
-	AssertVariableIsOfType(&ExecEvalSubroutineTemplate,
-						   ExecEvalSubroutine);
+	StaticAssertVariableIsOfType(&ExecEvalSubroutineTemplate,
+								 ExecEvalSubroutine);
 }
 
 extern bool ExecEvalBoolSubroutineTemplate(ExprState *state,
@@ -111,8 +111,8 @@ ExecEvalBoolSubroutineTemplate(ExprState *state,
 							   struct ExprEvalStep *op,
 							   ExprContext *econtext)
 {
-	AssertVariableIsOfType(&ExecEvalBoolSubroutineTemplate,
-						   ExecEvalBoolSubroutine);
+	StaticAssertVariableIsOfType(&ExecEvalBoolSubroutineTemplate,
+								 ExecEvalBoolSubroutine);
 
 	return false;
 }
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 1e17d64b3ec..7a37d54bebb 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -398,7 +398,7 @@ process_data_type_check(DbInfo *dbinfo, PGresult *res, void *arg)
 	int			i_attname = PQfnumber(res, "attname");
 	FILE	   *script = NULL;
 
-	AssertVariableIsOfType(&process_data_type_check, UpgradeTaskProcessCB);
+	StaticAssertVariableIsOfType(&process_data_type_check, UpgradeTaskProcessCB);
 
 	if (ntups == 0)
 		return;
@@ -1262,8 +1262,8 @@ process_isn_and_int8_passing_mismatch(DbInfo *dbinfo, PGresult *res, void *arg)
 	int			i_proname = PQfnumber(res, "proname");
 	UpgradeTaskReport *report = (UpgradeTaskReport *) arg;
 
-	AssertVariableIsOfType(&process_isn_and_int8_passing_mismatch,
-						   UpgradeTaskProcessCB);
+	StaticAssertVariableIsOfType(&process_isn_and_int8_passing_mismatch,
+								 UpgradeTaskProcessCB);
 
 	if (ntups == 0)
 		return;
@@ -1351,8 +1351,8 @@ process_user_defined_postfix_ops(DbInfo *dbinfo, PGresult *res, void *arg)
 	int			i_typnsp = PQfnumber(res, "typnsp");
 	int			i_typname = PQfnumber(res, "typname");
 
-	AssertVariableIsOfType(&process_user_defined_postfix_ops,
-						   UpgradeTaskProcessCB);
+	StaticAssertVariableIsOfType(&process_user_defined_postfix_ops,
+								 UpgradeTaskProcessCB);
 
 	if (ntups == 0)
 		return;
@@ -1442,8 +1442,8 @@ process_incompat_polymorphics(DbInfo *dbinfo, PGresult *res, void *arg)
 	int			i_objkind = PQfnumber(res, "objkind");
 	int			i_objname = PQfnumber(res, "objname");
 
-	AssertVariableIsOfType(&process_incompat_polymorphics,
-						   UpgradeTaskProcessCB);
+	StaticAssertVariableIsOfType(&process_incompat_polymorphics,
+								 UpgradeTaskProcessCB);
 
 	if (ntups == 0)
 		return;
@@ -1575,7 +1575,7 @@ process_with_oids_check(DbInfo *dbinfo, PGresult *res, void *arg)
 	int			i_nspname = PQfnumber(res, "nspname");
 	int			i_relname = PQfnumber(res, "relname");
 
-	AssertVariableIsOfType(&process_with_oids_check, UpgradeTaskProcessCB);
+	StaticAssertVariableIsOfType(&process_with_oids_check, UpgradeTaskProcessCB);
 
 	if (ntups == 0)
 		return;
@@ -1646,8 +1646,8 @@ process_inconsistent_notnull(DbInfo *dbinfo, PGresult *res, void *arg)
 	int			i_relname = PQfnumber(res, "relname");
 	int			i_attname = PQfnumber(res, "attname");
 
-	AssertVariableIsOfType(&process_inconsistent_notnull,
-						   UpgradeTaskProcessCB);
+	StaticAssertVariableIsOfType(&process_inconsistent_notnull,
+								 UpgradeTaskProcessCB);
 
 	if (ntups == 0)
 		return;
@@ -1793,8 +1793,8 @@ process_user_defined_encoding_conversions(DbInfo *dbinfo, PGresult *res, void *a
 	int			i_conname = PQfnumber(res, "conname");
 	int			i_nspname = PQfnumber(res, "nspname");
 
-	AssertVariableIsOfType(&process_user_defined_encoding_conversions,
-						   UpgradeTaskProcessCB);
+	StaticAssertVariableIsOfType(&process_user_defined_encoding_conversions,
+								 UpgradeTaskProcessCB);
 
 	if (ntups == 0)
 		return;
@@ -2309,7 +2309,7 @@ process_old_sub_state_check(DbInfo *dbinfo, PGresult *res, void *arg)
 	int			i_nspname = PQfnumber(res, "nspname");
 	int			i_relname = PQfnumber(res, "relname");
 
-	AssertVariableIsOfType(&process_old_sub_state_check, UpgradeTaskProcessCB);
+	StaticAssertVariableIsOfType(&process_old_sub_state_check, UpgradeTaskProcessCB);
 
 	if (ntups == 0)
 		return;
diff --git a/src/bin/pg_upgrade/function.c b/src/bin/pg_upgrade/function.c
index 9ad53caeebc..66982a142e1 100644
--- a/src/bin/pg_upgrade/function.c
+++ b/src/bin/pg_upgrade/function.c
@@ -61,7 +61,7 @@ process_loadable_libraries(DbInfo *dbinfo, PGresult *res, void *arg)
 {
 	struct loadable_libraries_state *state = (struct loadable_libraries_state *) arg;
 
-	AssertVariableIsOfType(&process_loadable_libraries, UpgradeTaskProcessCB);
+	StaticAssertVariableIsOfType(&process_loadable_libraries, UpgradeTaskProcessCB);
 
 	state->ress[dbinfo - old_cluster.dbarr.dbs] = res;
 	state->totaltups += PQntuples(res);
diff --git a/src/bin/pg_upgrade/info.c b/src/bin/pg_upgrade/info.c
index 7ce08270168..397682b11d5 100644
--- a/src/bin/pg_upgrade/info.c
+++ b/src/bin/pg_upgrade/info.c
@@ -601,7 +601,7 @@ process_rel_infos(DbInfo *dbinfo, PGresult *res, void *arg)
 	char	   *last_namespace = NULL;
 	char	   *last_tablespace = NULL;
 
-	AssertVariableIsOfType(&process_rel_infos, UpgradeTaskProcessCB);
+	StaticAssertVariableIsOfType(&process_rel_infos, UpgradeTaskProcessCB);
 
 	for (int relnum = 0; relnum < ntups; relnum++)
 	{
@@ -727,8 +727,8 @@ process_old_cluster_logical_slot_infos(DbInfo *dbinfo, PGresult *res, void *arg)
 	LogicalSlotInfo *slotinfos = NULL;
 	int			num_slots = PQntuples(res);
 
-	AssertVariableIsOfType(&process_old_cluster_logical_slot_infos,
-						   UpgradeTaskProcessCB);
+	StaticAssertVariableIsOfType(&process_old_cluster_logical_slot_infos,
+								 UpgradeTaskProcessCB);
 
 	if (num_slots)
 	{
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 3ad5a991a30..c499582660e 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -152,7 +152,7 @@ process_extension_updates(DbInfo *dbinfo, PGresult *res, void *arg)
 	UpgradeTaskReport *report = (UpgradeTaskReport *) arg;
 	PQExpBufferData connectbuf;
 
-	AssertVariableIsOfType(&process_extension_updates, UpgradeTaskProcessCB);
+	StaticAssertVariableIsOfType(&process_extension_updates, UpgradeTaskProcessCB);
 
 	if (ntups == 0)
 		return;
diff --git a/src/include/access/xlogdefs.h b/src/include/access/xlogdefs.h
index 5f07e57c832..4badeca2458 100644
--- a/src/include/access/xlogdefs.h
+++ b/src/include/access/xlogdefs.h
@@ -44,7 +44,7 @@ typedef uint64 XLogRecPtr;
  * To avoid breaking translatable messages, we're directly applying the
  * LSN format instead of using a macro.
  */
-#define LSN_FORMAT_ARGS(lsn) (AssertVariableIsOfTypeMacro((lsn), XLogRecPtr), (uint32) ((lsn) >> 32)), ((uint32) (lsn))
+#define LSN_FORMAT_ARGS(lsn) (StaticAssertVariableIsOfTypeMacro((lsn), XLogRecPtr), (uint32) ((lsn) >> 32)), ((uint32) (lsn))
 
 /*
  * XLogSegNo - physical log file sequence number.
diff --git a/src/include/c.h b/src/include/c.h
index cb8a38669be..bea342a5de6 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -965,26 +965,26 @@ pg_noreturn extern void ExceptionalCondition(const char *conditionName,
 /*
  * Compile-time checks that a variable (or expression) has the specified type.
  *
- * AssertVariableIsOfType() can be used as a statement.
- * AssertVariableIsOfTypeMacro() is intended for use in macros, eg
- *		#define foo(x) (AssertVariableIsOfTypeMacro(x, int), bar(x))
+ * StaticAssertVariableIsOfType() can be used as a statement.
+ * StaticAssertVariableIsOfTypeMacro() is intended for use in macros, eg
+ *		#define foo(x) (StaticAssertVariableIsOfTypeMacro(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) \
+#define StaticAssertVariableIsOfType(varname, typename) \
 	StaticAssertStmt(__builtin_types_compatible_p(__typeof__(varname), typename), \
 	CppAsString(varname) " does not have type " CppAsString(typename))
-#define AssertVariableIsOfTypeMacro(varname, typename) \
+#define StaticAssertVariableIsOfTypeMacro(varname, typename) \
 	(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 StaticAssertVariableIsOfType(varname, typename) \
 	StaticAssertStmt(sizeof(varname) == sizeof(typename), \
 	CppAsString(varname) " does not have type " CppAsString(typename))
-#define AssertVariableIsOfTypeMacro(varname, typename) \
+#define StaticAssertVariableIsOfTypeMacro(varname, typename) \
 	(StaticAssertExpr(sizeof(varname) == sizeof(typename), \
 	 CppAsString(varname) " does not have type " CppAsString(typename)))
 #endif							/* HAVE__BUILTIN_TYPES_COMPATIBLE_P */
diff --git a/src/include/lib/ilist.h b/src/include/lib/ilist.h
index 85a641eb41d..88bde08319b 100644
--- a/src/include/lib/ilist.h
+++ b/src/include/lib/ilist.h
@@ -591,8 +591,8 @@ dlist_tail_node(dlist_head *head)
  * This is used to convert a dlist_node * back to its containing struct.
  */
 #define dlist_container(type, membername, ptr)								\
-	(AssertVariableIsOfTypeMacro(ptr, dlist_node *),						\
-	 AssertVariableIsOfTypeMacro(((type *) NULL)->membername, dlist_node),	\
+	(StaticAssertVariableIsOfTypeMacro(ptr, dlist_node *),						\
+	 StaticAssertVariableIsOfTypeMacro(((type *) NULL)->membername, dlist_node),	\
 	 ((type *) ((char *) (ptr) - offsetof(type, membername))))
 
 /*
@@ -601,7 +601,7 @@ dlist_tail_node(dlist_head *head)
  * The list must not be empty.
  */
 #define dlist_head_element(type, membername, lhead)							\
-	(AssertVariableIsOfTypeMacro(((type *) NULL)->membername, dlist_node),	\
+	(StaticAssertVariableIsOfTypeMacro(((type *) NULL)->membername, dlist_node),	\
 	 (type *) dlist_head_element_off(lhead, offsetof(type, membername)))
 
 /*
@@ -610,7 +610,7 @@ dlist_tail_node(dlist_head *head)
  * The list must not be empty.
  */
 #define dlist_tail_element(type, membername, lhead)							\
-	(AssertVariableIsOfTypeMacro(((type *) NULL)->membername, dlist_node),	\
+	(StaticAssertVariableIsOfTypeMacro(((type *) NULL)->membername, dlist_node),	\
 	 ((type *) dlist_tail_element_off(lhead, offsetof(type, membername))))
 
 /*
@@ -621,8 +621,8 @@ dlist_tail_node(dlist_head *head)
  * It is *not* allowed to manipulate the list during iteration.
  */
 #define dlist_foreach(iter, lhead)											\
-	for (AssertVariableIsOfTypeMacro(iter, dlist_iter),						\
-		 AssertVariableIsOfTypeMacro(lhead, dlist_head *),					\
+	for (StaticAssertVariableIsOfTypeMacro(iter, dlist_iter),						\
+		 StaticAssertVariableIsOfTypeMacro(lhead, dlist_head *),					\
 		 (iter).end = &(lhead)->head,										\
 		 (iter).cur = (iter).end->next ? (iter).end->next : (iter).end;		\
 		 (iter).cur != (iter).end;											\
@@ -638,8 +638,8 @@ dlist_tail_node(dlist_head *head)
  * fine to insert or delete adjacent nodes.
  */
 #define dlist_foreach_modify(iter, lhead)									\
-	for (AssertVariableIsOfTypeMacro(iter, dlist_mutable_iter),				\
-		 AssertVariableIsOfTypeMacro(lhead, dlist_head *),					\
+	for (StaticAssertVariableIsOfTypeMacro(iter, dlist_mutable_iter),				\
+		 StaticAssertVariableIsOfTypeMacro(lhead, dlist_head *),					\
 		 (iter).end = &(lhead)->head,										\
 		 (iter).cur = (iter).end->next ? (iter).end->next : (iter).end,		\
 		 (iter).next = (iter).cur->next;									\
@@ -652,8 +652,8 @@ dlist_tail_node(dlist_head *head)
  * It is *not* allowed to manipulate the list during iteration.
  */
 #define dlist_reverse_foreach(iter, lhead)									\
-	for (AssertVariableIsOfTypeMacro(iter, dlist_iter),						\
-		 AssertVariableIsOfTypeMacro(lhead, dlist_head *),					\
+	for (StaticAssertVariableIsOfTypeMacro(iter, dlist_iter),						\
+		 StaticAssertVariableIsOfTypeMacro(lhead, dlist_head *),					\
 		 (iter).end = &(lhead)->head,										\
 		 (iter).cur = (iter).end->prev ? (iter).end->prev : (iter).end;		\
 		 (iter).cur != (iter).end;											\
@@ -953,7 +953,7 @@ dclist_count(const dclist_head *head)
   * The list must not be empty.
   */
 #define dclist_head_element(type, membername, lhead)							\
-	(AssertVariableIsOfTypeMacro(((type *) NULL)->membername, dlist_node),	\
+	(StaticAssertVariableIsOfTypeMacro(((type *) NULL)->membername, dlist_node),	\
 	 (type *) dclist_head_element_off(lhead, offsetof(type, membername)))
 
  /*
@@ -962,7 +962,7 @@ dclist_count(const dclist_head *head)
   * The list must not be empty.
   */
 #define dclist_tail_element(type, membername, lhead)							\
-	(AssertVariableIsOfTypeMacro(((type *) NULL)->membername, dlist_node),	\
+	(StaticAssertVariableIsOfTypeMacro(((type *) NULL)->membername, dlist_node),	\
 	 ((type *) dclist_tail_element_off(lhead, offsetof(type, membername))))
 
 
@@ -1104,8 +1104,8 @@ slist_delete_current(slist_mutable_iter *iter)
  * This is used to convert a slist_node * back to its containing struct.
  */
 #define slist_container(type, membername, ptr)								\
-	(AssertVariableIsOfTypeMacro(ptr, slist_node *),						\
-	 AssertVariableIsOfTypeMacro(((type *) NULL)->membername, slist_node),	\
+	(StaticAssertVariableIsOfTypeMacro(ptr, slist_node *),						\
+	 StaticAssertVariableIsOfTypeMacro(((type *) NULL)->membername, slist_node),	\
 	 ((type *) ((char *) (ptr) - offsetof(type, membername))))
 
 /*
@@ -1114,7 +1114,7 @@ slist_delete_current(slist_mutable_iter *iter)
  * The list must not be empty.
  */
 #define slist_head_element(type, membername, lhead)							\
-	(AssertVariableIsOfTypeMacro(((type *) NULL)->membername, slist_node),	\
+	(StaticAssertVariableIsOfTypeMacro(((type *) NULL)->membername, slist_node),	\
 	 (type *) slist_head_element_off(lhead, offsetof(type, membername)))
 
 /*
@@ -1130,8 +1130,8 @@ slist_delete_current(slist_mutable_iter *iter)
  * not safe.)
  */
 #define slist_foreach(iter, lhead)											\
-	for (AssertVariableIsOfTypeMacro(iter, slist_iter),						\
-		 AssertVariableIsOfTypeMacro(lhead, slist_head *),					\
+	for (StaticAssertVariableIsOfTypeMacro(iter, slist_iter),						\
+		 StaticAssertVariableIsOfTypeMacro(lhead, slist_head *),					\
 		 (iter).cur = (lhead)->head.next;									\
 		 (iter).cur != NULL;												\
 		 (iter).cur = (iter).cur->next)
@@ -1146,8 +1146,8 @@ slist_delete_current(slist_mutable_iter *iter)
  * deletion of nodes adjacent to the current node would misbehave.
  */
 #define slist_foreach_modify(iter, lhead)									\
-	for (AssertVariableIsOfTypeMacro(iter, slist_mutable_iter),				\
-		 AssertVariableIsOfTypeMacro(lhead, slist_head *),					\
+	for (StaticAssertVariableIsOfTypeMacro(iter, slist_mutable_iter),				\
+		 StaticAssertVariableIsOfTypeMacro(lhead, slist_head *),					\
 		 (iter).prev = &(lhead)->head,										\
 		 (iter).cur = (iter).prev->next,									\
 		 (iter).next = (iter).cur ? (iter).cur->next : NULL;				\
diff --git a/src/include/lib/pairingheap.h b/src/include/lib/pairingheap.h
index 567586f2ecf..b0f4c325ba2 100644
--- a/src/include/lib/pairingheap.h
+++ b/src/include/lib/pairingheap.h
@@ -41,16 +41,16 @@ typedef struct pairingheap_node
  * This is used to convert a pairingheap_node * back to its containing struct.
  */
 #define pairingheap_container(type, membername, ptr) \
-	(AssertVariableIsOfTypeMacro(ptr, pairingheap_node *), \
-	 AssertVariableIsOfTypeMacro(((type *) NULL)->membername, pairingheap_node),  \
+	(StaticAssertVariableIsOfTypeMacro(ptr, pairingheap_node *), \
+	 StaticAssertVariableIsOfTypeMacro(((type *) NULL)->membername, pairingheap_node),  \
 	 ((type *) ((char *) (ptr) - offsetof(type, membername))))
 
 /*
  * Like pairingheap_container, but used when the pointer is 'const ptr'
  */
 #define pairingheap_const_container(type, membername, ptr) \
-	(AssertVariableIsOfTypeMacro(ptr, const pairingheap_node *), \
-	 AssertVariableIsOfTypeMacro(((type *) NULL)->membername, pairingheap_node),  \
+	(StaticAssertVariableIsOfTypeMacro(ptr, const pairingheap_node *), \
+	 StaticAssertVariableIsOfTypeMacro(((type *) NULL)->membername, pairingheap_node),  \
 	 ((const type *) ((const char *) (ptr) - offsetof(type, membername))))
 
 /*
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 357cbd6fd96..5859844d9b8 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -513,9 +513,9 @@ Float8GetDatum(float8 X)
  */
 
 #define Int64GetDatumFast(X) \
-	(AssertVariableIsOfTypeMacro(X, int64), Int64GetDatum(X))
+	(StaticAssertVariableIsOfTypeMacro(X, int64), Int64GetDatum(X))
 #define Float8GetDatumFast(X) \
-	(AssertVariableIsOfTypeMacro(X, double), Float8GetDatum(X))
+	(StaticAssertVariableIsOfTypeMacro(X, double), Float8GetDatum(X))
 
 
 /* ----------------------------------------------------------------
diff --git a/src/include/storage/proclist.h b/src/include/storage/proclist.h
index a157f8df67f..c43d774f6ad 100644
--- a/src/include/storage/proclist.h
+++ b/src/include/storage/proclist.h
@@ -204,8 +204,8 @@ proclist_pop_head_node_offset(proclist_head *list, size_t node_offset)
  * node with proclist_delete(list, iter.cur, node_offset).
  */
 #define proclist_foreach_modify(iter, lhead, link_member)					\
-	for (AssertVariableIsOfTypeMacro(iter, proclist_mutable_iter),			\
-		 AssertVariableIsOfTypeMacro(lhead, proclist_head *),				\
+	for (StaticAssertVariableIsOfTypeMacro(iter, proclist_mutable_iter),			\
+		 StaticAssertVariableIsOfTypeMacro(lhead, proclist_head *),				\
 		 (iter).cur = (lhead)->head,										\
 		 (iter).next = (iter).cur == INVALID_PROC_NUMBER ? INVALID_PROC_NUMBER :	\
 			 proclist_node_get((iter).cur,									\
diff --git a/src/include/utils/freepage.h b/src/include/utils/freepage.h
index 18643a8c35d..70127bad430 100644
--- a/src/include/utils/freepage.h
+++ b/src/include/utils/freepage.h
@@ -65,7 +65,7 @@ struct FreePageManager
 
 /* Macros to convert between page numbers (expressed as Size) and pointers. */
 #define fpm_page_to_pointer(base, page) \
-	(AssertVariableIsOfTypeMacro(page, Size), \
+	(StaticAssertVariableIsOfTypeMacro(page, Size), \
 	 (base) + FPM_PAGE_SIZE * (page))
 #define fpm_pointer_to_page(base, ptr)		\
 	(((Size) (((char *) (ptr)) - (base))) / FPM_PAGE_SIZE)
diff --git a/src/include/utils/relptr.h b/src/include/utils/relptr.h
index ea340fee657..3e3bdb3227f 100644
--- a/src/include/utils/relptr.h
+++ b/src/include/utils/relptr.h
@@ -40,7 +40,7 @@
 
 #ifdef HAVE__BUILTIN_TYPES_COMPATIBLE_P
 #define relptr_access(base, rp) \
-	(AssertVariableIsOfTypeMacro(base, char *), \
+	(StaticAssertVariableIsOfTypeMacro(base, char *), \
 	 (__typeof__((rp).relptr_type)) ((rp).relptr_off == 0 ? NULL : \
 		(base) + (rp).relptr_off - 1))
 #else
@@ -49,7 +49,7 @@
  * __typeof__ either.
  */
 #define relptr_access(base, rp) \
-	(AssertVariableIsOfTypeMacro(base, char *), \
+	(StaticAssertVariableIsOfTypeMacro(base, char *), \
 	 (void *) ((rp).relptr_off == 0 ? NULL : (base) + (rp).relptr_off - 1))
 #endif
 
@@ -74,8 +74,8 @@ relptr_store_eval(char *base, char *val)
 
 #ifdef HAVE__BUILTIN_TYPES_COMPATIBLE_P
 #define relptr_store(base, rp, val) \
-	(AssertVariableIsOfTypeMacro(base, char *), \
-	 AssertVariableIsOfTypeMacro(val, __typeof__((rp).relptr_type)), \
+	(StaticAssertVariableIsOfTypeMacro(base, char *), \
+	 StaticAssertVariableIsOfTypeMacro(val, __typeof__((rp).relptr_type)), \
 	 (rp).relptr_off = relptr_store_eval((base), (char *) (val)))
 #else
 /*
@@ -83,7 +83,7 @@ relptr_store_eval(char *base, char *val)
  * __typeof__ either.
  */
 #define relptr_store(base, rp, val) \
-	(AssertVariableIsOfTypeMacro(base, char *), \
+	(StaticAssertVariableIsOfTypeMacro(base, char *), \
 	 (rp).relptr_off = relptr_store_eval((base), (char *) (val)))
 #endif
 
-- 
2.51.0

0002-Change-StaticAssertVariableIsOfType-to-be-a-declarat.patchtext/plain; charset=UTF-8; name=0002-Change-StaticAssertVariableIsOfType-to-be-a-declarat.patchDownload
From 01c4101fd27348eafae1458254b319f77b0e4ccf Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Tue, 18 Nov 2025 08:43:13 +0100
Subject: [PATCH 2/6] Change StaticAssertVariableIsOfType to be a declaration

This allows moving the uses to more natural and useful positions.
Also, a declaration is the more native use of static assertions in C.
---
 contrib/hstore_plperl/hstore_plperl.c     | 13 +++++++------
 contrib/hstore_plpython/hstore_plpython.c | 17 +++++++++--------
 contrib/jsonb_plpython/jsonb_plpython.c   | 10 ++++++----
 contrib/ltree_plpython/ltree_plpython.c   |  5 +++--
 src/include/c.h                           |  6 +++---
 5 files changed, 28 insertions(+), 23 deletions(-)

diff --git a/contrib/hstore_plperl/hstore_plperl.c b/contrib/hstore_plperl/hstore_plperl.c
index 1380a1b4367..69001191cc0 100644
--- a/contrib/hstore_plperl/hstore_plperl.c
+++ b/contrib/hstore_plperl/hstore_plperl.c
@@ -21,6 +21,13 @@ static hstoreCheckKeyLen_t hstoreCheckKeyLen_p;
 typedef size_t (*hstoreCheckValLen_t) (size_t len);
 static hstoreCheckValLen_t hstoreCheckValLen_p;
 
+/* Static asserts verify that typedefs above match original declarations */
+StaticAssertVariableIsOfType(&hstoreUpgrade, hstoreUpgrade_t);
+StaticAssertVariableIsOfType(&hstoreUniquePairs, hstoreUniquePairs_t);
+StaticAssertVariableIsOfType(&hstorePairs, hstorePairs_t);
+StaticAssertVariableIsOfType(&hstoreCheckKeyLen, hstoreCheckKeyLen_t);
+StaticAssertVariableIsOfType(&hstoreCheckValLen, hstoreCheckValLen_t);
+
 
 /*
  * Module initialize function: fetch function pointers for cross-module calls.
@@ -28,24 +35,18 @@ static hstoreCheckValLen_t hstoreCheckValLen_p;
 void
 _PG_init(void)
 {
-	/* Static asserts verify that typedefs above match original declarations */
-	StaticAssertVariableIsOfType(&hstoreUpgrade, hstoreUpgrade_t);
 	hstoreUpgrade_p = (hstoreUpgrade_t)
 		load_external_function("$libdir/hstore", "hstoreUpgrade",
 							   true, NULL);
-	StaticAssertVariableIsOfType(&hstoreUniquePairs, hstoreUniquePairs_t);
 	hstoreUniquePairs_p = (hstoreUniquePairs_t)
 		load_external_function("$libdir/hstore", "hstoreUniquePairs",
 							   true, NULL);
-	StaticAssertVariableIsOfType(&hstorePairs, hstorePairs_t);
 	hstorePairs_p = (hstorePairs_t)
 		load_external_function("$libdir/hstore", "hstorePairs",
 							   true, NULL);
-	StaticAssertVariableIsOfType(&hstoreCheckKeyLen, hstoreCheckKeyLen_t);
 	hstoreCheckKeyLen_p = (hstoreCheckKeyLen_t)
 		load_external_function("$libdir/hstore", "hstoreCheckKeyLen",
 							   true, NULL);
-	StaticAssertVariableIsOfType(&hstoreCheckValLen, hstoreCheckValLen_t);
 	hstoreCheckValLen_p = (hstoreCheckValLen_t)
 		load_external_function("$libdir/hstore", "hstoreCheckValLen",
 							   true, NULL);
diff --git a/contrib/hstore_plpython/hstore_plpython.c b/contrib/hstore_plpython/hstore_plpython.c
index 3c8ada2a0dc..d2be030e07c 100644
--- a/contrib/hstore_plpython/hstore_plpython.c
+++ b/contrib/hstore_plpython/hstore_plpython.c
@@ -28,6 +28,15 @@ static hstoreCheckKeyLen_t hstoreCheckKeyLen_p;
 typedef size_t (*hstoreCheckValLen_t) (size_t len);
 static hstoreCheckValLen_t hstoreCheckValLen_p;
 
+/* Static asserts verify that typedefs above match original declarations */
+StaticAssertVariableIsOfType(&PLyObject_AsString, PLyObject_AsString_t);
+StaticAssertVariableIsOfType(&PLyUnicode_FromStringAndSize, PLyUnicode_FromStringAndSize_t);
+StaticAssertVariableIsOfType(&hstoreUpgrade, hstoreUpgrade_t);
+StaticAssertVariableIsOfType(&hstoreUniquePairs, hstoreUniquePairs_t);
+StaticAssertVariableIsOfType(&hstorePairs, hstorePairs_t);
+StaticAssertVariableIsOfType(&hstoreCheckKeyLen, hstoreCheckKeyLen_t);
+StaticAssertVariableIsOfType(&hstoreCheckValLen, hstoreCheckValLen_t);
+
 
 /*
  * Module initialize function: fetch function pointers for cross-module calls.
@@ -35,32 +44,24 @@ static hstoreCheckValLen_t hstoreCheckValLen_p;
 void
 _PG_init(void)
 {
-	/* Static asserts verify that typedefs above match original declarations */
-	StaticAssertVariableIsOfType(&PLyObject_AsString, PLyObject_AsString_t);
 	PLyObject_AsString_p = (PLyObject_AsString_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyObject_AsString",
 							   true, NULL);
-	StaticAssertVariableIsOfType(&PLyUnicode_FromStringAndSize, PLyUnicode_FromStringAndSize_t);
 	PLyUnicode_FromStringAndSize_p = (PLyUnicode_FromStringAndSize_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyUnicode_FromStringAndSize",
 							   true, NULL);
-	StaticAssertVariableIsOfType(&hstoreUpgrade, hstoreUpgrade_t);
 	hstoreUpgrade_p = (hstoreUpgrade_t)
 		load_external_function("$libdir/hstore", "hstoreUpgrade",
 							   true, NULL);
-	StaticAssertVariableIsOfType(&hstoreUniquePairs, hstoreUniquePairs_t);
 	hstoreUniquePairs_p = (hstoreUniquePairs_t)
 		load_external_function("$libdir/hstore", "hstoreUniquePairs",
 							   true, NULL);
-	StaticAssertVariableIsOfType(&hstorePairs, hstorePairs_t);
 	hstorePairs_p = (hstorePairs_t)
 		load_external_function("$libdir/hstore", "hstorePairs",
 							   true, NULL);
-	StaticAssertVariableIsOfType(&hstoreCheckKeyLen, hstoreCheckKeyLen_t);
 	hstoreCheckKeyLen_p = (hstoreCheckKeyLen_t)
 		load_external_function("$libdir/hstore", "hstoreCheckKeyLen",
 							   true, NULL);
-	StaticAssertVariableIsOfType(&hstoreCheckValLen, hstoreCheckValLen_t);
 	hstoreCheckValLen_p = (hstoreCheckValLen_t)
 		load_external_function("$libdir/hstore", "hstoreCheckValLen",
 							   true, NULL);
diff --git a/contrib/jsonb_plpython/jsonb_plpython.c b/contrib/jsonb_plpython/jsonb_plpython.c
index 5e6d62e5518..944a25e2e13 100644
--- a/contrib/jsonb_plpython/jsonb_plpython.c
+++ b/contrib/jsonb_plpython/jsonb_plpython.c
@@ -33,22 +33,24 @@ typedef PyObject *(*PLyUnicode_FromStringAndSize_t)
 			(const char *s, Py_ssize_t size);
 static PLyUnicode_FromStringAndSize_t PLyUnicode_FromStringAndSize_p;
 
+/* Static asserts verify that typedefs above match original declarations */
+StaticAssertVariableIsOfType(&PLyObject_AsString, PLyObject_AsString_t);
+StaticAssertVariableIsOfType(&PLyUnicode_FromStringAndSize, PLyUnicode_FromStringAndSize_t);
+StaticAssertVariableIsOfType(&PLy_elog_impl, PLy_elog_impl_t);
+
+
 /*
  * Module initialize function: fetch function pointers for cross-module calls.
  */
 void
 _PG_init(void)
 {
-	/* Static asserts verify that typedefs above match original declarations */
-	StaticAssertVariableIsOfType(&PLyObject_AsString, PLyObject_AsString_t);
 	PLyObject_AsString_p = (PLyObject_AsString_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyObject_AsString",
 							   true, NULL);
-	StaticAssertVariableIsOfType(&PLyUnicode_FromStringAndSize, PLyUnicode_FromStringAndSize_t);
 	PLyUnicode_FromStringAndSize_p = (PLyUnicode_FromStringAndSize_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyUnicode_FromStringAndSize",
 							   true, NULL);
-	StaticAssertVariableIsOfType(&PLy_elog_impl, PLy_elog_impl_t);
 	PLy_elog_impl_p = (PLy_elog_impl_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLy_elog_impl",
 							   true, NULL);
diff --git a/contrib/ltree_plpython/ltree_plpython.c b/contrib/ltree_plpython/ltree_plpython.c
index a25fb5c5fa5..d4e7b613fa1 100644
--- a/contrib/ltree_plpython/ltree_plpython.c
+++ b/contrib/ltree_plpython/ltree_plpython.c
@@ -13,6 +13,9 @@ PG_MODULE_MAGIC_EXT(
 typedef PyObject *(*PLyUnicode_FromStringAndSize_t) (const char *s, Py_ssize_t size);
 static PLyUnicode_FromStringAndSize_t PLyUnicode_FromStringAndSize_p;
 
+/* Static asserts verify that typedefs above match original declarations */
+StaticAssertVariableIsOfType(&PLyUnicode_FromStringAndSize, PLyUnicode_FromStringAndSize_t);
+
 
 /*
  * Module initialize function: fetch function pointers for cross-module calls.
@@ -20,8 +23,6 @@ static PLyUnicode_FromStringAndSize_t PLyUnicode_FromStringAndSize_p;
 void
 _PG_init(void)
 {
-	/* Static asserts verify that typedefs above match original declarations */
-	StaticAssertVariableIsOfType(&PLyUnicode_FromStringAndSize, PLyUnicode_FromStringAndSize_t);
 	PLyUnicode_FromStringAndSize_p = (PLyUnicode_FromStringAndSize_t)
 		load_external_function("$libdir/" PLPYTHON_LIBNAME, "PLyUnicode_FromStringAndSize",
 							   true, NULL);
diff --git a/src/include/c.h b/src/include/c.h
index bea342a5de6..41edd31b835 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -965,7 +965,7 @@ pg_noreturn extern void ExceptionalCondition(const char *conditionName,
 /*
  * Compile-time checks that a variable (or expression) has the specified type.
  *
- * StaticAssertVariableIsOfType() can be used as a statement.
+ * StaticAssertVariableIsOfType() can be used as a declaration.
  * StaticAssertVariableIsOfTypeMacro() is intended for use in macros, eg
  *		#define foo(x) (StaticAssertVariableIsOfTypeMacro(x, int), bar(x))
  *
@@ -975,14 +975,14 @@ pg_noreturn extern void ExceptionalCondition(const char *conditionName,
  */
 #ifdef HAVE__BUILTIN_TYPES_COMPATIBLE_P
 #define StaticAssertVariableIsOfType(varname, typename) \
-	StaticAssertStmt(__builtin_types_compatible_p(__typeof__(varname), typename), \
+	StaticAssertDecl(__builtin_types_compatible_p(__typeof__(varname), typename), \
 	CppAsString(varname) " does not have type " CppAsString(typename))
 #define StaticAssertVariableIsOfTypeMacro(varname, typename) \
 	(StaticAssertExpr(__builtin_types_compatible_p(__typeof__(varname), typename), \
 	 CppAsString(varname) " does not have type " CppAsString(typename)))
 #else							/* !HAVE__BUILTIN_TYPES_COMPATIBLE_P */
 #define StaticAssertVariableIsOfType(varname, typename) \
-	StaticAssertStmt(sizeof(varname) == sizeof(typename), \
+	StaticAssertDecl(sizeof(varname) == sizeof(typename), \
 	CppAsString(varname) " does not have type " CppAsString(typename))
 #define StaticAssertVariableIsOfTypeMacro(varname, typename) \
 	(StaticAssertExpr(sizeof(varname) == sizeof(typename), \
-- 
2.51.0

0003-Remove-most-StaticAssertStmt.patchtext/plain; charset=UTF-8; name=0003-Remove-most-StaticAssertStmt.patchDownload
From 9731567ce2a0e40d3daa740923bf55df40415a6d Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Tue, 18 Nov 2025 08:43:13 +0100
Subject: [PATCH 3/6] Remove most StaticAssertStmt()

Similar to commit 75f49221c22, it's better to use StaticAssertDecl()
instead of StaticAssertStmt() when possible.  In fact, we can get rid
of all uses of StaticAssertStmt(), which will eliminate the need to
have two different ways to do the same thing, which has clearly been
confusing.
---
 contrib/hstore/hstore_compat.c         |  9 ++++-----
 src/backend/access/heap/vacuumlazy.c   |  5 +++--
 src/backend/access/table/tableam.c     |  6 +++---
 src/backend/access/transam/parallel.c  |  7 ++++---
 src/backend/backup/basebackup.c        | 10 ++++++----
 src/backend/storage/buffer/bufmgr.c    |  4 ++--
 src/backend/storage/file/fd.c          | 17 -----------------
 src/backend/storage/ipc/waiteventset.c |  3 ++-
 src/backend/storage/lmgr/deadlock.c    | 12 +++++++-----
 src/backend/utils/adt/mac.c            |  2 +-
 src/backend/utils/cache/inval.c        |  2 +-
 src/backend/utils/mmgr/aset.c          |  6 ++++--
 src/include/storage/fd.h               | 16 ++++++++++++++++
 13 files changed, 53 insertions(+), 46 deletions(-)

diff --git a/contrib/hstore/hstore_compat.c b/contrib/hstore/hstore_compat.c
index d75e9cb23f5..3a9f7f45cb7 100644
--- a/contrib/hstore/hstore_compat.c
+++ b/contrib/hstore/hstore_compat.c
@@ -94,7 +94,7 @@
  * etc. are compatible.
  *
  * If the above statement isn't true on some bizarre platform, we're
- * a bit hosed (see StaticAssertStmt in hstoreValidOldFormat).
+ * a bit hosed.
  */
 typedef struct
 {
@@ -105,6 +105,9 @@ typedef struct
 				pos:31;
 } HOldEntry;
 
+StaticAssertDecl(sizeof(HOldEntry) == 2 * sizeof(HEntry),
+				 "old hstore format is not upward-compatible");
+
 static int	hstoreValidNewFormat(HStore *hs);
 static int	hstoreValidOldFormat(HStore *hs);
 
@@ -179,10 +182,6 @@ hstoreValidOldFormat(HStore *hs)
 	if (hs->size_ & HS_FLAG_NEWVERSION)
 		return 0;
 
-	/* New format uses an HEntry for key and another for value */
-	StaticAssertStmt(sizeof(HOldEntry) == 2 * sizeof(HEntry),
-					 "old hstore format is not upward-compatible");
-
 	if (count == 0)
 		return 2;
 
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index deb9a3dc0d1..61976c9999b 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -3369,6 +3369,9 @@ lazy_truncate_heap(LVRelState *vacrel)
 static BlockNumber
 count_nondeletable_pages(LVRelState *vacrel, bool *lock_waiter_detected)
 {
+	StaticAssertDecl((PREFETCH_SIZE & (PREFETCH_SIZE - 1)) == 0,
+					 "prefetch size must be power of 2");
+
 	BlockNumber blkno;
 	BlockNumber prefetchedUntil;
 	instr_time	starttime;
@@ -3383,8 +3386,6 @@ count_nondeletable_pages(LVRelState *vacrel, bool *lock_waiter_detected)
 	 * in forward direction, so that OS-level readahead can kick in.
 	 */
 	blkno = vacrel->rel_pages;
-	StaticAssertStmt((PREFETCH_SIZE & (PREFETCH_SIZE - 1)) == 0,
-					 "prefetch size must be power of 2");
 	prefetchedUntil = InvalidBlockNumber;
 	while (blkno > vacrel->nonempty_pages)
 	{
diff --git a/src/backend/access/table/tableam.c b/src/backend/access/table/tableam.c
index 5e41404937e..0c90feb0e97 100644
--- a/src/backend/access/table/tableam.c
+++ b/src/backend/access/table/tableam.c
@@ -423,14 +423,14 @@ table_block_parallelscan_startblock_init(Relation rel,
 										 ParallelBlockTableScanWorker pbscanwork,
 										 ParallelBlockTableScanDesc pbscan)
 {
+	StaticAssertDecl(MaxBlockNumber <= 0xFFFFFFFE,
+					 "pg_nextpower2_32 may be too small for non-standard BlockNumber width");
+
 	BlockNumber sync_startpage = InvalidBlockNumber;
 
 	/* Reset the state we use for controlling allocation size. */
 	memset(pbscanwork, 0, sizeof(*pbscanwork));
 
-	StaticAssertStmt(MaxBlockNumber <= 0xFFFFFFFE,
-					 "pg_nextpower2_32 may be too small for non-standard BlockNumber width");
-
 	/*
 	 * We determine the chunk size based on the size of the relation. First we
 	 * split the relation into PARALLEL_SEQSCAN_NCHUNKS chunks but we then
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index 94db1ec3012..73bd7ad61a0 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -266,6 +266,10 @@ InitializeParallelDSM(ParallelContext *pcxt)
 
 	if (pcxt->nworkers > 0)
 	{
+		StaticAssertDecl(BUFFERALIGN(PARALLEL_ERROR_QUEUE_SIZE) ==
+						 PARALLEL_ERROR_QUEUE_SIZE,
+						 "parallel error queue size not buffer-aligned");
+
 		/* Estimate space for various kinds of state sharing. */
 		library_len = EstimateLibraryStateSpace();
 		shm_toc_estimate_chunk(&pcxt->estimator, library_len);
@@ -297,9 +301,6 @@ InitializeParallelDSM(ParallelContext *pcxt)
 		shm_toc_estimate_keys(&pcxt->estimator, 12);
 
 		/* Estimate space need for error queues. */
-		StaticAssertStmt(BUFFERALIGN(PARALLEL_ERROR_QUEUE_SIZE) ==
-						 PARALLEL_ERROR_QUEUE_SIZE,
-						 "parallel error queue size not buffer-aligned");
 		shm_toc_estimate_chunk(&pcxt->estimator,
 							   mul_size(PARALLEL_ERROR_QUEUE_SIZE,
 										pcxt->nworkers));
diff --git a/src/backend/backup/basebackup.c b/src/backend/backup/basebackup.c
index 2be4e069816..c608b1625ca 100644
--- a/src/backend/backup/basebackup.c
+++ b/src/backend/backup/basebackup.c
@@ -635,10 +635,12 @@ perform_base_backup(basebackup_options *opt, bbsink *sink,
 		}
 
 		/* Properly terminate the tar file. */
-		StaticAssertStmt(2 * TAR_BLOCK_SIZE <= BLCKSZ,
-						 "BLCKSZ too small for 2 tar blocks");
-		memset(sink->bbs_buffer, 0, 2 * TAR_BLOCK_SIZE);
-		bbsink_archive_contents(sink, 2 * TAR_BLOCK_SIZE);
+		{
+			StaticAssertDecl(2 * TAR_BLOCK_SIZE <= BLCKSZ,
+							 "BLCKSZ too small for 2 tar blocks");
+			memset(sink->bbs_buffer, 0, 2 * TAR_BLOCK_SIZE);
+			bbsink_archive_contents(sink, 2 * TAR_BLOCK_SIZE);
+		}
 
 		/* OK, that's the end of the archive. */
 		bbsink_end_archive(sink);
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 327ddb7adc8..09405304769 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -6961,9 +6961,9 @@ buffer_readv_encode_error(PgAioResult *result,
 		error_count > 0 ? error_count : zeroed_count;
 	uint8		first_off;
 
-	StaticAssertStmt(PG_IOV_MAX <= 1 << READV_COUNT_BITS,
+	StaticAssertDecl(PG_IOV_MAX <= 1 << READV_COUNT_BITS,
 					 "PG_IOV_MAX is bigger than reserved space for error data");
-	StaticAssertStmt((1 + 1 + 3 * READV_COUNT_BITS) <= PGAIO_RESULT_ERROR_BITS,
+	StaticAssertDecl((1 + 1 + 3 * READV_COUNT_BITS) <= PGAIO_RESULT_ERROR_BITS,
 					 "PGAIO_RESULT_ERROR_BITS is insufficient for buffer_readv");
 
 	/*
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
index e9eaaf9c829..59114632d49 100644
--- a/src/backend/storage/file/fd.c
+++ b/src/backend/storage/file/fd.c
@@ -1111,23 +1111,6 @@ BasicOpenFilePerm(const char *fileName, int fileFlags, mode_t fileMode)
 
 tryAgain:
 #ifdef PG_O_DIRECT_USE_F_NOCACHE
-
-	/*
-	 * The value we defined to stand in for O_DIRECT when simulating it with
-	 * F_NOCACHE had better not collide with any of the standard flags.
-	 */
-	StaticAssertStmt((PG_O_DIRECT &
-					  (O_APPEND |
-					   O_CLOEXEC |
-					   O_CREAT |
-					   O_DSYNC |
-					   O_EXCL |
-					   O_RDWR |
-					   O_RDONLY |
-					   O_SYNC |
-					   O_TRUNC |
-					   O_WRONLY)) == 0,
-					 "PG_O_DIRECT value collides with standard flag");
 	fd = open(fileName, fileFlags & ~PG_O_DIRECT, fileMode);
 #else
 	fd = open(fileName, fileFlags, fileMode);
diff --git a/src/backend/storage/ipc/waiteventset.c b/src/backend/storage/ipc/waiteventset.c
index 465cee40ccc..ebdb22ac3c1 100644
--- a/src/backend/storage/ipc/waiteventset.c
+++ b/src/backend/storage/ipc/waiteventset.c
@@ -462,7 +462,6 @@ CreateWaitEventSet(ResourceOwner resowner, int nevents)
 	 * pending signals are serviced.
 	 */
 	set->handles[0] = pgwin32_signal_event;
-	StaticAssertStmt(WSA_INVALID_EVENT == NULL, "");
 #endif
 
 	return set;
@@ -979,6 +978,8 @@ WaitEventAdjustKqueue(WaitEventSet *set, WaitEvent *event, int old_events)
 #endif
 
 #if defined(WAIT_USE_WIN32)
+StaticAssertDecl(WSA_INVALID_EVENT == NULL, "");
+
 static void
 WaitEventAdjustWin32(WaitEventSet *set, WaitEvent *event)
 {
diff --git a/src/backend/storage/lmgr/deadlock.c b/src/backend/storage/lmgr/deadlock.c
index c4bfaaa67ac..c129d966f7b 100644
--- a/src/backend/storage/lmgr/deadlock.c
+++ b/src/backend/storage/lmgr/deadlock.c
@@ -192,11 +192,13 @@ InitDeadLockChecking(void)
 	 * last MaxBackends entries in possibleConstraints[] are reserved as
 	 * output workspace for FindLockCycle.
 	 */
-	StaticAssertStmt(MAX_BACKENDS_BITS <= (32 - 3),
-					 "MAX_BACKENDS_BITS too big for * 4");
-	maxPossibleConstraints = MaxBackends * 4;
-	possibleConstraints =
-		(EDGE *) palloc(maxPossibleConstraints * sizeof(EDGE));
+	{
+		StaticAssertDecl(MAX_BACKENDS_BITS <= (32 - 3),
+						 "MAX_BACKENDS_BITS too big for * 4");
+		maxPossibleConstraints = MaxBackends * 4;
+		possibleConstraints =
+			(EDGE *) palloc(maxPossibleConstraints * sizeof(EDGE));
+	}
 
 	MemoryContextSwitchTo(oldcxt);
 }
diff --git a/src/backend/utils/adt/mac.c b/src/backend/utils/adt/mac.c
index bb38ef2f5e4..55c3448e88c 100644
--- a/src/backend/utils/adt/mac.c
+++ b/src/backend/utils/adt/mac.c
@@ -485,7 +485,7 @@ macaddr_abbrev_convert(Datum original, SortSupport ssup)
 	 * There will be two bytes of zero padding on the end of the least
 	 * significant bits.
 	 */
-	StaticAssertStmt(sizeof(res) >= sizeof(macaddr),
+	StaticAssertDecl(sizeof(res) >= sizeof(macaddr),
 					 "Datum is too small for macaddr");
 	memset(&res, 0, sizeof(res));
 	memcpy(&res, authoritative, sizeof(macaddr));
diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c
index 02505c88b8e..cd1686a4202 100644
--- a/src/backend/utils/cache/inval.c
+++ b/src/backend/utils/cache/inval.c
@@ -1753,7 +1753,7 @@ CacheInvalidateSmgr(RelFileLocatorBackend rlocator)
 	SharedInvalidationMessage msg;
 
 	/* verify optimization stated above stays valid */
-	StaticAssertStmt(MAX_BACKENDS_BITS <= 23,
+	StaticAssertDecl(MAX_BACKENDS_BITS <= 23,
 					 "MAX_BACKENDS_BITS is too big for inval.c");
 
 	msg.sm.id = SHAREDINVALSMGR_ID;
diff --git a/src/backend/utils/mmgr/aset.c b/src/backend/utils/mmgr/aset.c
index bcd09c07533..d84edf716fe 100644
--- a/src/backend/utils/mmgr/aset.c
+++ b/src/backend/utils/mmgr/aset.c
@@ -503,8 +503,10 @@ AllocSetContextCreateInternal(MemoryContext parent,
 	 *
 	 * Also, allocChunkLimit must not exceed ALLOCSET_SEPARATE_THRESHOLD.
 	 */
-	StaticAssertStmt(ALLOC_CHUNK_LIMIT == ALLOCSET_SEPARATE_THRESHOLD,
-					 "ALLOC_CHUNK_LIMIT != ALLOCSET_SEPARATE_THRESHOLD");
+	{
+		StaticAssertDecl(ALLOC_CHUNK_LIMIT == ALLOCSET_SEPARATE_THRESHOLD,
+						 "ALLOC_CHUNK_LIMIT != ALLOCSET_SEPARATE_THRESHOLD");
+	}
 
 	/*
 	 * Determine the maximum size that a chunk can be before we allocate an
diff --git a/src/include/storage/fd.h b/src/include/storage/fd.h
index 3e821ce8fb7..d131fe21e2d 100644
--- a/src/include/storage/fd.h
+++ b/src/include/storage/fd.h
@@ -93,6 +93,22 @@ extern PGDLLIMPORT int max_safe_fds;
 #elif defined(F_NOCACHE)
 #define		PG_O_DIRECT 0x80000000
 #define		PG_O_DIRECT_USE_F_NOCACHE
+/*
+ * The value we defined to stand in for O_DIRECT when simulating it with
+ * F_NOCACHE had better not collide with any of the standard flags.
+ */
+StaticAssertDecl((PG_O_DIRECT &
+				  (O_APPEND |
+				   O_CLOEXEC |
+				   O_CREAT |
+				   O_DSYNC |
+				   O_EXCL |
+				   O_RDWR |
+				   O_RDONLY |
+				   O_SYNC |
+				   O_TRUNC |
+				   O_WRONLY)) == 0,
+				 "PG_O_DIRECT value collides with standard flag");
 #else
 #define		PG_O_DIRECT 0
 #endif
-- 
2.51.0

0004-Remove-StaticAssertStmt.patchtext/plain; charset=UTF-8; name=0004-Remove-StaticAssertStmt.patchDownload
From 01b3fa89ef82af22f1274d90c0fed83cd4005388 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Tue, 18 Nov 2025 08:43:13 +0100
Subject: [PATCH 4/6] Remove StaticAssertStmt()

It's no longer used.  You can always use StaticAssertDecl() instead.
---
 src/include/c.h | 18 +++++-------------
 1 file changed, 5 insertions(+), 13 deletions(-)

diff --git a/src/include/c.h b/src/include/c.h
index 41edd31b835..f074c50fc0c 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -919,8 +919,8 @@ pg_noreturn extern void ExceptionalCondition(const char *conditionName,
  * "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.
+ * statements in a function.  The macro StaticAssertExpr() makes it safe to
+ * use in an expression.
  *
  * For compilers without _Static_assert(), we fall back on a kluge that
  * assumes the compiler will complain about a negative width for a struct
@@ -931,33 +931,25 @@ pg_noreturn extern void ExceptionalCondition(const char *conditionName,
 #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; }))
+	((void) ({ StaticAssertDecl(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)
+	((void) sizeof(struct { int static_assert_failure : (condition) ? 1 : -1; }))
 #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)
 #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)
 #define StaticAssertExpr(condition, errmessage) \
-	((void) ({ StaticAssertStmt(condition, errmessage); }))
+	((void) ({ do { struct static_assert_struct { int static_assert_failure : (condition) ? 1 : -1; }; } while(0); }))
 #endif							/* __cpp_static_assert */
 #endif							/* C++ */
 
-- 
2.51.0

0005-Portable-StaticAssertExpr.patchtext/plain; charset=UTF-8; name=0005-Portable-StaticAssertExpr.patchDownload
From 7af13e08a0f7151137a46bc38543d1134eea961d Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Tue, 18 Nov 2025 08:43:13 +0100
Subject: [PATCH 5/6] Portable StaticAssertExpr

Use a different way to write StaticAssertExpr() that does not require
the GCC extension statement expressions.
---
 config/c-compiler.m4       | 20 --------------------
 configure                  | 31 -------------------------------
 configure.ac               |  1 -
 meson.build                | 17 -----------------
 src/include/c.h            | 29 +++++------------------------
 src/include/pg_config.h.in |  3 ---
 6 files changed, 5 insertions(+), 96 deletions(-)

diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index 236a59e8536..897194875c1 100644
--- a/config/c-compiler.m4
+++ b/config/c-compiler.m4
@@ -114,26 +114,6 @@ 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])])
-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
-
-
-
 # PGAC_C_TYPEOF
 # -------------
 # Check if the C compiler understands typeof or a variant.  Define
diff --git a/configure b/configure
index 3a0ed11fa8e..7db1dfe92e6 100755
--- a/configure
+++ b/configure
@@ -14714,37 +14714,6 @@ 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_n "(cached) " >&6
-else
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-int
-main ()
-{
-({ _Static_assert(1, "foo"); })
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
-  pgac_cv__static_assert=yes
-else
-  pgac_cv__static_assert=no
-fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv__static_assert" >&5
-$as_echo "$pgac_cv__static_assert" >&6; }
-if test x"$pgac_cv__static_assert" = xyes ; then
-
-$as_echo "#define HAVE__STATIC_ASSERT 1" >>confdefs.h
-
-fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for typeof" >&5
 $as_echo_n "checking for typeof... " >&6; }
 if ${pgac_cv_c_typeof+:} false; then :
diff --git a/configure.ac b/configure.ac
index c2413720a18..c17796a7f2e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1674,7 +1674,6 @@ 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_TYPEOF
 PGAC_C_TYPES_COMPATIBLE
 PGAC_C_BUILTIN_CONSTANT_P
diff --git a/meson.build b/meson.build
index c1e17aa3040..00ba587ec04 100644
--- a/meson.build
+++ b/meson.build
@@ -1877,23 +1877,6 @@ 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.
-if cc.compiles('''
-    int main(int arg, char **argv)
-    {
-        ({ _Static_assert(1, "foo"); });
-    }
-    ''',
-    name: '_Static_assert',
-    args: test_c_args)
-  cdata.set('HAVE__STATIC_ASSERT', 1)
-endif
-
-
 # Need to check a call with %m because netbsd supports gnu_printf but emits a
 # warning for each use of %m.
 printf_attributes = ['gnu_printf', '__syslog__', 'printf']
diff --git a/src/include/c.h b/src/include/c.h
index f074c50fc0c..d462ebbebd1 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -914,43 +914,24 @@ 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
- * "declaration", and so it must be placed where for example a variable
- * declaration would be valid.  As long as we compile with
+ * We require C11 and C++11, so _Static_assert()/static_assert() are expected
+ * to be there.  For portability, we wrap it into StaticAssertDecl().
+ * _Static_assert() 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.  The macro StaticAssertExpr() makes it safe to
  * use in an expression.
- *
- * For compilers without _Static_assert(), 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 StaticAssertExpr(condition, errmessage) \
-	((void) ({ StaticAssertDecl(condition, errmessage); true; }))
-#else							/* !HAVE__STATIC_ASSERT */
-#define StaticAssertDecl(condition, errmessage) \
-	extern void static_assert_func(int static_assert_failure[(condition) ? 1 : -1])
-#define StaticAssertExpr(condition, errmessage) \
-	((void) sizeof(struct { int static_assert_failure : (condition) ? 1 : -1; }))
-#endif							/* HAVE__STATIC_ASSERT */
+	((void) sizeof(struct {_Static_assert(condition, errmessage); char a;}))
 #else							/* C++ */
-#if defined(__cpp_static_assert) && __cpp_static_assert >= 200410
 #define StaticAssertDecl(condition, errmessage) \
 	static_assert(condition, errmessage)
 #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 StaticAssertExpr(condition, errmessage) \
-	((void) ({ do { struct static_assert_struct { int static_assert_failure : (condition) ? 1 : -1; }; } while(0); }))
-#endif							/* __cpp_static_assert */
 #endif							/* C++ */
 
 
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index b0b0cfdaf79..ec61f301b2c 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -565,9 +565,6 @@
 /* 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 as the maximum alignment requirement of any C data type. */
 #undef MAXIMUM_ALIGNOF
 
-- 
2.51.0

0006-Use-static_assert-inside-StaticAssert-macros.patchtext/plain; charset=UTF-8; name=0006-Use-static_assert-inside-StaticAssert-macros.patchDownload
From 18e3d334d480a05103d6dde46d511775fd987f84 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Tue, 18 Nov 2025 08:43:13 +0100
Subject: [PATCH 6/6] Use static_assert inside StaticAssert* macros

For C23 alignment and to unify the C and C++ definitions.
---
 src/include/c.h               | 19 +++++++++----------
 src/include/regex/regcustom.h |  1 +
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/src/include/c.h b/src/include/c.h
index d462ebbebd1..e5779ee5c26 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>
@@ -863,7 +864,6 @@ typedef NameData *Name;
 
 #elif defined(FRONTEND)
 
-#include <assert.h>
 #define Assert(p) assert(p)
 #define AssertMacro(p)	((void) assert(p))
 
@@ -914,22 +914,21 @@ 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).
  *
- * We require C11 and C++11, so _Static_assert()/static_assert() are expected
- * to be there.  For portability, we wrap it into StaticAssertDecl().
- * _Static_assert() is a "declaration", and so it must be placed where for
- * example a variable declaration would be valid.  As long as we compile with
+ * We require C11 and C++11, so static_assert() is expected to be there.
+ * StaticAssertDecl() was previously used for portability, but it's now just a
+ * plain wrapper and doesn't need to be used in new code.  static_assert() 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.  The macro StaticAssertExpr() makes it safe to
  * use in an expression.
  */
-#ifndef __cplusplus
 #define StaticAssertDecl(condition, errmessage) \
-	_Static_assert(condition, errmessage)
+	static_assert(condition, errmessage)
+#ifndef __cplusplus
 #define StaticAssertExpr(condition, errmessage) \
-	((void) sizeof(struct {_Static_assert(condition, errmessage); char a;}))
+	((void) sizeof(struct {static_assert(condition, errmessage); char a;}))
 #else							/* C++ */
-#define StaticAssertDecl(condition, errmessage) \
-	static_assert(condition, errmessage)
 #define StaticAssertExpr(condition, errmessage) \
 	({ static_assert(condition, errmessage); })
 #endif							/* C++ */
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.0

#5Peter Eisentraut
peter@eisentraut.org
In reply to: Thomas Munro (#3)
Re: Confused static assertion implementation

On 14.11.25 23:27, Thomas Munro wrote:

On Fri, Nov 14, 2025 at 6:18 PM Chao Li <li.evan.chao@gmail.com> wrote:

As you added a semi-colon in the line, the one after the empty line can be deleted, though C allows empty statement, but unnecessary, and may lead to confusion for code readers.

You missed to replace this pgac_cv__static_assert with the new name.

Ugh, yeah, the configure change was hopeless. It looked like it
worked in configure's stdout, which I mistook for success and posted
too soon, sorry about that. I have fixed those points and verified
that pg_config.h actually has the expected value.

I have committed this, with some light editing of the comments.

We are now down to 4 from previously 12 static assertion implementation
variants!

Note, however, that the now committed non-statement-expression fallback
implementation of StaticAssertExpr() does not work under C++. (The
compiler complains about "types may not be defined in 'sizeof'
expressions".) This isn't a regression in the overall sense, since, as
you had mentioned, previously all C++ variants required statement
expressions. So I think this can be added to the list of C++
portability issues that Jelte is currently working through.