From c8773ef525956b9dcf658f6734bbc1b5d445d4ea Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Fri, 31 Oct 2025 06:07:15 +1300
Subject: [PATCH] Add pg_expr_has_type_p() for type check assertions.

Remove use of GCC builtins, so our existing type check macros can work
on Visual Studio too.  This relies on new features in C11/C++11.

XXX works on Visual Studio 2022, crashes on Visual Studio 2019:

[23:39:47.176] ../src/common/file_utils.c(712): fatal error C1001: Internal compiler error.
[23:39:47.176] (compiler file 'msc1.cpp', line 1603)
[23:39:47.176]  To work around this problem, try simplifying or changing the program near the locations listed above.
---
 config/c-compiler.m4       | 19 ----------------
 configure                  | 30 --------------------------
 configure.ac               |  1 -
 meson.build                | 15 -------------
 src/include/c.h            | 44 +++++++++++++++-----------------------
 src/include/pg_config.h.in |  3 ---
 src/include/utils/relptr.h | 16 ++++----------
 7 files changed, 21 insertions(+), 107 deletions(-)

diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index 236a59e8536..26d5269e58a 100644
--- a/config/c-compiler.m4
+++ b/config/c-compiler.m4
@@ -161,25 +161,6 @@ fi])# PGAC_C_TYPEOF
 
 
 
-# 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 3a0ed11fa8e..5ad57aff950 100755
--- a/configure
+++ b/configure
@@ -14786,36 +14786,6 @@ cat >>confdefs.h <<_ACEOF
 _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
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_constant_p" >&5
 $as_echo_n "checking for __builtin_constant_p... " >&6; }
diff --git a/configure.ac b/configure.ac
index c2413720a18..e2821b665c1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1676,7 +1676,6 @@ AC_C_INLINE
 PGAC_PRINTF_ARCHETYPE
 PGAC_C_STATIC_ASSERT
 PGAC_C_TYPEOF
-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 c1e17aa3040..5170e0748f5 100644
--- a/meson.build
+++ b/meson.build
@@ -1949,21 +1949,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 757dfff4782..02f65424dcc 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -80,6 +80,13 @@
 #endif
 #ifdef ENABLE_NLS
 #include <libintl.h>
+#endif
+
+#ifdef __cplusplus
+extern "C++"
+{
+#include <type_traits>
+}
 #endif
 
  /* Pull in fundamental symbols that we also expose to applications */
