We have been progressively cleaning up -Wcast-qual warnings this year.
This patch set is the final part of this, containing a few tricky,
complicated, and/or ugly cases that I have been putting off. And then
we can turn -Wcast-qual on by default. Each patch is individually
described in its commit message. Alternative approaches are possible;
feedback welcome.From 225564b3bcf3fdbaa78d47e67d3b5da6fb5e8d39 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Tue, 3 Nov 2026 15:39:36 +0100
Subject: [PATCH 01/10] Fix accidentally casting away const
Recently introduced in commit 6e58d6356fc.
---
src/include/access/detoast.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/include/access/detoast.h b/src/include/access/detoast.h
index 93b7a253760..238af79f719 100644
--- a/src/include/access/detoast.h
+++ b/src/include/access/detoast.h
@@ -23,7 +23,7 @@
*/
#define VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr) \
do { \
- varattrib_1b_e *attre = (varattrib_1b_e *) (attr); \
+ const varattrib_1b_e *attre = (const varattrib_1b_e *) (attr); \
Assert(VARATT_IS_EXTERNAL(attre)); \
Assert(VARSIZE_EXTERNAL(attre) == sizeof(toast_pointer) +
VARHDRSZ_EXTERNAL); \
memcpy(&(toast_pointer), VARDATA_EXTERNAL(attre),
sizeof(toast_pointer)); \
base-commit: 3ba886b35a2804dcd275868a19135a31ceb81386
--
2.56.0
From d5b2c5fb2fca378b5c59aa005b16375d14cfd228 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Tue, 3 Nov 2026 15:39:36 +0100
Subject: [PATCH 02/10] Fix -Wcast-qual warning in lnext()
With a little bit of restructuring we can avoid the warning. Also, by
using list_cell_number() we don't have to duplicate the assertion.
---
src/include/nodes/pg_list.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h
index e93bcbf2698..2d16e0819ca 100644
--- a/src/include/nodes/pg_list.h
+++ b/src/include/nodes/pg_list.h
@@ -374,10 +374,10 @@ list_cell_number(const List *l, const ListCell *c)
static inline ListCell *
lnext(const List *l, const ListCell *c)
{
- Assert(c >= &l->elements[0] && c < &l->elements[l->length]);
- c++;
- if (c < &l->elements[l->length])
- return (ListCell *) c;
+ int n = list_cell_number(l, c) + 1;
+
+ if (n < l->length)
+ return &l->elements[n];
else
return NULL;
}
--
2.56.0
From b133812ebe3bdcdb8ea46bf1fd5a865ad8e2e824 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Tue, 3 Nov 2026 15:39:36 +0100
Subject: [PATCH 03/10] Fix -Wcast-qual warning in foreach_delete_current()
foreach_delete_current() returned the value of an assignment to the
loop state's "const List *" field, and then cast away the const to
return List *. The value comes from list_delete_nth_cell() and was
never const, so restructure with a small inline helper that returns
the List * directly, avoiding the cast altogether. Also, this way the
pg_nodiscard attribute of list_delete_nth_cell() can be passed
through.
---
src/include/nodes/pg_list.h | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h
index 2d16e0819ca..295626473d5 100644
--- a/src/include/nodes/pg_list.h
+++ b/src/include/nodes/pg_list.h
@@ -421,7 +421,7 @@ lnext(const List *l, const ListCell *c)
* active foreach or foreach_* loop's list in any other way!
*/
#define foreach_delete_current(lst, var_or_cell) \
- ((List *) (var_or_cell##__state.l = list_delete_nth_cell(lst,
var_or_cell##__state.i--)))
+ foreach_delete_current_impl(lst, &var_or_cell##__state)
/*
* foreach_current_index -
@@ -715,4 +715,14 @@ extern void list_sort(List *list, list_sort_comparator
cmp);
extern int list_int_cmp(const ListCell *p1, const ListCell *p2);
extern int list_oid_cmp(const ListCell *p1, const ListCell *p2);
+/* support for foreach_delete_current() */
+pg_nodiscard static inline List *
+foreach_delete_current_impl(List *lst, ForEachState *state)
+{
+ List *result = list_delete_nth_cell(lst, state->i--);
+
+ state->l = result;
+ return result;
+}
+
#endif /* PG_LIST_H */
--
2.56.0
From 9f163f7172ffecfcfe6c46013b82aa410e98c020 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Tue, 3 Nov 2026 15:39:36 +0100
Subject: [PATCH 04/10] Fix -Wcast-qual warning in XLogInsertRecord()
XLogInsertRecord() obtained the record header by casting the data
pointer of the first entry of the XLogRecData chain:
XLogRecord *rechdr = (XLogRecord *) rdata->data;
This casts away the const qualifier on XLogRecData.data. And
XLogInsertRecord() really does write through that pointer.
To fix, don't derive the header pointer from the chain but instead
pass the record header to XLogInsertRecord() as a separate argument
that is not const.
---
src/backend/access/transam/xlog.c | 13 ++++++++-----
src/backend/access/transam/xloginsert.c | 3 ++-
src/include/access/xlog.h | 4 +++-
3 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/src/backend/access/transam/xlog.c
b/src/backend/access/transam/xlog.c
index 8636ad9ab2c..d20c91dcde2 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -824,9 +824,11 @@ static XLogRecPtr XLogChecksums(uint32 new_type);
* current subtransaction. See XLogRecordAssemble().
*
* The first XLogRecData in the chain must be for the record header, and its
- * data must be MAXALIGNed. XLogInsertRecord fills in the xl_prev and
- * xl_crc fields in the header, the rest of the header must already be filled
- * by the caller.
+ * data must be MAXALIGNed. 'rechdr' must point to that same record header;
+ * it is passed separately because the data pointers in the chain are const.
+ * XLogInsertRecord fills in the xl_prev and xl_crc fields in the header
+ * through 'rechdr', the rest of the header must already be filled by the
+ * caller.
*
* Returns XLOG pointer to end of record (beginning of next record).
* This can be used as LSN for data pages affected by the logged action.
@@ -835,7 +837,8 @@ static XLogRecPtr XLogChecksums(uint32 new_type);
* WAL rule "write the log before the data".)
*/
XLogRecPtr
-XLogInsertRecord(XLogRecData *rdata,
+XLogInsertRecord(XLogRecord *rechdr,
+ XLogRecData *rdata,
XLogRecPtr fpw_lsn,
uint8 flags,
int num_fpi,
@@ -845,7 +848,6 @@ XLogInsertRecord(XLogRecData *rdata,
XLogCtlInsert *Insert = &XLogCtl->Insert;
pg_crc32c rdata_crc;
bool inserted;
- XLogRecord *rechdr = (XLogRecord *) rdata->data;
uint8 info = rechdr->xl_info & ~XLR_INFO_MASK;
WalInsertClass class = WALINSERT_NORMAL;
XLogRecPtr StartPos;
@@ -863,6 +865,7 @@ XLogInsertRecord(XLogRecData *rdata,
}
/* we assume that all of the record header is in the first chunk */
+ Assert(rdata->data == rechdr);
Assert(rdata->len >= SizeOfXLogRecord);
/* cross-check on whether we should be here or not */
diff --git a/src/backend/access/transam/xloginsert.c
b/src/backend/access/transam/xloginsert.c
index 341deb7b80d..3e5aca79cdf 100644
--- a/src/backend/access/transam/xloginsert.c
+++ b/src/backend/access/transam/xloginsert.c
@@ -534,7 +534,8 @@ XLogInsert(RmgrId rmid, uint8 info)
&fpw_lsn,
&num_fpi, &fpi_bytes,
&topxid_included);
- EndPos = XLogInsertRecord(rdt, fpw_lsn, curinsert_flags,
num_fpi,
+ EndPos = XLogInsertRecord((XLogRecord *) hdr_scratch, rdt,
fpw_lsn,
+
curinsert_flags, num_fpi,
fpi_bytes,
topxid_included);
} while (!XLogRecPtrIsValid(EndPos));
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index 2bf5714b51e..966b41a7d89 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -220,10 +220,12 @@ typedef enum WALAvailability
WALAVAIL_REMOVED, /* WAL segment has been removed
*/
} WALAvailability;
+struct XLogRecord;
struct XLogRecData;
struct XLogReaderState;
-extern XLogRecPtr XLogInsertRecord(struct XLogRecData *rdata,
+extern XLogRecPtr XLogInsertRecord(struct XLogRecord *rechdr,
+ struct
XLogRecData *rdata,
XLogRecPtr
fpw_lsn,
uint8 flags,
int num_fpi,
--
2.56.0
From 7b60019bde5c49c02d763da8da3f109e97124a68 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Tue, 3 Nov 2026 15:39:36 +0100
Subject: [PATCH 05/10] Fix -Wcast-qual warnings in regex module
- Replace some casts with unconstify() where appropriate.
- Make pg_regex_t.re_fns pointer to const, because the "functions" it
points to is also const.
- Define VS() to a no-op. The default definition of casting
to (void *) might cast away a qualifier. This construct is only
needed for supporting ancient C, so we don't need it.
---
src/backend/regex/regexec.c | 6 +++---
src/backend/regex/regfree.c | 2 +-
src/include/regex/regcustom.h | 1 +
src/include/regex/regex.h | 2 +-
src/include/regex/regguts.h | 2 +-
5 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/src/backend/regex/regexec.c b/src/backend/regex/regexec.c
index 665aa31bd03..d8bb5c0e617 100644
--- a/src/backend/regex/regexec.c
+++ b/src/backend/regex/regexec.c
@@ -249,9 +249,9 @@ pg_regexec(regex_t *re,
v->nmatch = nmatch;
}
v->details = details;
- v->start = (chr *) string;
- v->search_start = (chr *) string + search_start;
- v->stop = (chr *) string + len;
+ v->start = unconstify(chr *, string);
+ v->search_start = unconstify(chr *, string) + search_start;
+ v->stop = unconstify(chr *, string) + len;
v->err = 0;
v->subdfas = NULL;
v->ladfas = NULL;
diff --git a/src/backend/regex/regfree.c b/src/backend/regex/regfree.c
index ae17ae70eb6..2aac3db19c6 100644
--- a/src/backend/regex/regfree.c
+++ b/src/backend/regex/regfree.c
@@ -50,5 +50,5 @@ pg_regfree(regex_t *re)
{
if (re == NULL)
return;
- (*((struct fns *) re->re_fns)->free) (re);
+ (*((const struct fns *) re->re_fns)->free) (re);
}
diff --git a/src/include/regex/regcustom.h b/src/include/regex/regcustom.h
index 8c4d5d8cc7c..6b78e6a91fb 100644
--- a/src/include/regex/regcustom.h
+++ b/src/include/regex/regcustom.h
@@ -57,6 +57,7 @@
#define INTERRUPT(re) CHECK_FOR_INTERRUPTS()
#undef assert
#define assert(x) Assert(x)
+#define VS(x) (x)
/* internal character type and related */
typedef pg_wchar chr; /* the type itself */
diff --git a/src/include/regex/regex.h b/src/include/regex/regex.h
index f34ee3a85bd..30c2de11664 100644
--- a/src/include/regex/regex.h
+++ b/src/include/regex/regex.h
@@ -156,7 +156,7 @@ typedef struct
Oid re_collation; /* Collation that defines
LC_CTYPE behavior */
/* the rest is opaque pointers to hidden innards */
char *re_guts; /* `char *' is more portable than `void
*' */
- char *re_fns;
+ const void *re_fns;
} pg_regex_t;
/* result reporting (may acquire more fields later) */
diff --git a/src/include/regex/regguts.h b/src/include/regex/regguts.h
index 6d618ec69fb..bcd8676660d 100644
--- a/src/include/regex/regguts.h
+++ b/src/include/regex/regguts.h
@@ -534,7 +534,7 @@ struct fns
};
#define STACK_TOO_DEEP(re) \
- ((*((struct fns *) (re)->re_fns)->stack_too_deep) ())
+ ((*((const struct fns *) (re)->re_fns)->stack_too_deep) ())
/*
--
2.56.0
From aabf0fa09656bedabe14a4504950465a4fe13240 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Tue, 3 Nov 2026 15:39:36 +0100
Subject: [PATCH 06/10] Fix -Wcast-qual warning in guc_funcs.c
show_all_settings() passed its char *values[] array to
GetConfigOptionValues() by casting it to const char **, which triggers
-Wcast-qual warnings. This was harmless in practice, because the only
consumer, BuildTupleFromCStrings(), does not modify the strings.
Fix by declaring the array as const char *values[], which is what
GetConfigOptionValues() stores into it, and casting away the const
with unconstify() when passing it to BuildTupleFromCStrings(). This
keeps the const removal explicit and type-checked, and it compiles to
the same code as before.
---
src/backend/utils/misc/guc_funcs.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/src/backend/utils/misc/guc_funcs.c
b/src/backend/utils/misc/guc_funcs.c
index 0e75c903003..e27050575fb 100644
--- a/src/backend/utils/misc/guc_funcs.c
+++ b/src/backend/utils/misc/guc_funcs.c
@@ -978,7 +978,7 @@ show_all_settings(PG_FUNCTION_ARGS)
while (call_cntr < max_calls) /* do when there is more left to send */
{
struct config_generic *conf = guc_vars[call_cntr];
- char *values[NUM_PG_SETTINGS_ATTS];
+ const char *values[NUM_PG_SETTINGS_ATTS];
HeapTuple tuple;
Datum result;
@@ -991,10 +991,15 @@ show_all_settings(PG_FUNCTION_ARGS)
}
/* extract values for the current variable */
- GetConfigOptionValues(conf, (const char **) values);
+ GetConfigOptionValues(conf, values);
- /* build a tuple */
- tuple = BuildTupleFromCStrings(attinmeta, values);
+ /*
+ * build a tuple
+ *
+ * (BuildTupleFromCStrings() takes char ** but does not modify
the
+ * strings, so it's safe to cast away const here.)
+ */
+ tuple = BuildTupleFromCStrings(attinmeta, unconstify(char **,
values));
/* make the tuple into a datum */
result = HeapTupleGetDatum(tuple);
--
2.56.0
From 5b021f5243c872327598604165e057ea99712deb Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
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<underlying_type>(expr)
+#define unconstify_pointee(pointee_type, expr) const_cast<pointee_type *>(expr)
#define unconstify_constexpr(underlying_type, expr)
const_cast<underlying_type>(expr)
#define unvolatize(underlying_type, expr) const_cast<underlying_type>(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
From 1eac365ea644f3374c6e7ea5ccb46abceb47e80e Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Tue, 3 Nov 2026 15:39:36 +0100
Subject: [PATCH 08/10] Fix -Wcast-qual warnings in read/write accessor macros
Some macros compute pointers into structs and are used for both
reading and writing that struct. For example, ARR_DIMS() can be used
for both reading:
if (ARR_DIMS(arr)[0] != mcv_length)
and writing:
ARR_DIMS(r)[0] = num;
This makes it difficult to qualify the internal casts of those macros:
If we const-qualify the casts, then the writing case will complain
about writing through a const pointer. If we don't qualify the casts,
then read uses of the macro on a const object will complain with
-Wcast-qual.
To make this work under -Wcast-qual for now, we const-qualify the
casts and then remove the qualifier with unconstify(). Note that this
doesn't add or remove any const-safety.
Alternative solutions are writing separate write macros (e.g.,
ARR_SET_DIMS()) or writing type-generic macros (with _Generic, but
that would need a separate C++ solution for exported interfaces).
This could be future work.
---
contrib/ltree/ltree.h | 4 ++--
src/backend/utils/adt/multirangetypes.c | 6 +++---
src/include/tsearch/ts_type.h | 2 +-
src/include/utils/array.h | 8 ++++----
src/include/utils/varbit.h | 2 +-
src/include/varatt.h | 8 ++++----
6 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/contrib/ltree/ltree.h b/contrib/ltree/ltree.h
index 89c5b932292..0b45bf04c63 100644
--- a/contrib/ltree/ltree.h
+++ b/contrib/ltree/ltree.h
@@ -48,7 +48,7 @@ typedef struct
} ltree;
#define LTREE_HDRSIZE MAXALIGN( offsetof(ltree, data) )
-#define LTREE_FIRST(x) ( (ltree_level*)( ((char*)(x))+LTREE_HDRSIZE ) )
+#define LTREE_FIRST(x) ( (ltree_level*)( unconstify(char *, (const
char*)(x))+LTREE_HDRSIZE ) )
#define LTREE_MAX_LEVELS PG_UINT16_MAX /* ltree.numlevel is uint16 */
@@ -121,7 +121,7 @@ typedef struct
} lquery;
#define LQUERY_HDRSIZE MAXALIGN( offsetof(lquery, data) )
-#define LQUERY_FIRST(x) ( (lquery_level*)( ((char*)(x))+LQUERY_HDRSIZE ) )
+#define LQUERY_FIRST(x) ( (lquery_level*)( unconstify(char *, (const
char*)(x))+LQUERY_HDRSIZE ) )
#define LQUERY_MAX_LEVELS PG_UINT16_MAX /* lquery.numlevel is uint16 */
#define LQUERY_HASNOT 0x01
diff --git a/src/backend/utils/adt/multirangetypes.c
b/src/backend/utils/adt/multirangetypes.c
index 14a71fe534f..40f9b55abb9 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -68,11 +68,11 @@ typedef enum
* Macros for accessing past MultirangeType parts of multirange: items, flags
* and boundaries.
*/
-#define MultirangeGetItemsPtr(mr) ((uint32 *) ((char *) (mr) + \
+#define MultirangeGetItemsPtr(mr) ((uint32 *) (unconstify(char *, (const char
*) (mr)) + \
sizeof(MultirangeType)))
-#define MultirangeGetFlagsPtr(mr) ((uint8 *) ((char *) (mr) + \
+#define MultirangeGetFlagsPtr(mr) ((uint8 *) (unconstify(char *, (const char
*) (mr)) + \
sizeof(MultirangeType) + ((mr)->rangeCount - 1) * sizeof(uint32)))
-#define MultirangeGetBoundariesPtr(mr, align) ((char *) (mr) + \
+#define MultirangeGetBoundariesPtr(mr, align) (unconstify(char *, (const char
*) (mr)) + \
att_align_nominal(sizeof(MultirangeType) + \
((mr)->rangeCount - 1) * sizeof(uint32) + \
(mr)->rangeCount * sizeof(uint8), (align)))
diff --git a/src/include/tsearch/ts_type.h b/src/include/tsearch/ts_type.h
index 345a2dab930..9002e43b12b 100644
--- a/src/include/tsearch/ts_type.h
+++ b/src/include/tsearch/ts_type.h
@@ -117,7 +117,7 @@ typedef TSVectorData *TSVector;
#define ARRPTR(tsv) ( (tsv)->entries )
/* pointer to start of a tsvector's lexeme storage */
-#define STRPTR(tsv) ( (char *) &(tsv)->entries[(tsv)->size] )
+#define STRPTR(tsv) ( unconstify(char *, (const char *)
&(tsv)->entries[(tsv)->size]) )
/* pointer to WordEntryPosVector for a WordEntry */
#define _POSVECPTR(tsv,we) ((WordEntryPosVector *) \
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index e7ee184034e..45040bba72d 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -292,14 +292,14 @@ typedef struct ArrayIteratorData *ArrayIterator;
#define ARR_ELEMTYPE(a) ((a)->elemtype)
#define ARR_DIMS(a) \
- ((int *) (((char *) (a)) + sizeof(ArrayType)))
+ ((int *) ((unconstify(char *, (const char *) (a))) +
sizeof(ArrayType)))
#define ARR_LBOUND(a) \
- ((int *) (((char *) (a)) + sizeof(ArrayType) + \
+ ((int *) ((unconstify(char *, (const char *) (a))) +
sizeof(ArrayType) + \
sizeof(int) * ARR_NDIM(a)))
#define ARR_NULLBITMAP(a) \
(ARR_HASNULL(a) ? \
- (uint8 *) (((char *) (a)) + sizeof(ArrayType) + \
+ (uint8 *) ((unconstify(char *, (const char *) (a))) +
sizeof(ArrayType) + \
2 * sizeof(int) * ARR_NDIM(a)) \
: (uint8 *) NULL)
@@ -320,7 +320,7 @@ typedef struct ArrayIteratorData *ArrayIterator;
* Returns a pointer to the actual array data.
*/
#define ARR_DATA_PTR(a) \
- (((char *) (a)) + ARR_DATA_OFFSET(a))
+ ((unconstify(char *, (const char *) (a))) + ARR_DATA_OFFSET(a))
/*
* Macros for working with AnyArrayType inputs. Beware multiple references!
diff --git a/src/include/utils/varbit.h b/src/include/utils/varbit.h
index 8e2b981e1f6..f9f848ff969 100644
--- a/src/include/utils/varbit.h
+++ b/src/include/utils/varbit.h
@@ -68,7 +68,7 @@ VarBitPGetDatum(const VarBit *X)
/* Number of bits in this bit string */
#define VARBITLEN(PTR) (((VarBit *) (PTR))->bit_len)
/* Pointer to the first byte containing bit string data */
-#define VARBITS(PTR) (((VarBit *) (PTR))->bit_dat)
+#define VARBITS(PTR) ((unconstify(VarBit *, (const VarBit *)
(PTR)))->bit_dat)
/* Number of bytes in the data section of a bit string */
#define VARBITBYTES(PTR) (VARSIZE(PTR) - VARHDRSZ - VARBITHDRSZ)
/* Padding of the bit string at the end (in bits) */
diff --git a/src/include/varatt.h b/src/include/varatt.h
index 7951a946a87..f288ce30414 100644
--- a/src/include/varatt.h
+++ b/src/include/varatt.h
@@ -304,10 +304,10 @@ typedef struct
#endif /* WORDS_BIGENDIAN */
-#define VARDATA_4B(PTR) (((varattrib_4b *)
(PTR))->va_4byte.va_data)
-#define VARDATA_4B_C(PTR) (((varattrib_4b *)
(PTR))->va_compressed.va_data)
-#define VARDATA_1B(PTR) (((varattrib_1b *) (PTR))->va_data)
-#define VARDATA_1B_E(PTR) (((varattrib_1b_e *) (PTR))->va_data)
+#define VARDATA_4B(PTR) (unconstify(varattrib_4b *, (const
varattrib_4b *) (PTR))->va_4byte.va_data)
+#define VARDATA_4B_C(PTR) (unconstify(varattrib_4b *, (const varattrib_4b
*) (PTR))->va_compressed.va_data)
+#define VARDATA_1B(PTR) (unconstify(varattrib_1b *, (const
varattrib_1b *) (PTR))->va_data)
+#define VARDATA_1B_E(PTR) (unconstify(varattrib_1b_e *, (const
varattrib_1b_e *) (PTR))->va_data)
/*
* Externally visible TOAST functions and macros begin here. All of these
--
2.56.0
From 96abbe2a162587228b5b41b64e9576ffac000633 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Tue, 3 Nov 2026 15:39:36 +0100
Subject: [PATCH 09/10] Adjust unconstify/unvolatize to avoid -Wcast-qual
warnings
An intermediate cast to uintptr_t avoids the warning. That way, we
could turn on -Wcast-qual, and all unconstify() and unvolatize() uses
would pass but bare casts that discard qualifiers would raise
warnings.
Reviewed-by: Rui Zhao <[email protected]>
Discussion:
https://www.postgresql.org/message-id/c76b72dd-5736-453a-b698-6968d42e4768%40eisentraut.org
---
src/include/c.h | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/include/c.h b/src/include/c.h
index 79b82226551..d4699a4a271 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -1453,22 +1453,22 @@ typedef struct PGAlignedXLogBlock PGAlignedXLogBlock;
#else /* !__cplusplus */
#define unconstify(underlying_type, expr) \
(StaticAssertVariableIsOfTypeMacro(expr, const underlying_type), \
- (underlying_type) (expr))
+ (underlying_type) (uintptr_t) (expr))
#define unconstify_pointee(pointee_type, expr) \
(StaticAssertVariableIsOfTypeMacro(expr, pointee_type const *), \
- (pointee_type *) (expr))
+ (pointee_type *) (uintptr_t) (expr))
#define unvolatize(underlying_type, expr) \
(StaticAssertVariableIsOfTypeMacro(expr, volatile underlying_type), \
- (underlying_type) (expr))
+ (underlying_type) (uintptr_t) (expr))
#ifdef __GNUC__
#define unconstify_constexpr(underlying_type, expr) \
__builtin_choose_expr( \
_Generic((expr), const underlying_type: 1, default: 0), \
- (underlying_type) (expr), \
+ (underlying_type) (uintptr_t) (expr), \
(void) 0)
#else /* !__GNUC_ */
#define unconstify_constexpr(underlying_type, expr) \
- ((underlying_type) (expr))
+ ((underlying_type) (uintptr_t) (expr))
#endif /* !__GNUC_ */
#endif /* !__cplusplus */
--
2.56.0
From 69c22cee4800a6ae49f595fa4db0350a95be5d15 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Tue, 3 Nov 2026 15:39:36 +0100
Subject: [PATCH 10/10] Add warning option -Wcast-qual
All the warnings have been fixed by previous patches, so this can be
activated by default now.
---
configure | 4 ++--
configure.ac | 4 ++--
meson.build | 1 +
3 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/configure b/configure
index 6b05d4c84ab..4e62d8e67b8 100755
--- a/configure
+++ b/configure
@@ -5250,8 +5250,8 @@ fi
# but has its own. Also check other compiler-specific flags here.
if test "$GCC" = yes -a "$ICC" = no; then
- CFLAGS="-Wall -Wmissing-prototypes -Wpointer-arith"
- CXXFLAGS="-Wall -Wpointer-arith"
+ CFLAGS="-Wall -Wcast-qual -Wmissing-prototypes -Wpointer-arith"
+ CXXFLAGS="-Wall -Wcast-qual -Wpointer-arith"
# These work in some but not all gcc versions
save_CFLAGS=$CFLAGS
diff --git a/configure.ac b/configure.ac
index 6864268eaa0..8c9618ac953 100644
--- a/configure.ac
+++ b/configure.ac
@@ -535,8 +535,8 @@ fi
# but has its own. Also check other compiler-specific flags here.
if test "$GCC" = yes -a "$ICC" = no; then
- CFLAGS="-Wall -Wmissing-prototypes -Wpointer-arith"
- CXXFLAGS="-Wall -Wpointer-arith"
+ CFLAGS="-Wall -Wcast-qual -Wmissing-prototypes -Wpointer-arith"
+ CXXFLAGS="-Wall -Wcast-qual -Wpointer-arith"
# These work in some but not all gcc versions
save_CFLAGS=$CFLAGS
PGAC_PROG_CC_CFLAGS_OPT([-Wdeclaration-after-statement])
diff --git a/meson.build b/meson.build
index 3595c847d11..1e60820d77c 100644
--- a/meson.build
+++ b/meson.build
@@ -2202,6 +2202,7 @@ vectorize_cflags =
cc.get_supported_arguments(['-ftree-vectorize'])
unroll_loops_cflags = cc.get_supported_arguments(['-funroll-loops'])
common_warning_flags = [
+ '-Wcast-qual',
'-Wpointer-arith',
# Really don't want VLAs to be used in our dialect of C
'-Werror=vla',
--
2.56.0