On 05.12.25 15:46, Jelte Fennema-Nio wrote:
Calling copyObject fails in C++ with an error like in most setups:

error: use of undeclared identifier 'typeof'; did you mean 'typeid'

This is due to the C compiler supporting used to compile postgres
supporting typeof, but that function actually not being present in the
C++ compiler. This fixes that by using decltype instead of typeof when
including the header in C++.

Realized because of Thomas' not about how much of our headers should
work in C++, and remembering I hit this specific problem myself.

Another approach would be to force the value of HAVE_TYPEOF to 0 if __cplusplus.

In the long run, I would like to change copyObject() to use typeof_unqual instead, because that handles qualifiers more correctly. (Currently, copyObject() of a const-qualified pointer results in a const-qualified pointer, which is nonsensical because the reason you made the copy is that you can modify it.) See attached patch for an example. Does C++ have something that is semantically similar to that?
From 1e733f16f116be638cbd4f6a359a01541c9a5d24 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Mon, 8 Dec 2025 08:33:18 +0100
Subject: [PATCH] Change copyObject() to use typeof_unqual

The new implementation ensures that the result of copyObject() is not
const-qualified when the input is.  This was previously incorrect,
since the point of copyObject() is to make a copy to mutate, but
apparently no code ran into it.
---
 config/c-compiler.m4       | 25 +++++++++++++++++++++++
 configure                  | 42 ++++++++++++++++++++++++++++++++++++++
 configure.ac               |  1 +
 meson.build                | 24 ++++++++++++++++++++++
 src/include/nodes/nodes.h  |  4 ++--
 src/include/pg_config.h.in |  7 +++++++
 6 files changed, 101 insertions(+), 2 deletions(-)

diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index 236a59e8536..8ee860c9091 100644
--- a/config/c-compiler.m4
+++ b/config/c-compiler.m4
@@ -160,6 +160,31 @@ if test "$pgac_cv_c_typeof" != no; then
 fi])# PGAC_C_TYPEOF
 
 
+# PGAC_C_TYPEOF_UNQUAL
+# --------------------
+# Check if the C compiler understands typeof_unqual or a variant.  Define
+# HAVE_TYPEOF_UNQUAL if so, and define 'typeof_unqual' to the actual key word.
+#
+AC_DEFUN([PGAC_C_TYPEOF_UNQUAL],
+[AC_CACHE_CHECK(for typeof_unqual, pgac_cv_c_typeof_unqual,
+[pgac_cv_c_typeof_unqual=no
+for pgac_kw in typeof_unqual __typeof_unqual__; do
+  AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],
+[int x = 0;
+$pgac_kw(x) y;
+y = x;
+return y;])],
+[pgac_cv_c_typeof_unqual=$pgac_kw])
+  test "$pgac_cv_c_typeof_unqual" != no && break
+done])
+if test "$pgac_cv_c_typeof_unqual" != no; then
+  AC_DEFINE(HAVE_TYPEOF_UNQUAL, 1,
+            [Define to 1 if your compiler understands `typeof_unqual' or 
something similar.])
+  if test "$pgac_cv_c_typeof_unqual" != typeof_unqual; then
+    AC_DEFINE_UNQUOTED(typeof_unqual, $pgac_cv_c_typeof_unqual, [Define to how 
the compiler spells `typeof_unqual'.])
+  fi
+fi])# PGAC_C_TYPEOF_UNQUAL
+
 
 # PGAC_C_TYPES_COMPATIBLE
 # -----------------------
diff --git a/configure b/configure
index 3a0ed11fa8e..af856b20f0b 100755
--- a/configure
+++ b/configure
@@ -14787,6 +14787,48 @@ _ACEOF
 
   fi
 fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for typeof_unqual" >&5
+$as_echo_n "checking for typeof_unqual... " >&6; }
+if ${pgac_cv_c_typeof_unqual+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  pgac_cv_c_typeof_unqual=no
+for pgac_kw in typeof_unqual __typeof_unqual__; do
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+int x = 0;
+$pgac_kw(x) y;
+y = x;
+return y;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  pgac_cv_c_typeof_unqual=$pgac_kw
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  test "$pgac_cv_c_typeof_unqual" != no && break
+done
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_c_typeof_unqual" >&5
+$as_echo "$pgac_cv_c_typeof_unqual" >&6; }
+if test "$pgac_cv_c_typeof_unqual" != no; then
+
+$as_echo "#define HAVE_TYPEOF_UNQUAL 1" >>confdefs.h
+
+  if test "$pgac_cv_c_typeof_unqual" != typeof_unqual; then
+
+cat >>confdefs.h <<_ACEOF
+#define typeof_unqual $pgac_cv_c_typeof_unqual
+_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 :
diff --git a/configure.ac b/configure.ac
index c2413720a18..8eced24beb5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1676,6 +1676,7 @@ AC_C_INLINE
 PGAC_PRINTF_ARCHETYPE
 PGAC_C_STATIC_ASSERT
 PGAC_C_TYPEOF
+PGAC_C_TYPEOF_UNQUAL
 PGAC_C_TYPES_COMPATIBLE
 PGAC_C_BUILTIN_CONSTANT_P
 PGAC_C_BUILTIN_OP_OVERFLOW
diff --git a/meson.build b/meson.build
index 6e7ddd74683..85b98ad81db 100644
--- a/meson.build
+++ b/meson.build
@@ -2818,6 +2818,30 @@ int main(void)
   endif
 endforeach
 
+# Check if the C compiler understands typeof_unqual or a variant.  Define
+# HAVE_TYPEOF_UNQUAL if so, and define 'typeof_unqual' to the actual key word.
+foreach kw : ['typeof_unqual', '__typeof_unqual__']
+  if cc.compiles('''
+int main(void)
+{
+    int x = 0;
+    @0@(x) y;
+    y = x;
+    return y;
+}
+'''.format(kw),
+    name: kw,
+    args: test_c_args, include_directories: postgres_inc)
+
+    cdata.set('HAVE_TYPEOF_UNQUAL', 1)
+    if kw != 'typeof_unqual'
+      cdata.set('typeof_unqual', kw)
+    endif
+
+    break
+  endif
+endforeach
+
 
 # Even though restrict is in C99 and should be supported by all
 # supported compilers, this indirection is useful because __restrict
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index fb3957e75e5..0ac0be1b288 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -226,8 +226,8 @@ extern int16 *readAttrNumberCols(int numCols);
 extern void *copyObjectImpl(const void *from);
 
 /* cast result back to argument type, if supported by compiler */
-#ifdef HAVE_TYPEOF
-#define copyObject(obj) ((typeof(obj)) copyObjectImpl(obj))
+#ifdef HAVE_TYPEOF_UNQUAL
+#define copyObject(obj) ((typeof_unqual(*(obj)) *) copyObjectImpl(obj))
 #else
 #define copyObject(obj) copyObjectImpl(obj)
 #endif
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index b0b0cfdaf79..503467ca092 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -472,6 +472,10 @@
 /* Define to 1 if your compiler understands `typeof' or something similar. */
 #undef HAVE_TYPEOF
 
+/* Define to 1 if your compiler understands `typeof_unqual' or something
+   similar. */
+#undef HAVE_TYPEOF_UNQUAL
+
 /* Define to 1 if you have the <uchar.h> header file. */
 #undef HAVE_UCHAR_H
 
@@ -815,3 +819,6 @@
 
 /* Define to how the compiler spells `typeof'. */
 #undef typeof
+
+/* Define to how the compiler spells `typeof_unqual'. */
+#undef typeof_unqual
-- 
2.52.0

Reply via email to