From 5b021f5243c872327598604165e057ea99712deb Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Tue, 3 Nov 2026 15:39:36 +0100 Subject: [PATCH 07/10] Add unconstify_pointee() getopt_long() deliberately reorders argv in place despite its "char *const argv[]" declaration, so it aliases the array as a writable "char **", casting away the const that qualifies the char * elements. Plain unconstify() cannot express this. It checks the expression against "const underlying_type", with const prepended textually, so the const always binds to the base type spelled first: unconstify(char **, x) expects a "const char **", not a "char *const *". Add unconstify_pointee(), which takes the pointed-to type and turns a "pointee_type const *" into a "pointee_type *". Placing the const after the type makes it qualify the whole pointee type, whichever way that is spelled, and it keeps the same static type-checking as unconstify(). Use it in getopt_long.c and also for TupleTableSlotOps initialization. Both of these uses write through the result, which the comment on unconstify() claimed was never safe. But it is only undefined behavior if the object itself was defined as const, which is not the case for argv or for a palloc'ed TupleTableSlot. Reword that comment accordingly. --- src/backend/executor/execTuples.c | 2 +- src/include/c.h | 23 ++++++++++++++++++----- src/port/getopt_long.c | 2 +- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index d83322cc510..664cf0f18cf 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -1394,7 +1394,7 @@ MakeTupleTableSlot(TupleDesc tupleDesc, slot = palloc0(allocsz); /* const for optimization purposes, OK to modify at allocation time */ - *((const TupleTableSlotOps **) &slot->tts_ops) = tts_ops; + *(unconstify_pointee(const TupleTableSlotOps *, &slot->tts_ops)) = tts_ops; slot->type = T_TupleTableSlot; slot->tts_flags = TTS_FLAG_EMPTY | flags; if (tupleDesc != NULL) diff --git a/src/include/c.h b/src/include/c.h index b285755e8c4..79b82226551 100644 --- a/src/include/c.h +++ b/src/include/c.h @@ -1420,11 +1420,11 @@ typedef struct PGAlignedXLogBlock PGAlignedXLogBlock; * will fail. In the C++ implementation, const_cast will error for * non-pointers.) * - * Please note IT IS NOT SAFE to cast constness away if the result will ever - * be modified (it would be undefined behaviour). Doing so anyway can cause - * compiler misoptimizations or runtime crashes (modifying readonly memory). - * It is only safe to use when the result will not be modified, but API - * design or language restrictions prevent you from declaring that + * Please note IT IS NOT SAFE to modify an object through the result if the + * object itself was defined as const (it would be undefined behaviour). + * Doing so anyway can cause compiler misoptimizations or runtime crashes + * (modifying readonly memory). These macros are meant for when API design + * or language restrictions prevent you from declaring the actual constness * (e.g. because a function returns both const and non-const variables). * * unconstify_constexpr is for contexts where a constant expression is @@ -1435,15 +1435,28 @@ typedef struct PGAlignedXLogBlock PGAlignedXLogBlock; * * (An unvolatize_constexpr doesn't seem necessary, but it could be added if * required.) + * + * unconstify checks that expr has type "const underlying_type", with const + * prepended textually. So the const binds to the base type spelled first, + * for example, unconstify(char **, x) casts a "const char **" to "char **". + * That means it cannot remove a const that qualifies a pointer type spelled + * with "*", as in "char *const *". For that, use unconstify_pointee, which + * takes the pointed-to type and turns a "pointee_type const *" into a + * "pointee_type *". For example, unconstify_pointee(char *, argv) casts a + * "char *const *" to "char **". */ #if defined(__cplusplus) #define unconstify(underlying_type, expr) const_cast(expr) +#define unconstify_pointee(pointee_type, expr) const_cast(expr) #define unconstify_constexpr(underlying_type, expr) const_cast(expr) #define unvolatize(underlying_type, expr) const_cast(expr) #else /* !__cplusplus */ #define unconstify(underlying_type, expr) \ (StaticAssertVariableIsOfTypeMacro(expr, const underlying_type), \ (underlying_type) (expr)) +#define unconstify_pointee(pointee_type, expr) \ + (StaticAssertVariableIsOfTypeMacro(expr, pointee_type const *), \ + (pointee_type *) (expr)) #define unvolatize(underlying_type, expr) \ (StaticAssertVariableIsOfTypeMacro(expr, volatile underlying_type), \ (underlying_type) (expr)) diff --git a/src/port/getopt_long.c b/src/port/getopt_long.c index f2edadb59d5..4b35072de3d 100644 --- a/src/port/getopt_long.c +++ b/src/port/getopt_long.c @@ -71,7 +71,7 @@ getopt_long(int argc, char *const argv[], if (!*place) { /* update scanning pointer */ - char **args = (char **) argv; + char **args = unconstify_pointee(char *, argv); retry: -- 2.56.0