public inbox for [email protected]
help / color / mirror / Atom feedFrom: Peter Eisentraut <[email protected]>
To: Jelte Fennema-Nio <[email protected]>
To: PostgreSQL Hackers <[email protected]>
To: Thomas Munro <[email protected]>
Subject: Re: Make copyObject work in C++
Date: Mon, 8 Dec 2025 08:57:17 +0100
Message-ID: <[email protected]> (raw)
In-Reply-To: <CAGECzQR21OnnKiZO_1rLWO0-16kg1JBxnVq-wymYW0-_1cUNtg@mail.gmail.com>
References: <CAGECzQR21OnnKiZO_1rLWO0-16kg1JBxnVq-wymYW0-_1cUNtg@mail.gmail.com>
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
Attachments:
[text/plain] 0001-Change-copyObject-to-use-typeof_unqual.patch (5.8K, 2-0001-Change-copyObject-to-use-typeof_unqual.patch)
download | inline diff:
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
view thread (25+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected], [email protected]
Subject: Re: Make copyObject work in C++
In-Reply-To: <[email protected]>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox