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 <https://www.postgresql.org/message-id/01a69441-af54-4822-891b-ca28e05b215a%40eisentraut.org> 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.
From 6b06890f74a0003abc33aabf130825ffe3eeb694 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
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

From 01c4101fd27348eafae1458254b319f77b0e4ccf Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
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

From 9731567ce2a0e40d3daa740923bf55df40415a6d Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
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

From 01b3fa89ef82af22f1274d90c0fed83cd4005388 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
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

From 7af13e08a0f7151137a46bc38543d1134eea961d Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
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

From 18e3d334d480a05103d6dde46d511775fd987f84 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
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

Reply via email to