@@ -968,26 +975,19 @@ pg_noreturn extern void ExceptionalCondition(const char *conditionName,
  * AssertVariableIsOfType() can be used as a statement.
  * AssertVariableIsOfTypeMacro() is intended for use in macros, eg
  *		#define foo(x) (AssertVariableIsOfTypeMacro(x, int), bar(x))
- *
- * If we don't have __builtin_types_compatible_p, we can still assert that
- * the types have the same size.  This is far from ideal (especially on 32-bit
- * platforms) but it provides at least some coverage.
  */
-#ifdef HAVE__BUILTIN_TYPES_COMPATIBLE_P
-#define AssertVariableIsOfType(varname, typename) \
-	StaticAssertStmt(__builtin_types_compatible_p(__typeof__(varname), typename), \
-	CppAsString(varname) " does not have type " CppAsString(typename))
-#define AssertVariableIsOfTypeMacro(varname, typename) \
-	(StaticAssertExpr(__builtin_types_compatible_p(__typeof__(varname), typename), \
-	 CppAsString(varname) " does not have type " CppAsString(typename)))
-#else							/* !HAVE__BUILTIN_TYPES_COMPATIBLE_P */
+#ifdef __cplusplus
+#define pg_expr_has_type_p(expr, typename) \
+	std::is_same<std::remove_reference<decltype(expr)>::type, typename>::value
+#else
+#define pg_expr_has_type_p(expr, typename) _Generic((expr), typename: 1, default: 0)
+#endif
 #define AssertVariableIsOfType(varname, typename) \
-	StaticAssertStmt(sizeof(varname) == sizeof(typename), \
+	StaticAssertStmt(pg_expr_has_type_p(varname, typename), \
 	CppAsString(varname) " does not have type " CppAsString(typename))
 #define AssertVariableIsOfTypeMacro(varname, typename) \
-	(StaticAssertExpr(sizeof(varname) == sizeof(typename), \
+	(StaticAssertExpr(pg_expr_has_type_p(varname, typename), \
 	 CppAsString(varname) " does not have type " CppAsString(typename)))
-#endif							/* HAVE__BUILTIN_TYPES_COMPATIBLE_P */
 
 
 /* ----------------------------------------------------------------
@@ -1235,24 +1235,14 @@ typedef union PGAlignedXLogBlock
  * Note that this only works in function scope, not for global variables (it'd
  * be nice, but not trivial, to improve that).
  */
-#if defined(__cplusplus)
-#define unconstify(underlying_type, expr) const_cast<underlying_type>(expr)
-#define unvolatize(underlying_type, expr) const_cast<underlying_type>(expr)
-#elif defined(HAVE__BUILTIN_TYPES_COMPATIBLE_P)
 #define unconstify(underlying_type, expr) \
-	(StaticAssertExpr(__builtin_types_compatible_p(__typeof(expr), const underlying_type), \
+	(StaticAssertExpr(pg_expr_has_type_p((expr), const underlying_type), \
 					  "wrong cast"), \
 	 (underlying_type) (expr))
 #define unvolatize(underlying_type, expr) \
-	(StaticAssertExpr(__builtin_types_compatible_p(__typeof(expr), volatile underlying_type), \
+	(StaticAssertExpr(pg_expr_has_type_p((expr), volatile underlying_type), \
 					  "wrong cast"), \
 	 (underlying_type) (expr))
-#else
-#define unconstify(underlying_type, expr) \
-	((underlying_type) (expr))
-#define unvolatize(underlying_type, expr) \
-	((underlying_type) (expr))
-#endif
 
 /* ----------------------------------------------------------------
  *				Section 9: system-specific hacks
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index b0b0cfdaf79..4a25155239e 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -547,9 +547,6 @@
 /* Define to 1 if your compiler understands __builtin_popcount. */
 #undef HAVE__BUILTIN_POPCOUNT
 
-/* 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/include/utils/relptr.h b/src/include/utils/relptr.h
index ea340fee657..48e394dba71 100644
--- a/src/include/utils/relptr.h
+++ b/src/include/utils/relptr.h
@@ -38,16 +38,12 @@
 #define relptr_declare(type, relptrtype) \
 	typedef relptr(type) relptrtype
 
-#ifdef HAVE__BUILTIN_TYPES_COMPATIBLE_P
+#ifdef HAVE_TYPEOF
 #define relptr_access(base, rp) \
 	(AssertVariableIsOfTypeMacro(base, char *), \
-	 (__typeof__((rp).relptr_type)) ((rp).relptr_off == 0 ? NULL : \
+	 (typeof((rp).relptr_type)) ((rp).relptr_off == 0 ? NULL : \
 		(base) + (rp).relptr_off - 1))
 #else
-/*
- * If we don't have __builtin_types_compatible_p, assume we might not have
- * __typeof__ either.
- */
 #define relptr_access(base, rp) \
 	(AssertVariableIsOfTypeMacro(base, char *), \
 	 (void *) ((rp).relptr_off == 0 ? NULL : (base) + (rp).relptr_off - 1))
@@ -72,16 +68,12 @@ relptr_store_eval(char *base, char *val)
 	}
 }
 
-#ifdef HAVE__BUILTIN_TYPES_COMPATIBLE_P
+#ifdef HAVE_TYPEOF
 #define relptr_store(base, rp, val) \
 	(AssertVariableIsOfTypeMacro(base, char *), \
-	 AssertVariableIsOfTypeMacro(val, __typeof__((rp).relptr_type)), \
+	 AssertVariableIsOfTypeMacro(val, typeof((rp).relptr_type)), \
 	 (rp).relptr_off = relptr_store_eval((base), (char *) (val)))
 #else
-/*
- * If we don't have __builtin_types_compatible_p, assume we might not have
- * __typeof__ either.
- */
 #define relptr_store(base, rp, val) \
 	(AssertVariableIsOfTypeMacro(base, char *), \
 	 (rp).relptr_off = relptr_store_eval((base), (char *) (val)))
-- 
2.51.1

