Hi,
Our assertion quality is lower on Visual Studio. I assumed there was
nothing stopping us from writing a pg_expr_has_type_p() macro using
pure standard C and C++ these days, and the attached patch seemed to
work on GCC, Clang and Visual Studio 2022 (via CI, no Windows here).
Unfortunately our unconstify() macro triggers internal errors on
Visual Studio 2019 with this applied:
[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.
Presumably _Generic type resolution and StaticAssertExpr()'s
definition are just too much for it. I wonder if some other phrasing
could help. Posting where I got to with this, in case anyone has any
ideas...
From c8773ef525956b9dcf658f6734bbc1b5d445d4ea Mon Sep 17 00:00:00 2001
From: Thomas Munro <[email protected]>
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