v13-0000-Replace-__builtin_types_compatible_p-with-_Gener.patch
text/x-patch
Filename: v13-0000-Replace-__builtin_types_compatible_p-with-_Gener.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v13-0000
Subject: Replace __builtin_types_compatible_p with _Generic
| File | + | − |
|---|---|---|
| config/c-compiler.m4 | 0 | 19 |
| configure | 1 | 30 |
| configure.ac | 0 | 1 |
| meson.build | 0 | 15 |
| src/include/c.h | 28 | 9 |
| src/include/pg_config.h.in | 0 | 3 |
| src/test/modules/test_cplusplusext/test_cplusplusext.cpp | 17 | 0 |
| src/test/modules/test_extensions/test_ext.c | 16 | 0 |
From 16c0bf5045984e358df88640940db603c058dd1a Mon Sep 17 00:00:00 2001
From: Jelte Fennema-Nio <postgres@jeltef.nl>
Date: Wed, 18 Mar 2026 09:37:32 +0100
Subject: [PATCH v13 0/5] Replace __builtin_types_compatible_p with _Generic
Remove all our use of GCC __builtin_types_compatible_p builtin with a
new pg_expr_has_type_p macro that relies on standard C11/C++11 features.
This allows our existing type check macros to work on Visual Studio too.
Some explicit tests for the new macro are added, both for C and C++ to
ensure they behave the same.
---
config/c-compiler.m4 | 19 ----------
configure | 31 +---------------
configure.ac | 1 -
meson.build | 15 --------
src/include/c.h | 37 ++++++++++++++-----
src/include/pg_config.h.in | 3 --
.../test_cplusplusext/test_cplusplusext.cpp | 17 +++++++++
src/test/modules/test_extensions/test_ext.c | 16 ++++++++
8 files changed, 62 insertions(+), 77 deletions(-)
diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index 629572ee350..d2d93002985 100644
--- a/config/c-compiler.m4
+++ b/config/c-compiler.m4
@@ -267,25 +267,6 @@ fi])# PGAC_CXX_TYPEOF_UNQUAL
-# PGAC_C_TYPES_COMPATIBLE
-# -----------------------
-# Check if the C compiler understands __builtin_types_compatible_p,
-# and define HAVE__BUILTIN_TYPES_COMPATIBLE_P if so.
-#
-# We check usage with __typeof__, though it's unlikely any compiler would
-# have the former and not the latter.
-AC_DEFUN([PGAC_C_TYPES_COMPATIBLE],
-[AC_CACHE_CHECK(for __builtin_types_compatible_p, pgac_cv__types_compatible,
-[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],
-[[ int x; static int y[__builtin_types_compatible_p(__typeof__(x), int)]; ]])],
-[pgac_cv__types_compatible=yes],
-[pgac_cv__types_compatible=no])])
-if test x"$pgac_cv__types_compatible" = xyes ; then
-AC_DEFINE(HAVE__BUILTIN_TYPES_COMPATIBLE_P, 1,
- [Define to 1 if your compiler understands __builtin_types_compatible_p.])
-fi])# PGAC_C_TYPES_COMPATIBLE
-
-
# PGAC_C_BUILTIN_CONSTANT_P
# -------------------------
# Check if the C compiler understands __builtin_constant_p(),
diff --git a/configure b/configure
index fe22bc71d0c..cff9a6bb4f0 100755
--- a/configure
+++ b/configure
@@ -15182,36 +15182,7 @@ _ACEOF
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_types_compatible_p" >&5
-$as_echo_n "checking for __builtin_types_compatible_p... " >&6; }
-if ${pgac_cv__types_compatible+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-
-int
-main ()
-{
- int x; static int y[__builtin_types_compatible_p(__typeof__(x), int)];
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- pgac_cv__types_compatible=yes
-else
- pgac_cv__types_compatible=no
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv__types_compatible" >&5
-$as_echo "$pgac_cv__types_compatible" >&6; }
-if test x"$pgac_cv__types_compatible" = xyes ; then
-
-$as_echo "#define HAVE__BUILTIN_TYPES_COMPATIBLE_P 1" >>confdefs.h
-
-fi
+PGAC_C_TYPES_COMPATIBLE
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_constant_p" >&5
$as_echo_n "checking for __builtin_constant_p... " >&6; }
if ${pgac_cv__builtin_constant_p+:} false; then :
diff --git a/configure.ac b/configure.ac
index 6873b7546dd..667c566522a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1727,7 +1727,6 @@ PGAC_C_TYPEOF
PGAC_CXX_TYPEOF
PGAC_C_TYPEOF_UNQUAL
PGAC_CXX_TYPEOF_UNQUAL
-PGAC_C_TYPES_COMPATIBLE
PGAC_C_BUILTIN_CONSTANT_P
PGAC_C_BUILTIN_OP_OVERFLOW
PGAC_C_BUILTIN_UNREACHABLE
diff --git a/meson.build b/meson.build
index 6bc74c2ba79..822efce9ba2 100644
--- a/meson.build
+++ b/meson.build
@@ -2070,21 +2070,6 @@ foreach builtin : builtins
endforeach
-# Check if the C compiler understands __builtin_types_compatible_p,
-# and define HAVE__BUILTIN_TYPES_COMPATIBLE_P if so.
-#
-# We check usage with __typeof__, though it's unlikely any compiler would
-# have the former and not the latter.
-if cc.compiles('''
- static int x;
- static int y[__builtin_types_compatible_p(__typeof__(x), int)];
- ''',
- name: '__builtin_types_compatible_p',
- args: test_c_args)
- cdata.set('HAVE__BUILTIN_TYPES_COMPATIBLE_P', 1)
-endif
-
-
# Check if the C compiler understands __builtin_$op_overflow(),
# and define HAVE__BUILTIN_OP_OVERFLOW if so.
#
diff --git a/src/include/c.h b/src/include/c.h
index 88d13ec9993..6db88644491 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -393,6 +393,23 @@ extern "C++"
#define HAVE_PG_INTEGER_CONSTANT_P
#endif
+/*
+ * pg_expr_has_type_p(expr, type) - Check if an expression has a specific type.
+ *
+ * The C implementation uses _Generic, which applies lvalue conversion to the
+ * controlling expression: arrays decay to pointers, functions decay to function
+ * pointers, and top-level cv-qualifiers are stripped. The C++ implementation
+ * uses std::decay to match this behavior. Note that only top-level qualifiers
+ * are stripped — pointee qualifiers are preserved (e.g. const int * stays
+ * const int *, but int *const becomes int *).
+ */
+#if defined(__cplusplus)
+#define pg_expr_has_type_p(expr, _type) \
+ std::is_same<typename std::decay<decltype(expr)>::type, _type>::value
+#else
+#define pg_expr_has_type_p(expr, type) _Generic((expr), type: 1, default: 0)
+#endif
+
/*
* pg_assume(expr) states that we assume `expr` to evaluate to true. In assert
* enabled builds pg_assume() is turned into an assertion, in optimized builds
@@ -1052,26 +1069,28 @@ pg_noreturn extern void ExceptionalCondition(const char *conditionName,
* StaticAssertVariableIsOfType() can be used as a declaration.
* 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
+#if !defined(_MSC_VER) || _MSC_VER >= 1933
#define StaticAssertVariableIsOfType(varname, typename) \
- StaticAssertDecl(__builtin_types_compatible_p(typeof(varname), typename), \
+ StaticAssertDecl(pg_expr_has_type_p(varname, typename), \
CppAsString(varname) " does not have type " CppAsString(typename))
#define StaticAssertVariableIsOfTypeMacro(varname, typename) \
- (StaticAssertExpr(__builtin_types_compatible_p(typeof(varname), typename), \
+ (StaticAssertExpr(pg_expr_has_type_p(varname, typename), \
CppAsString(varname) " does not have type " CppAsString(typename)))
-#else /* !HAVE__BUILTIN_TYPES_COMPATIBLE_P */
+#else /* _MSC_VER < 1933 */
+/*
+ * This compiler is buggy and fails to compile the previous variant; use a
+ * fallback implementation that asserts that the types have the same size.
+ * This is far from ideal (especially on 32-bit platforms) but it provides at
+ * least some coverage.
+ */
#define StaticAssertVariableIsOfType(varname, typename) \
StaticAssertDecl(sizeof(varname) == sizeof(typename), \
CppAsString(varname) " does not have type " CppAsString(typename))
#define StaticAssertVariableIsOfTypeMacro(varname, typename) \
(StaticAssertExpr(sizeof(varname) == sizeof(typename), \
CppAsString(varname) " does not have type " CppAsString(typename)))
-#endif /* HAVE__BUILTIN_TYPES_COMPATIBLE_P */
+#endif /* _MSC_VER < 1933 */
/* ----------------------------------------------------------------
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index d8d61918aff..cf7abc56c1c 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -541,9 +541,6 @@
/* Define to 1 if your compiler understands __builtin_$op_overflow. */
#undef HAVE__BUILTIN_OP_OVERFLOW
-/* Define to 1 if your compiler understands __builtin_types_compatible_p. */
-#undef HAVE__BUILTIN_TYPES_COMPATIBLE_P
-
/* Define to 1 if your compiler understands __builtin_unreachable. */
#undef HAVE__BUILTIN_UNREACHABLE
diff --git a/src/test/modules/test_cplusplusext/test_cplusplusext.cpp b/src/test/modules/test_cplusplusext/test_cplusplusext.cpp
index 93cd7dd07f7..df7a592cb70 100644
--- a/src/test/modules/test_cplusplusext/test_cplusplusext.cpp
+++ b/src/test/modules/test_cplusplusext/test_cplusplusext.cpp
@@ -27,6 +27,23 @@ PG_FUNCTION_INFO_V1(test_cplusplus_add);
StaticAssertDecl(sizeof(int32) == 4, "int32 should be 4 bytes");
+/* Same tests as in test_ext.c, but compiled with a C++ compiler to verify that
+ * the pg_expr_has_type_p macro works correctly in C++. */
+StaticAssertDecl(pg_expr_has_type_p((int32) 123, int32), "int32 expression should be int32");
+StaticAssertDecl(!pg_expr_has_type_p((int32) 123, int64), "int32 expression should not be int64");
+StaticAssertDecl(pg_expr_has_type_p(((char (*)[10]) nullptr)[0], char *),
+ "array should decay into pointer");
+StaticAssertDecl(pg_expr_has_type_p((char (*)[10]) nullptr, char (*)[10]),
+ "pointer to an aray should work if it has the same size");
+StaticAssertDecl(!pg_expr_has_type_p((char (*)[5]) nullptr, char (*)[10]),
+ "pointer to an aray should not match if it does not have the same size");
+StaticAssertDecl(pg_expr_has_type_p((const int *) nullptr, const int *),
+ "const pointers of same type should match");
+StaticAssertDecl(!pg_expr_has_type_p((const int *) nullptr, int *),
+ "const pointer should not match non-const pointer");
+StaticAssertDecl(pg_expr_has_type_p((const int) 0, int),
+ "top-level const should be stripped");
+
/*
* Simple function that returns the sum of two integers. This verifies that
* C++ extension modules can be loaded and called correctly at runtime.
diff --git a/src/test/modules/test_extensions/test_ext.c b/src/test/modules/test_extensions/test_ext.c
index a23165ba67a..d528a770dee 100644
--- a/src/test/modules/test_extensions/test_ext.c
+++ b/src/test/modules/test_extensions/test_ext.c
@@ -13,6 +13,22 @@ PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(test_ext);
+/* Confirm that C implementation of pg_expr_has_type_p works as expected on all compilers. */
+StaticAssertDecl(pg_expr_has_type_p((int32) 123, int32), "int32 expression should be int32");
+StaticAssertDecl(!pg_expr_has_type_p((int32) 123, int64), "int32 expression should not be int64");
+StaticAssertDecl(pg_expr_has_type_p(((char (*)[10]) NULL)[0], char *),
+ "array should decay into pointer");
+StaticAssertDecl(pg_expr_has_type_p((char (*)[10]) NULL, char (*)[10]),
+ "pointer to an aray should work if it has the same size");
+StaticAssertDecl(!pg_expr_has_type_p((char (*)[5]) NULL, char (*)[10]),
+ "pointer to an aray should not match if it does not have the same size");
+StaticAssertDecl(pg_expr_has_type_p((const int *) NULL, const int *),
+ "const pointers of same type should match");
+StaticAssertDecl(!pg_expr_has_type_p((const int *) NULL, int *),
+ "const pointer should not match non-const pointer");
+StaticAssertDecl(pg_expr_has_type_p((const int) 0, int),
+ "top-level const should be stripped");
+
Datum
test_ext(PG_FUNCTION_ARGS)
{
base-commit: effaa464afd355e8927bf430cfe6a0ddd2ee5695
--
2.53.0