On Fri Sep 25, 2026 at 4:02 PM UTC, Japin Li wrote:
>
> Hi
> Hi Tristan,
>
> On Thu, 24 Sep 2026 at 16:17, "Tristan Partin" <[email protected]> wrote:
>> On Wed Sep 23, 2026 at 6:33 AM CDT, Japin Li wrote:
>>>
>>> Hi, Tristan
>>>
>>> Thanks for updating the patches.
>>>
>>> On Wed, 23 Sep 2026 at 07:26, "Tristan Partin" <[email protected]> wrote:
>>>> On Tue Sep 22, 2026 at 5:28 AM UTC, Peter Eisentraut wrote:
>>>>> On 30.07.26 00:07, Tristan Partin wrote:
>>>>>> The counted_by[0] compiler attribute is fairly new. It was added in GCC
>>>>>> 15 and Clang 18. It has been used fairly extensively in the Linux
>>>>>> kernel[0].
>>>>>> 
>>>>>> To summarize the benefits of the attribute:
>>>>>> 
>>>>>> - Runtime bounds checking with -DFORTIFY_SOURCE=3 and -fsanitize-bounds
>>>>>> - Accurate reporting of __builtin_dynamic_object_size()
>>>>>> 
>>>>>> While we don't use __builtin_dynamic_object_size(), I think the runtime
>>>>>> bounds checking improvements are easily worth the little bit of effort
>>>>>> to add the attribute in various locations and review the code. I think
>>>>>> it will improve things for buildfarm animals using ASan due to expanded
>>>>>> coverage.
>>>>>
>>>>> I took a closer look at this.  There are several problems with the 
>>>>> proposed patches.
>>>>>
>>>>> 1) In C++, both gcc and clang have __has_attribute(counted_by) return 1 
>>>>> (true), but the compiler actually rejects the attribute with a warning. 
>>>>> This is not immediately evident in your patch, but it would show up 
>>>>> under cpluspluscheck and whenever we extend this attribute to header 
>>>>> files that happen to get pulled in by C++ source files. 
>>>>> (access/tupdesc.h is an obvious candidate.) Therefore, there needs to be 
>>>>> some #ifndef __cplusplus somewhere.
>>>>
>>>> I would love to understand the rationale for returning 1 when the 
>>>> compiler will just throw a warning anyway. Fixed.
>>>>
>>>>> 2) gcc 15 and clang 18 accept the counted_by attribute only for flexible 
>>>>> array members, not for pointers members. (Using it on a pointer causes 
>>>>> an error.) If you want to apply this to pointer members, as your patch 
>>>>> does in buffile.c, you'd have to write a configure test. Or else 
>>>>> restrict it to flexible array members for now.
>>>>
>>>> I like the idea of restricting it to flexible array members for now. 
>>>> It'll make for an easier review. Maybe in a subsequent patch we can 
>>>> raise the minimum compiler versions of using pg_attribute_counted_by() 
>>>> to GCC 16 and Clang 21.
>>>>
>>>>> 3) The counted_by attribute requires that, when extending the counted 
>>>>> array, the count field is increased before writing into the new element 
>>>>> at the end. The code dealing with struct BufFile currently doesn't do 
>>>>> that, and so your change in buffile.c fails under -fsanitize=bounds:
>>>>>
>>>>> ../src/backend/storage/file/buffile.c:919:3: runtime error: index 1 out 
>>>>> of bounds for type 'File * __counted_by(numFiles)' (aka 'int *')
>>>>>
>>>>> (Reproduce with meson configure -Db_sanitize=bounds and meson test ... 
>>>>> --suite regress.)
>>>>>
>>>>> The code needs to be carefully analyzed and adjusted to fix this. (The 
>>>>> code for the tuplesort.c change appears to be ok.)
>>>>
>>>> Good catch. In the upcoming changes, I ran test suites with 
>>>> -fsanitize=bounds, and found one place that needed a fix. Note that 
>>>> changes to buffile.c are not currently in scope for this patchset since 
>>>> it wasn't a flexible array member.
>>>>
>>>>> 4) Although the compilers are flexible with the placement, the most 
>>>>> correct placement of the attribute is at the beginning of the 
>>>>> declaration, like
>>>>>
>>>>> pg_attribute_counted_by(nTapes) TapeShare tapes[FLEXIBLE_ARRAY_MEMBER];
>>>>>
>>>>> (Note that the gcc documentation effectively writes it this way.)
>>>>
>>>> The second patch uses postfix notation, but subsequent patches enable 
>>>> support for prefix notation. I'll let you be the judge of whether to 
>>>> commit prefix or postfix. Commits 3 & 4 are genuine improvements, though 
>>>> they do also enable prefix support.
>>>>
>>>>> Additionally, with this arrangement, we could also make use of the MSVC 
>>>>> _Field_size_ annotation.
>>>>
>>>> This is good motivation.
>>>>
>>>>> 5) Minor: The counted_by attribute only takes a single argument, so the 
>>>>> use of __VA_ARGS__ seems excessive.
>>>>
>>>> I think I just blindly copied surrounding macro code and forgot to 
>>>> change it. Fixed in this new version.
>>>>
>>>>> 6) Minor: Awkward wording in comment: "This provides the compiler to 
>>>>> improve ..." -> "enables the compiler ..."?
>>>>
>>>> Fixed.
>>>>
>>>>> Suggestion:
>>>>> - Add C++ guard. (Maybe add annotation in access/tupdesc.h to test.)
>>>>> - Skip use of the attribute on pointer members for now.
>>>>> - Make sure cpluspluscheck and -fsanitize=bounds pass.
>>>>> - Consider the cosmetic adjustments mentioned.
>>>>
>>>> Thanks for the review.
>>>>
>>>
>>> I tested it locally, and all tests passed.
>>>
>>> I found some places that could use the new pg_attribute_counted_by() 
>>> attribute
>>> e.g., in heapam_xlog.h, xact.h, etc. Are those intentional omissions?
>>>
>>> Was this an oversight, or is pg_attribute_counted_by not needed here?
>>>
>>> Haven't checked everywhere yet.  If it is oversight, I'll check it later.
>>
>> I originally had these as well, but my understanding is that these 
>> structures are populated from the filesystem, so in the event of data 
>> corruption, the count field may no longer be accurate and could cause 
>> a runtime failure similar to what Peter mentioned above in his review. 
>> It's possible that I am being too cautious though. Let me know what you 
>> think. I should have brought this up in my last email, but now is as 
>> good a time as any to discuss!
>
> Thanks for the clear explanation.
>
> I agree with you on the cautious approach — especially for structures that
> may be populated from the filesystem, or where the count field does not
> strictly represent the allocated/accessible length of the flexible array
> member.
>
> While reviewing remaining FAMs that do not yet use pg_attribute_counted_by(),
> two cases looked like solid candidates:
>
> 1. BackgroundWorkerArray in bgworker.c
>
>     typedef struct BackgroundWorkerArray
>     {
>         int         total_slots;
>         uint32      parallel_register_count;
>         uint32      parallel_terminate_count;
>         BackgroundWorkerSlot slot[FLEXIBLE_ARRAY_MEMBER];
>     } BackgroundWorkerArray;
>
> The total_slots is set once at shared-memory initialization (based on
> max_worker_processes) and never changes afterwards.  All accesses are of the
> form slot[i] with i < total_slots.  The invariant is therefore trivial to
> maintain.
>
> 2. vbits in contrib/pg_visibility/pg_visibility.c
>
>     typedef struct vbits
>     {
>         BlockNumber next;
>         BlockNumber count;
>         uint8       bits[FLEXIBLE_ARRAY_MEMBER];
>     } vbits;
>
> The structure is allocated and filled in one place (collect_visibility_data),
> with count equal to the number of blocks.  Subsequent use is strictly 
> read-only
> and always checks next < count before indexing into bits[].
>
> Both are purely internal, the count field matches the actual array bound, and
> the update order is safe.  I think it would be worthwhile to annotate them.
>
> Thoughts?

Great catches. I have included those in the next version of commits.

-- 
Tristan Partin
PostgreSQL Contributors Team
AWS (https://aws.amazon.com)
From 3a72ff8df2558484a2f2760f6d22052c11125ac9 Mon Sep 17 00:00:00 2001
From: Tristan Partin <[email protected]>
Date: Wed, 23 Sep 2026 02:59:33 +0000
Subject: [PATCH v3 1/7] Add pg_attribute_counted_by() to various flexible
 array members

Annotate flexible array members whose element count is held by another
member of the same struct, so that the compiler knows the real extent of
these arrays.

Signed-off-by: Tristan Partin <[email protected]>
---
 contrib/hstore/hstore_io.c                     |  2 +-
 contrib/pageinspect/brinfuncs.c                |  2 +-
 contrib/pg_visibility/pg_visibility.c          |  2 +-
 src/backend/access/brin/brin_minmax_multi.c    |  2 +-
 src/backend/access/nbtree/nbtutils.c           |  2 +-
 src/backend/access/transam/multixact.c         |  2 +-
 src/backend/access/transam/xact.c              |  2 +-
 src/backend/access/transam/xlogprefetcher.c    |  2 +-
 src/backend/catalog/index.c                    |  2 +-
 src/backend/commands/tablespace.c              |  2 +-
 src/backend/commands/trigger.c                 |  2 +-
 src/backend/executor/execParallel.c            |  2 +-
 src/backend/nodes/bitmapset.c                  |  4 ++--
 src/backend/postmaster/bgworker.c              |  2 +-
 src/backend/postmaster/checkpointer.c          |  2 +-
 src/backend/postmaster/launch_backend.c        |  2 +-
 .../replication/logical/reorderbuffer.c        |  2 +-
 src/backend/storage/buffer/freelist.c          |  2 +-
 src/backend/storage/ipc/dsm.c                  |  2 +-
 src/backend/storage/ipc/pmsignal.c             |  2 +-
 src/backend/utils/adt/rowtypes.c               |  4 ++--
 src/backend/utils/adt/tsvector_op.c            |  2 +-
 src/backend/utils/cache/typcache.c             |  2 +-
 src/backend/utils/sort/sharedtuplestore.c      |  2 +-
 src/backend/utils/sort/tuplesort.c             |  2 +-
 src/bin/pg_dump/pg_dump.h                      |  2 +-
 src/bin/pg_rewind/filemap.h                    |  2 +-
 src/bin/pgbench/pgbench.c                      |  2 +-
 src/include/access/brin_internal.h             |  2 +-
 src/include/access/tupdesc.h                   |  2 +-
 src/include/executor/execPartition.h           |  4 ++--
 src/include/executor/instrument.h              |  2 +-
 src/include/executor/instrument_node.h         | 18 +++++++++---------
 src/include/fe_utils/parallel_slot.h           |  2 +-
 src/include/jit/jit.h                          |  2 +-
 src/include/nodes/bitmapset.h                  |  2 +-
 src/include/regex/regguts.h                    |  4 ++--
 .../statistics/extended_stats_internal.h       |  2 +-
 src/include/statistics/statistics.h            |  8 ++++----
 src/include/tsearch/dicts/spell.h              |  2 +-
 src/include/utils/catcache.h                   |  2 +-
 src/include/utils/datetime.h                   |  2 +-
 src/pl/plpython/plpy_procedure.h               |  2 +-
 43 files changed, 58 insertions(+), 58 deletions(-)

diff --git a/contrib/hstore/hstore_io.c b/contrib/hstore/hstore_io.c
index 785aa308cde..8e047eed4fc 100644
--- a/contrib/hstore/hstore_io.c
+++ b/contrib/hstore/hstore_io.c
@@ -843,7 +843,7 @@ typedef struct RecordIOData
 	/* this field is used only if target type is domain over composite: */
 	void	   *domain_info;	/* opaque cache for domain checks */
 	int			ncolumns;
-	ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER];
+	ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(ncolumns);
 } RecordIOData;
 
 PG_FUNCTION_INFO_V1(hstore_from_record);
diff --git a/contrib/pageinspect/brinfuncs.c b/contrib/pageinspect/brinfuncs.c
index c64124b5b02..0b3df183887 100644
--- a/contrib/pageinspect/brinfuncs.c
+++ b/contrib/pageinspect/brinfuncs.c
@@ -34,7 +34,7 @@ PG_FUNCTION_INFO_V1(brin_revmap_data);
 typedef struct brin_column_state
 {
 	int			nstored;
-	FmgrInfo	outputFn[FLEXIBLE_ARRAY_MEMBER];
+	FmgrInfo	outputFn[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nstored);
 } brin_column_state;
 
 
diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c
index 4292d077be1..ec95b85e624 100644
--- a/contrib/pg_visibility/pg_visibility.c
+++ b/contrib/pg_visibility/pg_visibility.c
@@ -34,7 +34,7 @@ typedef struct vbits
 {
 	BlockNumber next;
 	BlockNumber count;
-	uint8		bits[FLEXIBLE_ARRAY_MEMBER];
+	uint8		bits[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(count);
 } vbits;
 
 typedef struct corrupt_items
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 1a6056a6b47..386f8fc760a 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -190,7 +190,7 @@ typedef struct Ranges
 	int			target_maxvalues;
 
 	/* values stored for this range - either raw values, or ranges */
-	Datum		values[FLEXIBLE_ARRAY_MEMBER];
+	Datum		values[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(maxvalues);
 } Ranges;
 
 /*
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 014faa1622f..ef81ec50482 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -413,7 +413,7 @@ typedef struct BTVacInfo
 	BTCycleId	cycle_ctr;		/* cycle ID most recently assigned */
 	int			num_vacuums;	/* number of currently active VACUUMs */
 	int			max_vacuums;	/* allocated length of vacuums[] array */
-	BTOneVacInfo vacuums[FLEXIBLE_ARRAY_MEMBER];
+	BTOneVacInfo vacuums[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(max_vacuums);
 } BTVacInfo;
 
 static BTVacInfo *btvacinfo;
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 3caedb54850..2ae1857a7a0 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -293,7 +293,7 @@ typedef struct mXactCacheEnt
 	MultiXactId multi;
 	int			nmembers;
 	dlist_node	node;
-	MultiXactMember members[FLEXIBLE_ARRAY_MEMBER];
+	MultiXactMember members[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nmembers);
 } mXactCacheEnt;
 
 #define MAX_CACHE_ENTRIES	256
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 7b67db514ec..a8e063933e5 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -235,7 +235,7 @@ typedef struct SerializedTransactionState
 	FullTransactionId currentFullTransactionId;
 	CommandId	currentCommandId;
 	int			nParallelCurrentXids;
-	TransactionId parallelCurrentXids[FLEXIBLE_ARRAY_MEMBER];
+	TransactionId parallelCurrentXids[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nParallelCurrentXids);
 } SerializedTransactionState;
 
 /* The size of SerializedTransactionState, not including the final array. */
diff --git a/src/backend/access/transam/xlogprefetcher.c b/src/backend/access/transam/xlogprefetcher.c
index dff57642ab4..011f0a98e74 100644
--- a/src/backend/access/transam/xlogprefetcher.c
+++ b/src/backend/access/transam/xlogprefetcher.c
@@ -117,7 +117,7 @@ typedef struct LsnReadQueue
 	{
 		bool		io;
 		XLogRecPtr	lsn;
-	}			queue[FLEXIBLE_ARRAY_MEMBER];
+	}			queue[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(size);
 } LsnReadQueue;
 
 /*
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 4d232b85ad5..af3318184ad 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -96,7 +96,7 @@ typedef struct
 	Oid			currentlyReindexedHeap;
 	Oid			currentlyReindexedIndex;
 	int			numPendingReindexedIndexes;
-	Oid			pendingReindexedIndexes[FLEXIBLE_ARRAY_MEMBER];
+	Oid			pendingReindexedIndexes[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(numPendingReindexedIndexes);
 } SerializedReindexState;
 
 /* non-export function prototypes */
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index e01fb2db913..8c5f1a5a1e4 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -1216,7 +1216,7 @@ typedef struct
 {
 	/* Array of OIDs to be passed to SetTempTablespaces() */
 	int			numSpcs;
-	Oid			tblSpcs[FLEXIBLE_ARRAY_MEMBER];
+	Oid			tblSpcs[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(numSpcs);
 } temp_tablespaces_extra;
 
 /* check_hook: validate new temp_tablespaces */
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 1d10fb1c13c..91d451d229e 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -3669,7 +3669,7 @@ typedef struct SetConstraintStateData
 	bool		all_isdeferred;
 	int			numstates;		/* number of trigstates[] entries in use */
 	int			numalloc;		/* allocated size of trigstates[] */
-	SetConstraintTriggerData trigstates[FLEXIBLE_ARRAY_MEMBER];
+	SetConstraintTriggerData trigstates[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(numalloc);
 } SetConstraintStateData;
 
 typedef SetConstraintStateData *SetConstraintState;
diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c
index 6b85508a696..9d495673a73 100644
--- a/src/backend/executor/execParallel.c
+++ b/src/backend/executor/execParallel.c
@@ -103,7 +103,7 @@ struct SharedExecutorInstrumentation
 	int			instrument_offset;
 	int			num_workers;
 	int			num_plan_nodes;
-	int			plan_node_id[FLEXIBLE_ARRAY_MEMBER];
+	int			plan_node_id[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_plan_nodes);
 
 	/*
 	 * Array of num_plan_nodes * num_workers NodeInstrumentation objects
diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index 4dd97e55842..a13a96799d5 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -1106,14 +1106,14 @@ bms_replace_members(Bitmapset *a, const Bitmapset *b)
 	if (a->nwords < b->nwords)
 		a = (Bitmapset *) repalloc(a, BITMAPSET_SIZE(b->nwords));
 
+	a->nwords = b->nwords;
+
 	i = 0;
 	do
 	{
 		a->words[i] = b->words[i];
 	} while (++i < b->nwords);
 
-	a->nwords = b->nwords;
-
 #ifdef REALLOCATE_BITMAPSETS
 
 	/*
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 3da2a90417f..74ac98dae23 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -101,7 +101,7 @@ typedef struct BackgroundWorkerArray
 	int			total_slots;
 	uint32		parallel_register_count;
 	uint32		parallel_terminate_count;
-	BackgroundWorkerSlot slot[FLEXIBLE_ARRAY_MEMBER];
+	BackgroundWorkerSlot slot[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(total_slots);
 } BackgroundWorkerArray;
 
 struct BackgroundWorkerHandle
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 580c7944119..1d1431b7bb7 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -140,7 +140,7 @@ typedef struct
 								 * buffer */
 
 	/* The ring buffer of pending checkpointer requests */
-	CheckpointerRequest requests[FLEXIBLE_ARRAY_MEMBER];
+	CheckpointerRequest requests[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(max_requests);
 } CheckpointerShmemStruct;
 
 static CheckpointerShmemStruct *CheckpointerShmem;
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 8f3cfea880c..8bb77e7880b 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -145,7 +145,7 @@ typedef struct
 	 * Extra startup data, content depends on the child process.
 	 */
 	size_t		startup_data_len;
-	char		startup_data[FLEXIBLE_ARRAY_MEMBER];
+	char		startup_data[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(startup_data_len);
 } BackendParameters;
 
 #define SizeOfBackendParameters(startup_data_len) (offsetof(BackendParameters, startup_data) + startup_data_len)
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index 94112c30501..9d17594f901 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -171,7 +171,7 @@ typedef struct ReorderBufferIterTXNState
 	binaryheap *heap;
 	Size		nr_txns;
 	dlist_head	old_change;
-	ReorderBufferIterTXNEntry entries[FLEXIBLE_ARRAY_MEMBER];
+	ReorderBufferIterTXNEntry entries[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nr_txns);
 } ReorderBufferIterTXNState;
 
 /* toast datastructures */
diff --git a/src/backend/storage/buffer/freelist.c b/src/backend/storage/buffer/freelist.c
index fdb5bad7910..6a3c002cfc0 100644
--- a/src/backend/storage/buffer/freelist.c
+++ b/src/backend/storage/buffer/freelist.c
@@ -90,7 +90,7 @@ typedef struct BufferAccessStrategyData
 	 * simplicity this is palloc'd together with the fixed fields of the
 	 * struct.
 	 */
-	Buffer		buffers[FLEXIBLE_ARRAY_MEMBER];
+	Buffer		buffers[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nbuffers);
 }			BufferAccessStrategyData;
 
 
diff --git a/src/backend/storage/ipc/dsm.c b/src/backend/storage/ipc/dsm.c
index 8b69df4ff26..1c99e600765 100644
--- a/src/backend/storage/ipc/dsm.c
+++ b/src/backend/storage/ipc/dsm.c
@@ -93,7 +93,7 @@ typedef struct dsm_control_header
 	uint32		magic;
 	uint32		nitems;
 	uint32		maxitems;
-	dsm_control_item item[FLEXIBLE_ARRAY_MEMBER];
+	dsm_control_item item[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(maxitems);
 } dsm_control_header;
 
 static void dsm_cleanup_for_mmap(void);
diff --git a/src/backend/storage/ipc/pmsignal.c b/src/backend/storage/ipc/pmsignal.c
index bdad5fdd043..a1bb635fb42 100644
--- a/src/backend/storage/ipc/pmsignal.c
+++ b/src/backend/storage/ipc/pmsignal.c
@@ -78,7 +78,7 @@ struct PMSignalData
 	QuitSignalReason sigquit_reason;	/* why SIGQUIT was sent */
 	/* per-child-process flags */
 	int			num_child_flags;	/* # of entries in PMChildFlags[] */
-	sig_atomic_t PMChildFlags[FLEXIBLE_ARRAY_MEMBER];
+	sig_atomic_t PMChildFlags[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_child_flags);
 };
 
 /* PMSignalState pointer is valid in both postmaster and child processes */
diff --git a/src/backend/utils/adt/rowtypes.c b/src/backend/utils/adt/rowtypes.c
index 0da0f10c2a0..ffa60aaaba0 100644
--- a/src/backend/utils/adt/rowtypes.c
+++ b/src/backend/utils/adt/rowtypes.c
@@ -45,7 +45,7 @@ typedef struct RecordIOData
 	Oid			record_type;
 	int32		record_typmod;
 	int			ncolumns;
-	ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER];
+	ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(ncolumns);
 } RecordIOData;
 
 /*
@@ -63,7 +63,7 @@ typedef struct RecordCompareData
 	int32		record1_typmod;
 	Oid			record2_type;
 	int32		record2_typmod;
-	ColumnCompareData columns[FLEXIBLE_ARRAY_MEMBER];
+	ColumnCompareData columns[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(ncolumns);
 } RecordCompareData;
 
 
diff --git a/src/backend/utils/adt/tsvector_op.c b/src/backend/utils/adt/tsvector_op.c
index 0bd32a5619f..53de1d7b455 100644
--- a/src/backend/utils/adt/tsvector_op.c
+++ b/src/backend/utils/adt/tsvector_op.c
@@ -50,7 +50,7 @@ typedef struct StatEntry
 	struct StatEntry *left;
 	struct StatEntry *right;
 	uint32		lenlexeme;
-	char		lexeme[FLEXIBLE_ARRAY_MEMBER];
+	char		lexeme[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(lenlexeme);
 } StatEntry;
 
 #define STATENTRYHDRSZ	(offsetof(StatEntry, lexeme))
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index eca2d73231a..57c47be242a 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -155,7 +155,7 @@ typedef struct TypeCacheEnumData
 	Oid			bitmap_base;	/* OID corresponding to bit 0 of bitmapset */
 	Bitmapset  *sorted_values;	/* Set of OIDs known to be in order */
 	int			num_values;		/* total number of values in enum */
-	EnumItem	enum_values[FLEXIBLE_ARRAY_MEMBER];
+	EnumItem	enum_values[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_values);
 } TypeCacheEnumData;
 
 /*
diff --git a/src/backend/utils/sort/sharedtuplestore.c b/src/backend/utils/sort/sharedtuplestore.c
index 04189f708fa..5c6af636682 100644
--- a/src/backend/utils/sort/sharedtuplestore.c
+++ b/src/backend/utils/sort/sharedtuplestore.c
@@ -64,7 +64,7 @@ struct SharedTuplestore
 	char		name[NAMEDATALEN];	/* A name for this tuplestore. */
 
 	/* Followed by per-participant shared state. */
-	SharedTuplestoreParticipant participants[FLEXIBLE_ARRAY_MEMBER];
+	SharedTuplestoreParticipant participants[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nparticipants);
 };
 
 /* Per-participant state that lives in backend-local memory. */
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index 37c40763ee0..868baaac05e 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -362,7 +362,7 @@ struct Sharedsort
 	 * Tapes array used by workers to report back information needed by the
 	 * leader to concatenate all worker tapes into one for merging
 	 */
-	TapeShare	tapes[FLEXIBLE_ARRAY_MEMBER];
+	TapeShare	tapes[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nTapes);
 };
 
 /*
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 2bbb5d5773b..1888c6f07ba 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -640,7 +640,7 @@ typedef struct _loInfo
 	DumpableAcl dacl;
 	const char *rolname;
 	int			numlos;
-	Oid			looids[FLEXIBLE_ARRAY_MEMBER];
+	Oid			looids[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(numlos);
 } LoInfo;
 
 /*
diff --git a/src/bin/pg_rewind/filemap.h b/src/bin/pg_rewind/filemap.h
index 4c6dd8740d7..c528aa74263 100644
--- a/src/bin/pg_rewind/filemap.h
+++ b/src/bin/pg_rewind/filemap.h
@@ -101,7 +101,7 @@ typedef struct filemap_t
 	uint64		fetch_size;		/* number of bytes that needs to be copied */
 
 	int			nentries;		/* size of 'entries' array */
-	file_entry_t *entries[FLEXIBLE_ARRAY_MEMBER];
+	file_entry_t *entries[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nentries);
 } filemap_t;
 
 /* Functions for populating the filemap */
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index 181f0d892ef..d6318020a64 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -98,7 +98,7 @@ typedef struct socket_set
 {
 	int			maxfds;			/* allocated length of pollfds[] array */
 	int			curfds;			/* number currently in use */
-	struct pollfd pollfds[FLEXIBLE_ARRAY_MEMBER];
+	struct pollfd pollfds[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(maxfds);
 } socket_set;
 
 #endif							/* POLL_USING_PPOLL */
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index e17c7fee511..29fb4b870ae 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -34,7 +34,7 @@ typedef struct BrinOpcInfo
 	void	   *oi_opaque;
 
 	/* Type cache entries of the stored columns */
-	TypeCacheEntry *oi_typcache[FLEXIBLE_ARRAY_MEMBER];
+	TypeCacheEntry *oi_typcache[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(oi_nstored);
 } BrinOpcInfo;
 
 /* the size of a BrinOpcInfo for the given number of columns */
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index d26287271e9..8febd18cc3a 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -158,7 +158,7 @@ typedef struct TupleDescData
 										 * compact_attrs element. */
 	TupleConstr *constr;		/* constraints, or NULL if none */
 	/* compact_attrs[N] is the compact metadata of Attribute Number N+1 */
-	CompactAttribute compact_attrs[FLEXIBLE_ARRAY_MEMBER];
+	CompactAttribute compact_attrs[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(natts);
 }			TupleDescData;
 typedef struct TupleDescData *TupleDesc;
 
diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h
index 82063ec2a16..9c029c9340c 100644
--- a/src/include/executor/execPartition.h
+++ b/src/include/executor/execPartition.h
@@ -84,7 +84,7 @@ typedef struct PartitionedRelPruningData
 typedef struct PartitionPruningData
 {
 	int			num_partrelprunedata;	/* number of array entries */
-	PartitionedRelPruningData partrelprunedata[FLEXIBLE_ARRAY_MEMBER];
+	PartitionedRelPruningData partrelprunedata[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_partrelprunedata);
 } PartitionPruningData;
 
 /*
@@ -127,7 +127,7 @@ typedef struct PartitionPruneState
 	bool		do_initial_prune;
 	bool		do_exec_prune;
 	int			num_partprunedata;
-	PartitionPruningData *partprunedata[FLEXIBLE_ARRAY_MEMBER];
+	PartitionPruningData *partprunedata[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_partprunedata);
 } PartitionPruneState;
 
 extern void ExecDoInitialPruning(EState *estate);
diff --git a/src/include/executor/instrument.h b/src/include/executor/instrument.h
index f093a52aae0..d955095c882 100644
--- a/src/include/executor/instrument.h
+++ b/src/include/executor/instrument.h
@@ -115,7 +115,7 @@ typedef struct NodeInstrumentation
 typedef struct WorkerNodeInstrumentation
 {
 	int			num_workers;	/* # of structures that follow */
-	NodeInstrumentation instrument[FLEXIBLE_ARRAY_MEMBER];
+	NodeInstrumentation instrument[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_workers);
 } WorkerNodeInstrumentation;
 
 typedef struct TriggerInstrumentation
diff --git a/src/include/executor/instrument_node.h b/src/include/executor/instrument_node.h
index 41a7d33f19c..ec690197b04 100644
--- a/src/include/executor/instrument_node.h
+++ b/src/include/executor/instrument_node.h
@@ -44,7 +44,7 @@ typedef struct AggregateInstrumentation
 typedef struct SharedAggInfo
 {
 	int			num_workers;
-	AggregateInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER];
+	AggregateInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_workers);
 } SharedAggInfo;
 
 
@@ -116,7 +116,7 @@ typedef struct IndexScanInstrumentation
 typedef struct SharedIndexScanInstrumentation
 {
 	int			num_workers;
-	IndexScanInstrumentation winstrument[FLEXIBLE_ARRAY_MEMBER];
+	IndexScanInstrumentation winstrument[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_workers);
 } SharedIndexScanInstrumentation;
 
 
@@ -140,7 +140,7 @@ typedef struct BitmapHeapScanInstrumentation
 typedef struct SharedBitmapHeapInstrumentation
 {
 	int			num_workers;
-	BitmapHeapScanInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER];
+	BitmapHeapScanInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_workers);
 } SharedBitmapHeapInstrumentation;
 
 
@@ -169,7 +169,7 @@ typedef struct MemoizeInstrumentation
 typedef struct SharedMemoizeInfo
 {
 	int			num_workers;
-	MemoizeInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER];
+	MemoizeInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_workers);
 } SharedMemoizeInfo;
 
 
@@ -215,7 +215,7 @@ typedef struct TuplesortInstrumentation
 typedef struct SharedSortInfo
 {
 	int			num_workers;
-	TuplesortInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER];
+	TuplesortInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_workers);
 } SharedSortInfo;
 
 
@@ -238,7 +238,7 @@ typedef struct HashInstrumentation
 typedef struct SharedHashInfo
 {
 	int			num_workers;
-	HashInstrumentation hinstrument[FLEXIBLE_ARRAY_MEMBER];
+	HashInstrumentation hinstrument[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_workers);
 } SharedHashInfo;
 
 
@@ -266,7 +266,7 @@ typedef struct IncrementalSortInfo
 typedef struct SharedIncrementalSortInfo
 {
 	int			num_workers;
-	IncrementalSortInfo sinfo[FLEXIBLE_ARRAY_MEMBER];
+	IncrementalSortInfo sinfo[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_workers);
 } SharedIncrementalSortInfo;
 
 
@@ -285,7 +285,7 @@ typedef struct SeqScanInstrumentation
 typedef struct SharedSeqScanInstrumentation
 {
 	int			num_workers;
-	SeqScanInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER];
+	SeqScanInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_workers);
 } SharedSeqScanInstrumentation;
 
 
@@ -303,7 +303,7 @@ typedef struct TidRangeScanInstrumentation
 typedef struct SharedTidRangeScanInstrumentation
 {
 	int			num_workers;
-	TidRangeScanInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER];
+	TidRangeScanInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_workers);
 } SharedTidRangeScanInstrumentation;
 
 #endif							/* INSTRUMENT_NODE_H */
diff --git a/src/include/fe_utils/parallel_slot.h b/src/include/fe_utils/parallel_slot.h
index a6ebe273ce0..24f1fe3942f 100644
--- a/src/include/fe_utils/parallel_slot.h
+++ b/src/include/fe_utils/parallel_slot.h
@@ -40,7 +40,7 @@ typedef struct ParallelSlotArray
 	const char *progname;
 	bool		echo;
 	const char *initcmd;
-	ParallelSlot slots[FLEXIBLE_ARRAY_MEMBER];
+	ParallelSlot slots[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(numslots);
 } ParallelSlotArray;
 
 static inline void
diff --git a/src/include/jit/jit.h b/src/include/jit/jit.h
index e2baa4c2ed0..8ffa60a2001 100644
--- a/src/include/jit/jit.h
+++ b/src/include/jit/jit.h
@@ -51,7 +51,7 @@ typedef struct JitInstrumentation
 typedef struct SharedJitInstrumentation
 {
 	int			num_workers;
-	JitInstrumentation jit_instr[FLEXIBLE_ARRAY_MEMBER];
+	JitInstrumentation jit_instr[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_workers);
 } SharedJitInstrumentation;
 
 typedef struct JitContext
diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h
index 2f0b1f04bb7..350b9bb189e 100644
--- a/src/include/nodes/bitmapset.h
+++ b/src/include/nodes/bitmapset.h
@@ -52,7 +52,7 @@ typedef struct Bitmapset
 
 	NodeTag		type;
 	int			nwords;			/* number of words in array */
-	bitmapword	words[FLEXIBLE_ARRAY_MEMBER];	/* really [nwords] */
+	bitmapword	words[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nwords);
 } Bitmapset;
 
 
diff --git a/src/include/regex/regguts.h b/src/include/regex/regguts.h
index 6fb9551721d..fce0f8981b5 100644
--- a/src/include/regex/regguts.h
+++ b/src/include/regex/regguts.h
@@ -320,7 +320,7 @@ struct arcbatch
 {								/* for bulk allocation of arcs */
 	struct arcbatch *next;		/* chain link */
 	size_t		narcs;			/* number of arcs allocated in this arcbatch */
-	struct arc	a[FLEXIBLE_ARRAY_MEMBER];
+	struct arc	a[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(narcs);
 };
 #define  ARCBATCHSIZE(n)  ((n) * sizeof(struct arc) + offsetof(struct arcbatch, a))
 /* first batch will have FIRSTABSIZE arcs; then double it until MAXABSIZE */
@@ -346,7 +346,7 @@ struct statebatch
 {								/* for bulk allocation of states */
 	struct statebatch *next;	/* chain link */
 	size_t		nstates;		/* number of states allocated in this batch */
-	struct state s[FLEXIBLE_ARRAY_MEMBER];
+	struct state s[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nstates);
 };
 #define  STATEBATCHSIZE(n)  ((n) * sizeof(struct state) + offsetof(struct statebatch, s))
 /* first batch will have FIRSTSBSIZE states; then double it until MAXSBSIZE */
diff --git a/src/include/statistics/extended_stats_internal.h b/src/include/statistics/extended_stats_internal.h
index c775442f2ee..db64e45f036 100644
--- a/src/include/statistics/extended_stats_internal.h
+++ b/src/include/statistics/extended_stats_internal.h
@@ -45,7 +45,7 @@ typedef struct MultiSortSupportData
 {
 	int			ndims;			/* number of dimensions */
 	/* sort support data for each dimension: */
-	SortSupportData ssup[FLEXIBLE_ARRAY_MEMBER];
+	SortSupportData ssup[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(ndims);
 } MultiSortSupportData;
 
 typedef MultiSortSupportData *MultiSortSupport;
diff --git a/src/include/statistics/statistics.h b/src/include/statistics/statistics.h
index 0b163103a72..550f9f27bb7 100644
--- a/src/include/statistics/statistics.h
+++ b/src/include/statistics/statistics.h
@@ -36,7 +36,7 @@ typedef struct MVNDistinct
 	uint32		magic;			/* magic constant marker */
 	uint32		type;			/* type of ndistinct (BASIC) */
 	uint32		nitems;			/* number of items in the statistic */
-	MVNDistinctItem items[FLEXIBLE_ARRAY_MEMBER];
+	MVNDistinctItem items[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nitems);
 } MVNDistinct;
 
 /* Multivariate functional dependencies */
@@ -51,7 +51,7 @@ typedef struct MVDependency
 {
 	double		degree;			/* degree of validity (0-1) */
 	AttrNumber	nattributes;	/* number of attributes */
-	AttrNumber	attributes[FLEXIBLE_ARRAY_MEMBER];	/* attribute numbers */
+	AttrNumber	attributes[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nattributes);	/* attribute numbers */
 } MVDependency;
 
 typedef struct MVDependencies
@@ -59,7 +59,7 @@ typedef struct MVDependencies
 	uint32		magic;			/* magic constant marker */
 	uint32		type;			/* type of MV Dependencies (BASIC) */
 	uint32		ndeps;			/* number of dependencies */
-	MVDependency *deps[FLEXIBLE_ARRAY_MEMBER];	/* dependencies */
+	MVDependency *deps[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(ndeps);	/* dependencies */
 } MVDependencies;
 
 /* used to flag stats serialized to bytea */
@@ -91,7 +91,7 @@ typedef struct MCVList
 	uint32		nitems;			/* number of MCV items in the array */
 	AttrNumber	ndimensions;	/* number of dimensions */
 	Oid			types[STATS_MAX_DIMENSIONS];	/* OIDs of data types */
-	MCVItem		items[FLEXIBLE_ARRAY_MEMBER];	/* array of MCV items */
+	MCVItem		items[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nitems);	/* array of MCV items */
 } MCVList;
 
 extern MVNDistinct *statext_ndistinct_load(Oid mvoid, bool inh);
diff --git a/src/include/tsearch/dicts/spell.h b/src/include/tsearch/dicts/spell.h
index 7ca7e6fe69f..315c74e3d57 100644
--- a/src/include/tsearch/dicts/spell.h
+++ b/src/include/tsearch/dicts/spell.h
@@ -50,7 +50,7 @@ typedef struct
 typedef struct SPNode
 {
 	uint32		length;
-	SPNodeData	data[FLEXIBLE_ARRAY_MEMBER];
+	SPNodeData	data[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(length);
 } SPNode;
 
 #define SPNHDRSZ	(offsetof(SPNode,data))
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index a28a1e483eb..57d7406c49d 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -179,7 +179,7 @@ typedef struct catclist
 	short		nkeys;			/* number of lookup keys specified */
 	int			n_members;		/* number of member tuples */
 	CatCache   *my_cache;		/* link to owning catcache */
-	CatCTup    *members[FLEXIBLE_ARRAY_MEMBER]; /* members */
+	CatCTup    *members[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(n_members); /* members */
 } CatCList;
 
 
diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h
index 87c50eebf12..0cda0352bcc 100644
--- a/src/include/utils/datetime.h
+++ b/src/include/utils/datetime.h
@@ -216,7 +216,7 @@ typedef struct TimeZoneAbbrevTable
 {
 	Size		tblsize;		/* size in bytes of TimeZoneAbbrevTable */
 	int			numabbrevs;		/* number of entries in abbrevs[] array */
-	datetkn		abbrevs[FLEXIBLE_ARRAY_MEMBER];
+	datetkn		abbrevs[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(numabbrevs);
 	/* DynamicZoneAbbrev(s) may follow the abbrevs[] array */
 } TimeZoneAbbrevTable;
 
diff --git a/src/pl/plpython/plpy_procedure.h b/src/pl/plpython/plpy_procedure.h
index 4103fd22945..0532c4ba72a 100644
--- a/src/pl/plpython/plpy_procedure.h
+++ b/src/pl/plpython/plpy_procedure.h
@@ -26,7 +26,7 @@ typedef struct PLySavedArgs
 	PyObject   *args;			/* "args" element of globals dict */
 	PyObject   *td;				/* "TD" element of globals dict, if trigger */
 	int			nargs;			/* length of namedargs array */
-	PyObject   *namedargs[FLEXIBLE_ARRAY_MEMBER];	/* named args */
+	PyObject   *namedargs[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nargs);	/* named args */
 } PLySavedArgs;
 
 /* saved state for a set-returning function */
-- 
Tristan Partin
https://tristan.partin.io

From da930d38b5f6125b189c93ce44215e88b10a2acd Mon Sep 17 00:00:00 2001
From: Tristan Partin <[email protected]>
Date: Wed, 23 Sep 2026 06:12:21 +0000
Subject: [PATCH v3 2/7] Keep the alignas() placeholder the same width in
 pgindent

pre_indent() hides each alignas(...) call from pg_bsd_indent behind
a plain identifier, because pg_bsd_indent has a fixed table of
declaration keywords and lexes anything else followed by "(" as the
start of an expression.

The placeholder was not the same width as the text it stood in for,
though, and pg_bsd_indent decides where to put a trailing comment from
the width of the code preceding it. A member declared with alignas()
and carrying a trailing comment therefore got the comment separated by
a tab where the real width calls for a single space, or the other way
round. Pad the placeholder out to the original width so that the
decision is made on the real width.

No member currently declared with alignas() has a trailing comment, so
this changes nothing in the tree today; it is a correctness fix for the
helper.

Author: Tristan Partin <[email protected]>
Signed-off-by: Tristan Partin <[email protected]>
---
 src/tools/pgindent/pgindent | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/src/tools/pgindent/pgindent b/src/tools/pgindent/pgindent
index eea6c0ad734..6f4b7cbc92a 100755
--- a/src/tools/pgindent/pgindent
+++ b/src/tools/pgindent/pgindent
@@ -239,6 +239,17 @@ sub write_source
 # by post_indent().
 my @alignas_stash;
 
+sub stash_alignas
+{
+	my $text = shift;
+
+	push(@alignas_stash, $text);
+	my $tag = 'alignas_' . $#alignas_stash;
+	$tag .= '_' x (length($text) - length($tag))
+	  if length($text) > length($tag);
+	return $tag;
+}
+
 sub pre_indent
 {
 	my $source = shift;
@@ -276,8 +287,7 @@ sub pre_indent
 		\b alignas \s*
 		( \( (?: [^()]++ | (?1) )*+ \) )
 	!
-		push(@alignas_stash, "alignas" . $1);
-		"alignas_" . $#alignas_stash . "_";
+		stash_alignas("alignas" . $1);
 	!gex;
 
 	return $source;
@@ -288,7 +298,7 @@ sub post_indent
 	my $source = shift;
 
 	# Restore alignas(...)
-	$source =~ s!\balignas_(\d+)_!$alignas_stash[$1]!g;
+	$source =~ s!\balignas_(\d+)_*!$alignas_stash[$1]!g;
 
 	# Restore CATALOG lines
 	$source =~ s!^/\*(CATALOG\(.*)\*/$!$1!gm;
-- 
Tristan Partin
https://tristan.partin.io

From 7fc13bfcadc05e71786a6cdbfcc6a7733b2a06eb Mon Sep 17 00:00:00 2001
From: Tristan Partin <[email protected]>
Date: Wed, 23 Sep 2026 06:24:51 +0000
Subject: [PATCH v3 3/7] Generalize the alignas() workaround to a list

alignas() may not be the only syntax that needs to be worked around, so
make the change more generic.

Author: Tristan Partin <[email protected]>
Signed-off-by: Tristan Partin <[email protected]>
---
 src/tools/pgindent/pgindent | 37 +++++++++++++++++++++----------------
 1 file changed, 21 insertions(+), 16 deletions(-)

diff --git a/src/tools/pgindent/pgindent b/src/tools/pgindent/pgindent
index 6f4b7cbc92a..67e76549f05 100755
--- a/src/tools/pgindent/pgindent
+++ b/src/tools/pgindent/pgindent
@@ -235,16 +235,21 @@ sub write_source
 	return;
 }
 
-# Text of each alignas(...) call, stashed by pre_indent() and restored
-# by post_indent().
-my @alignas_stash;
+# Declaration attributes that pg_bsd_indent doesn't know how to handle.
+my @declaration_attributes = qw(
+  alignas
+);
 
-sub stash_alignas
+# Text of each declaration attribute, stashed by pre_indent() and restored
+# by post_indent().
+my @attribute_stash;
+
+sub stash_attribute
 {
 	my $text = shift;
 
-	push(@alignas_stash, $text);
-	my $tag = 'alignas_' . $#alignas_stash;
+	push(@attribute_stash, $text);
+	my $tag = '_pgattr' . $#attribute_stash;
 	$tag .= '_' x (length($text) - length($tag))
 	  if length($text) > length($tag);
 	return $tag;
@@ -278,16 +283,16 @@ sub pre_indent
 	# Protect wrapping in CATALOG()
 	$source =~ s!^(CATALOG\(.*)$!/*$1*/!gm;
 
-	# pg_bsd_indent doesn't know about alignas(), so a non-first struct
-	# member declared with it gets misindented.  Disguise each call as
-	# a plain identifier; stash the original text rather than embed it,
-	# so nested parens or line breaks in the argument aren't a problem.
-	@alignas_stash = ();
+	# Disguise each declaration attribute as a plain identifier; stash the
+	# original text rather than embed it, so nested parens or line breaks in
+	# the argument aren't a problem.
+	@attribute_stash = ();
+	my $attribute = join('|', @declaration_attributes);
 	$source =~ s!
-		\b alignas \s*
-		( \( (?: [^()]++ | (?1) )*+ \) )
+		\b ($attribute) \s*
+		( \( (?: [^()]++ | (?2) )*+ \) )
 	!
-		stash_alignas("alignas" . $1);
+		stash_attribute($1 . $2);
 	!gex;
 
 	return $source;
@@ -297,8 +302,8 @@ sub post_indent
 {
 	my $source = shift;
 
-	# Restore alignas(...)
-	$source =~ s!\balignas_(\d+)_*!$alignas_stash[$1]!g;
+	# Restore declaration attributes
+	$source =~ s!\b_pgattr(\d+)_*!$attribute_stash[$1]!g;
 
 	# Restore CATALOG lines
 	$source =~ s!^/\*(CATALOG\(.*)\*/$!$1!gm;
-- 
Tristan Partin
https://tristan.partin.io

From 1cf55a82b6cc755efcf9677963db3d3449ae1c70 Mon Sep 17 00:00:00 2001
From: Tristan Partin <[email protected]>
Date: Wed, 23 Sep 2026 06:25:15 +0000
Subject: [PATCH v3 4/7] Hide pg_attribute_counted_by() from pg_bsd_indent

Add it to the list of declaration attributes, so that it can be written
in front of the type.

Author: Tristan Partin <[email protected]>
Signed-off-by: Tristan Partin <[email protected]>
---
 src/tools/pgindent/pgindent | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/tools/pgindent/pgindent b/src/tools/pgindent/pgindent
index 67e76549f05..0dd36e194f7 100755
--- a/src/tools/pgindent/pgindent
+++ b/src/tools/pgindent/pgindent
@@ -238,6 +238,7 @@ sub write_source
 # Declaration attributes that pg_bsd_indent doesn't know how to handle.
 my @declaration_attributes = qw(
   alignas
+  pg_attribute_counted_by
 );
 
 # Text of each declaration attribute, stashed by pre_indent() and restored
-- 
Tristan Partin
https://tristan.partin.io

From c1d579bdfa7bcd2420bfc5d648b2fec3fe2aaf5c Mon Sep 17 00:00:00 2001
From: Tristan Partin <[email protected]>
Date: Wed, 23 Sep 2026 06:31:28 +0000
Subject: [PATCH v3 5/7] Give LsnReadQueue's entries a struct name

Anonymous structs are a little bit harder to read in Clang's
diagnostics:

	struct (unnamed struct at file.c:<line>:<col>)[] __counted_by(size)

With an actual struct name, the diagnostic is much more readable:

	struct LsnReadQueueEntry[] __counted_by(size)

Author: Tristan Partin <[email protected]>
Signed-off-by: Tristan Partin <[email protected]>
---
 src/backend/access/transam/xlogprefetcher.c | 15 ++++++++++-----
 src/tools/pgindent/typedefs.list            |  1 +
 2 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/src/backend/access/transam/xlogprefetcher.c b/src/backend/access/transam/xlogprefetcher.c
index 011f0a98e74..f78656bce9e 100644
--- a/src/backend/access/transam/xlogprefetcher.c
+++ b/src/backend/access/transam/xlogprefetcher.c
@@ -97,6 +97,15 @@ typedef enum
 typedef LsnReadQueueNextStatus (*LsnReadQueueNextFun) (uintptr_t lrq_private,
 													   XLogRecPtr *lsn);
 
+/*
+ * One entry of the queue below.
+ */
+typedef struct LsnReadQueueEntry
+{
+	bool		io;
+	XLogRecPtr	lsn;
+} LsnReadQueueEntry;
+
 /*
  * A simple circular queue of LSNs, using to control the number of
  * (potentially) inflight IOs.  This stands in for a later more general IO
@@ -113,11 +122,7 @@ typedef struct LsnReadQueue
 	uint32		head;
 	uint32		tail;
 	uint32		size;
-	struct
-	{
-		bool		io;
-		XLogRecPtr	lsn;
-	}			queue[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(size);
+	LsnReadQueueEntry queue[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(size);
 } LsnReadQueue;
 
 /*
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 656f1f60862..ced917aa207 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -1704,6 +1704,7 @@ LogicalTape
 LogicalTapeSet
 LookupSet
 LsnReadQueue
+LsnReadQueueEntry
 LsnReadQueueNextFun
 LsnReadQueueNextStatus
 LtreeGistOptions
-- 
Tristan Partin
https://tristan.partin.io

From 5c7994d04ee23658dd06b58d735d12e26e4d3ede Mon Sep 17 00:00:00 2001
From: Tristan Partin <[email protected]>
Date: Wed, 23 Sep 2026 06:32:55 +0000
Subject: [PATCH v3 6/7] Write pg_attribute_counted_by() in front of the type

This is the form that the GCC manual uses[0]. It also opens us up to
supporting MSVC's _Field_size_() in the future, which is a similar
construct.

Link: https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html#index-counted_005fby [0]
Author: Tristan Partin <[email protected]>
Signed-off-by: Tristan Partin <[email protected]>
---
 contrib/hstore/hstore_io.c                     |  2 +-
 contrib/pageinspect/brinfuncs.c                |  2 +-
 contrib/pg_visibility/pg_visibility.c          |  2 +-
 src/backend/access/brin/brin_minmax_multi.c    |  2 +-
 src/backend/access/nbtree/nbtutils.c           |  2 +-
 src/backend/access/transam/multixact.c         |  2 +-
 src/backend/access/transam/xact.c              |  2 +-
 src/backend/access/transam/xlogprefetcher.c    |  2 +-
 src/backend/catalog/index.c                    |  2 +-
 src/backend/commands/tablespace.c              |  2 +-
 src/backend/commands/trigger.c                 |  2 +-
 src/backend/executor/execParallel.c            |  2 +-
 src/backend/postmaster/bgworker.c              |  2 +-
 src/backend/postmaster/checkpointer.c          |  2 +-
 src/backend/postmaster/launch_backend.c        |  2 +-
 .../replication/logical/reorderbuffer.c        |  2 +-
 src/backend/storage/buffer/freelist.c          |  2 +-
 src/backend/storage/ipc/dsm.c                  |  2 +-
 src/backend/storage/ipc/pmsignal.c             |  2 +-
 src/backend/utils/adt/jsonfuncs.c              |  2 +-
 src/backend/utils/adt/rowtypes.c               |  4 ++--
 src/backend/utils/adt/tsvector_op.c            |  2 +-
 src/backend/utils/cache/typcache.c             |  2 +-
 src/backend/utils/sort/sharedtuplestore.c      |  2 +-
 src/backend/utils/sort/tuplesort.c             |  2 +-
 src/bin/pg_dump/pg_dump.h                      |  2 +-
 src/bin/pg_rewind/filemap.h                    |  2 +-
 src/bin/pgbench/pgbench.c                      |  2 +-
 src/include/access/brin_internal.h             |  2 +-
 src/include/access/tupdesc.h                   |  2 +-
 src/include/executor/execPartition.h           |  4 ++--
 src/include/executor/instrument.h              |  2 +-
 src/include/executor/instrument_node.h         | 18 +++++++++---------
 src/include/fe_utils/parallel_slot.h           |  2 +-
 src/include/jit/jit.h                          |  2 +-
 src/include/nodes/bitmapset.h                  |  2 +-
 src/include/regex/regguts.h                    |  4 ++--
 .../statistics/extended_stats_internal.h       |  2 +-
 src/include/statistics/statistics.h            |  8 ++++----
 src/include/tsearch/dicts/spell.h              |  2 +-
 src/include/utils/catcache.h                   |  2 +-
 src/include/utils/datetime.h                   |  2 +-
 src/pl/plpython/plpy_procedure.h               |  2 +-
 43 files changed, 57 insertions(+), 57 deletions(-)

diff --git a/contrib/hstore/hstore_io.c b/contrib/hstore/hstore_io.c
index 8e047eed4fc..4beb5a10cbc 100644
--- a/contrib/hstore/hstore_io.c
+++ b/contrib/hstore/hstore_io.c
@@ -843,7 +843,7 @@ typedef struct RecordIOData
 	/* this field is used only if target type is domain over composite: */
 	void	   *domain_info;	/* opaque cache for domain checks */
 	int			ncolumns;
-	ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(ncolumns);
+	pg_attribute_counted_by(ncolumns) ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER];
 } RecordIOData;
 
 PG_FUNCTION_INFO_V1(hstore_from_record);
diff --git a/contrib/pageinspect/brinfuncs.c b/contrib/pageinspect/brinfuncs.c
index 0b3df183887..c9301ece9d2 100644
--- a/contrib/pageinspect/brinfuncs.c
+++ b/contrib/pageinspect/brinfuncs.c
@@ -34,7 +34,7 @@ PG_FUNCTION_INFO_V1(brin_revmap_data);
 typedef struct brin_column_state
 {
 	int			nstored;
-	FmgrInfo	outputFn[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nstored);
+	pg_attribute_counted_by(nstored) FmgrInfo outputFn[FLEXIBLE_ARRAY_MEMBER];
 } brin_column_state;
 
 
diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c
index ec95b85e624..717c851583f 100644
--- a/contrib/pg_visibility/pg_visibility.c
+++ b/contrib/pg_visibility/pg_visibility.c
@@ -34,7 +34,7 @@ typedef struct vbits
 {
 	BlockNumber next;
 	BlockNumber count;
-	uint8		bits[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(count);
+	pg_attribute_counted_by(count) uint8 bits[FLEXIBLE_ARRAY_MEMBER];
 } vbits;
 
 typedef struct corrupt_items
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index 386f8fc760a..a0494e90242 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -190,7 +190,7 @@ typedef struct Ranges
 	int			target_maxvalues;
 
 	/* values stored for this range - either raw values, or ranges */
-	Datum		values[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(maxvalues);
+	pg_attribute_counted_by(maxvalues) Datum values[FLEXIBLE_ARRAY_MEMBER];
 } Ranges;
 
 /*
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index ef81ec50482..85c1b632522 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -413,7 +413,7 @@ typedef struct BTVacInfo
 	BTCycleId	cycle_ctr;		/* cycle ID most recently assigned */
 	int			num_vacuums;	/* number of currently active VACUUMs */
 	int			max_vacuums;	/* allocated length of vacuums[] array */
-	BTOneVacInfo vacuums[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(max_vacuums);
+	pg_attribute_counted_by(max_vacuums) BTOneVacInfo vacuums[FLEXIBLE_ARRAY_MEMBER];
 } BTVacInfo;
 
 static BTVacInfo *btvacinfo;
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 2ae1857a7a0..9994444c65c 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -293,7 +293,7 @@ typedef struct mXactCacheEnt
 	MultiXactId multi;
 	int			nmembers;
 	dlist_node	node;
-	MultiXactMember members[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nmembers);
+	pg_attribute_counted_by(nmembers) MultiXactMember members[FLEXIBLE_ARRAY_MEMBER];
 } mXactCacheEnt;
 
 #define MAX_CACHE_ENTRIES	256
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a8e063933e5..747aae72eab 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -235,7 +235,7 @@ typedef struct SerializedTransactionState
 	FullTransactionId currentFullTransactionId;
 	CommandId	currentCommandId;
 	int			nParallelCurrentXids;
-	TransactionId parallelCurrentXids[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nParallelCurrentXids);
+	pg_attribute_counted_by(nParallelCurrentXids) TransactionId parallelCurrentXids[FLEXIBLE_ARRAY_MEMBER];
 } SerializedTransactionState;
 
 /* The size of SerializedTransactionState, not including the final array. */
diff --git a/src/backend/access/transam/xlogprefetcher.c b/src/backend/access/transam/xlogprefetcher.c
index f78656bce9e..efaa0e92e95 100644
--- a/src/backend/access/transam/xlogprefetcher.c
+++ b/src/backend/access/transam/xlogprefetcher.c
@@ -122,7 +122,7 @@ typedef struct LsnReadQueue
 	uint32		head;
 	uint32		tail;
 	uint32		size;
-	LsnReadQueueEntry queue[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(size);
+	pg_attribute_counted_by(size) LsnReadQueueEntry queue[FLEXIBLE_ARRAY_MEMBER];
 } LsnReadQueue;
 
 /*
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index af3318184ad..99413556572 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -96,7 +96,7 @@ typedef struct
 	Oid			currentlyReindexedHeap;
 	Oid			currentlyReindexedIndex;
 	int			numPendingReindexedIndexes;
-	Oid			pendingReindexedIndexes[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(numPendingReindexedIndexes);
+	pg_attribute_counted_by(numPendingReindexedIndexes) Oid pendingReindexedIndexes[FLEXIBLE_ARRAY_MEMBER];
 } SerializedReindexState;
 
 /* non-export function prototypes */
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 8c5f1a5a1e4..7c40176b7b9 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -1216,7 +1216,7 @@ typedef struct
 {
 	/* Array of OIDs to be passed to SetTempTablespaces() */
 	int			numSpcs;
-	Oid			tblSpcs[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(numSpcs);
+	pg_attribute_counted_by(numSpcs) Oid tblSpcs[FLEXIBLE_ARRAY_MEMBER];
 } temp_tablespaces_extra;
 
 /* check_hook: validate new temp_tablespaces */
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 91d451d229e..82b0ebac250 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -3669,7 +3669,7 @@ typedef struct SetConstraintStateData
 	bool		all_isdeferred;
 	int			numstates;		/* number of trigstates[] entries in use */
 	int			numalloc;		/* allocated size of trigstates[] */
-	SetConstraintTriggerData trigstates[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(numalloc);
+	pg_attribute_counted_by(numalloc) SetConstraintTriggerData trigstates[FLEXIBLE_ARRAY_MEMBER];
 } SetConstraintStateData;
 
 typedef SetConstraintStateData *SetConstraintState;
diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c
index 9d495673a73..72856c1a57d 100644
--- a/src/backend/executor/execParallel.c
+++ b/src/backend/executor/execParallel.c
@@ -103,7 +103,7 @@ struct SharedExecutorInstrumentation
 	int			instrument_offset;
 	int			num_workers;
 	int			num_plan_nodes;
-	int			plan_node_id[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_plan_nodes);
+	pg_attribute_counted_by(num_plan_nodes) int plan_node_id[FLEXIBLE_ARRAY_MEMBER];
 
 	/*
 	 * Array of num_plan_nodes * num_workers NodeInstrumentation objects
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 74ac98dae23..4aef037d5ae 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -101,7 +101,7 @@ typedef struct BackgroundWorkerArray
 	int			total_slots;
 	uint32		parallel_register_count;
 	uint32		parallel_terminate_count;
-	BackgroundWorkerSlot slot[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(total_slots);
+	pg_attribute_counted_by(total_slots) BackgroundWorkerSlot slot[FLEXIBLE_ARRAY_MEMBER];
 } BackgroundWorkerArray;
 
 struct BackgroundWorkerHandle
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 1d1431b7bb7..02e4f6c338a 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -140,7 +140,7 @@ typedef struct
 								 * buffer */
 
 	/* The ring buffer of pending checkpointer requests */
-	CheckpointerRequest requests[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(max_requests);
+	pg_attribute_counted_by(max_requests) CheckpointerRequest requests[FLEXIBLE_ARRAY_MEMBER];
 } CheckpointerShmemStruct;
 
 static CheckpointerShmemStruct *CheckpointerShmem;
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 8bb77e7880b..bafdf5e8df1 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -145,7 +145,7 @@ typedef struct
 	 * Extra startup data, content depends on the child process.
 	 */
 	size_t		startup_data_len;
-	char		startup_data[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(startup_data_len);
+	pg_attribute_counted_by(startup_data_len) char startup_data[FLEXIBLE_ARRAY_MEMBER];
 } BackendParameters;
 
 #define SizeOfBackendParameters(startup_data_len) (offsetof(BackendParameters, startup_data) + startup_data_len)
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index 9d17594f901..b70ea469d39 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -171,7 +171,7 @@ typedef struct ReorderBufferIterTXNState
 	binaryheap *heap;
 	Size		nr_txns;
 	dlist_head	old_change;
-	ReorderBufferIterTXNEntry entries[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nr_txns);
+	pg_attribute_counted_by(nr_txns) ReorderBufferIterTXNEntry entries[FLEXIBLE_ARRAY_MEMBER];
 } ReorderBufferIterTXNState;
 
 /* toast datastructures */
diff --git a/src/backend/storage/buffer/freelist.c b/src/backend/storage/buffer/freelist.c
index 6a3c002cfc0..ac84821517a 100644
--- a/src/backend/storage/buffer/freelist.c
+++ b/src/backend/storage/buffer/freelist.c
@@ -90,7 +90,7 @@ typedef struct BufferAccessStrategyData
 	 * simplicity this is palloc'd together with the fixed fields of the
 	 * struct.
 	 */
-	Buffer		buffers[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nbuffers);
+	pg_attribute_counted_by(nbuffers) Buffer buffers[FLEXIBLE_ARRAY_MEMBER];
 }			BufferAccessStrategyData;
 
 
diff --git a/src/backend/storage/ipc/dsm.c b/src/backend/storage/ipc/dsm.c
index 1c99e600765..577854297f9 100644
--- a/src/backend/storage/ipc/dsm.c
+++ b/src/backend/storage/ipc/dsm.c
@@ -93,7 +93,7 @@ typedef struct dsm_control_header
 	uint32		magic;
 	uint32		nitems;
 	uint32		maxitems;
-	dsm_control_item item[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(maxitems);
+	pg_attribute_counted_by(maxitems) dsm_control_item item[FLEXIBLE_ARRAY_MEMBER];
 } dsm_control_header;
 
 static void dsm_cleanup_for_mmap(void);
diff --git a/src/backend/storage/ipc/pmsignal.c b/src/backend/storage/ipc/pmsignal.c
index a1bb635fb42..31116287a05 100644
--- a/src/backend/storage/ipc/pmsignal.c
+++ b/src/backend/storage/ipc/pmsignal.c
@@ -78,7 +78,7 @@ struct PMSignalData
 	QuitSignalReason sigquit_reason;	/* why SIGQUIT was sent */
 	/* per-child-process flags */
 	int			num_child_flags;	/* # of entries in PMChildFlags[] */
-	sig_atomic_t PMChildFlags[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_child_flags);
+	pg_attribute_counted_by(num_child_flags) sig_atomic_t PMChildFlags[FLEXIBLE_ARRAY_MEMBER];
 };
 
 /* PMSignalState pointer is valid in both postmaster and child processes */
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 64c85e9ddd1..adbafd0ecd3 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -233,7 +233,7 @@ struct RecordIOData
 	Oid			record_type;
 	int32		record_typmod;
 	int			ncolumns;
-	ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(ncolumns);
+	pg_attribute_counted_by(ncolumns) ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER];
 };
 
 /* per-query cache for populate_record_worker and populate_recordset_worker */
diff --git a/src/backend/utils/adt/rowtypes.c b/src/backend/utils/adt/rowtypes.c
index ffa60aaaba0..abe2246c47e 100644
--- a/src/backend/utils/adt/rowtypes.c
+++ b/src/backend/utils/adt/rowtypes.c
@@ -45,7 +45,7 @@ typedef struct RecordIOData
 	Oid			record_type;
 	int32		record_typmod;
 	int			ncolumns;
-	ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(ncolumns);
+	pg_attribute_counted_by(ncolumns) ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER];
 } RecordIOData;
 
 /*
@@ -63,7 +63,7 @@ typedef struct RecordCompareData
 	int32		record1_typmod;
 	Oid			record2_type;
 	int32		record2_typmod;
-	ColumnCompareData columns[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(ncolumns);
+	pg_attribute_counted_by(ncolumns) ColumnCompareData columns[FLEXIBLE_ARRAY_MEMBER];
 } RecordCompareData;
 
 
diff --git a/src/backend/utils/adt/tsvector_op.c b/src/backend/utils/adt/tsvector_op.c
index 53de1d7b455..cc9fd588698 100644
--- a/src/backend/utils/adt/tsvector_op.c
+++ b/src/backend/utils/adt/tsvector_op.c
@@ -50,7 +50,7 @@ typedef struct StatEntry
 	struct StatEntry *left;
 	struct StatEntry *right;
 	uint32		lenlexeme;
-	char		lexeme[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(lenlexeme);
+	pg_attribute_counted_by(lenlexeme) char lexeme[FLEXIBLE_ARRAY_MEMBER];
 } StatEntry;
 
 #define STATENTRYHDRSZ	(offsetof(StatEntry, lexeme))
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 57c47be242a..151ade166db 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -155,7 +155,7 @@ typedef struct TypeCacheEnumData
 	Oid			bitmap_base;	/* OID corresponding to bit 0 of bitmapset */
 	Bitmapset  *sorted_values;	/* Set of OIDs known to be in order */
 	int			num_values;		/* total number of values in enum */
-	EnumItem	enum_values[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_values);
+	pg_attribute_counted_by(num_values) EnumItem enum_values[FLEXIBLE_ARRAY_MEMBER];
 } TypeCacheEnumData;
 
 /*
diff --git a/src/backend/utils/sort/sharedtuplestore.c b/src/backend/utils/sort/sharedtuplestore.c
index 5c6af636682..78c3f504d14 100644
--- a/src/backend/utils/sort/sharedtuplestore.c
+++ b/src/backend/utils/sort/sharedtuplestore.c
@@ -64,7 +64,7 @@ struct SharedTuplestore
 	char		name[NAMEDATALEN];	/* A name for this tuplestore. */
 
 	/* Followed by per-participant shared state. */
-	SharedTuplestoreParticipant participants[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nparticipants);
+	pg_attribute_counted_by(nparticipants) SharedTuplestoreParticipant participants[FLEXIBLE_ARRAY_MEMBER];
 };
 
 /* Per-participant state that lives in backend-local memory. */
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index 868baaac05e..da3c41d7084 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -362,7 +362,7 @@ struct Sharedsort
 	 * Tapes array used by workers to report back information needed by the
 	 * leader to concatenate all worker tapes into one for merging
 	 */
-	TapeShare	tapes[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nTapes);
+	pg_attribute_counted_by(nTapes) TapeShare tapes[FLEXIBLE_ARRAY_MEMBER];
 };
 
 /*
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 1888c6f07ba..4dcb6c854e8 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -640,7 +640,7 @@ typedef struct _loInfo
 	DumpableAcl dacl;
 	const char *rolname;
 	int			numlos;
-	Oid			looids[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(numlos);
+	pg_attribute_counted_by(numlos) Oid looids[FLEXIBLE_ARRAY_MEMBER];
 } LoInfo;
 
 /*
diff --git a/src/bin/pg_rewind/filemap.h b/src/bin/pg_rewind/filemap.h
index c528aa74263..e6252bb9db4 100644
--- a/src/bin/pg_rewind/filemap.h
+++ b/src/bin/pg_rewind/filemap.h
@@ -101,7 +101,7 @@ typedef struct filemap_t
 	uint64		fetch_size;		/* number of bytes that needs to be copied */
 
 	int			nentries;		/* size of 'entries' array */
-	file_entry_t *entries[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nentries);
+	pg_attribute_counted_by(nentries) file_entry_t *entries[FLEXIBLE_ARRAY_MEMBER];
 } filemap_t;
 
 /* Functions for populating the filemap */
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index d6318020a64..06c43fd6942 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -98,7 +98,7 @@ typedef struct socket_set
 {
 	int			maxfds;			/* allocated length of pollfds[] array */
 	int			curfds;			/* number currently in use */
-	struct pollfd pollfds[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(maxfds);
+	pg_attribute_counted_by(maxfds) struct pollfd pollfds[FLEXIBLE_ARRAY_MEMBER];
 } socket_set;
 
 #endif							/* POLL_USING_PPOLL */
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index 29fb4b870ae..f860ac13f45 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -34,7 +34,7 @@ typedef struct BrinOpcInfo
 	void	   *oi_opaque;
 
 	/* Type cache entries of the stored columns */
-	TypeCacheEntry *oi_typcache[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(oi_nstored);
+	pg_attribute_counted_by(oi_nstored) TypeCacheEntry *oi_typcache[FLEXIBLE_ARRAY_MEMBER];
 } BrinOpcInfo;
 
 /* the size of a BrinOpcInfo for the given number of columns */
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index 8febd18cc3a..89f9a41b104 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -158,7 +158,7 @@ typedef struct TupleDescData
 										 * compact_attrs element. */
 	TupleConstr *constr;		/* constraints, or NULL if none */
 	/* compact_attrs[N] is the compact metadata of Attribute Number N+1 */
-	CompactAttribute compact_attrs[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(natts);
+	pg_attribute_counted_by(natts) CompactAttribute compact_attrs[FLEXIBLE_ARRAY_MEMBER];
 }			TupleDescData;
 typedef struct TupleDescData *TupleDesc;
 
diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h
index 9c029c9340c..f2d72dbb8f8 100644
--- a/src/include/executor/execPartition.h
+++ b/src/include/executor/execPartition.h
@@ -84,7 +84,7 @@ typedef struct PartitionedRelPruningData
 typedef struct PartitionPruningData
 {
 	int			num_partrelprunedata;	/* number of array entries */
-	PartitionedRelPruningData partrelprunedata[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_partrelprunedata);
+	pg_attribute_counted_by(num_partrelprunedata) PartitionedRelPruningData partrelprunedata[FLEXIBLE_ARRAY_MEMBER];
 } PartitionPruningData;
 
 /*
@@ -127,7 +127,7 @@ typedef struct PartitionPruneState
 	bool		do_initial_prune;
 	bool		do_exec_prune;
 	int			num_partprunedata;
-	PartitionPruningData *partprunedata[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_partprunedata);
+	pg_attribute_counted_by(num_partprunedata) PartitionPruningData *partprunedata[FLEXIBLE_ARRAY_MEMBER];
 } PartitionPruneState;
 
 extern void ExecDoInitialPruning(EState *estate);
diff --git a/src/include/executor/instrument.h b/src/include/executor/instrument.h
index d955095c882..08aa2ac6c3a 100644
--- a/src/include/executor/instrument.h
+++ b/src/include/executor/instrument.h
@@ -115,7 +115,7 @@ typedef struct NodeInstrumentation
 typedef struct WorkerNodeInstrumentation
 {
 	int			num_workers;	/* # of structures that follow */
-	NodeInstrumentation instrument[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_workers);
+	pg_attribute_counted_by(num_workers) NodeInstrumentation instrument[FLEXIBLE_ARRAY_MEMBER];
 } WorkerNodeInstrumentation;
 
 typedef struct TriggerInstrumentation
diff --git a/src/include/executor/instrument_node.h b/src/include/executor/instrument_node.h
index ec690197b04..8b8e7648550 100644
--- a/src/include/executor/instrument_node.h
+++ b/src/include/executor/instrument_node.h
@@ -44,7 +44,7 @@ typedef struct AggregateInstrumentation
 typedef struct SharedAggInfo
 {
 	int			num_workers;
-	AggregateInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_workers);
+	pg_attribute_counted_by(num_workers) AggregateInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER];
 } SharedAggInfo;
 
 
@@ -116,7 +116,7 @@ typedef struct IndexScanInstrumentation
 typedef struct SharedIndexScanInstrumentation
 {
 	int			num_workers;
-	IndexScanInstrumentation winstrument[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_workers);
+	pg_attribute_counted_by(num_workers) IndexScanInstrumentation winstrument[FLEXIBLE_ARRAY_MEMBER];
 } SharedIndexScanInstrumentation;
 
 
@@ -140,7 +140,7 @@ typedef struct BitmapHeapScanInstrumentation
 typedef struct SharedBitmapHeapInstrumentation
 {
 	int			num_workers;
-	BitmapHeapScanInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_workers);
+	pg_attribute_counted_by(num_workers) BitmapHeapScanInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER];
 } SharedBitmapHeapInstrumentation;
 
 
@@ -169,7 +169,7 @@ typedef struct MemoizeInstrumentation
 typedef struct SharedMemoizeInfo
 {
 	int			num_workers;
-	MemoizeInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_workers);
+	pg_attribute_counted_by(num_workers) MemoizeInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER];
 } SharedMemoizeInfo;
 
 
@@ -215,7 +215,7 @@ typedef struct TuplesortInstrumentation
 typedef struct SharedSortInfo
 {
 	int			num_workers;
-	TuplesortInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_workers);
+	pg_attribute_counted_by(num_workers) TuplesortInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER];
 } SharedSortInfo;
 
 
@@ -238,7 +238,7 @@ typedef struct HashInstrumentation
 typedef struct SharedHashInfo
 {
 	int			num_workers;
-	HashInstrumentation hinstrument[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_workers);
+	pg_attribute_counted_by(num_workers) HashInstrumentation hinstrument[FLEXIBLE_ARRAY_MEMBER];
 } SharedHashInfo;
 
 
@@ -266,7 +266,7 @@ typedef struct IncrementalSortInfo
 typedef struct SharedIncrementalSortInfo
 {
 	int			num_workers;
-	IncrementalSortInfo sinfo[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_workers);
+	pg_attribute_counted_by(num_workers) IncrementalSortInfo sinfo[FLEXIBLE_ARRAY_MEMBER];
 } SharedIncrementalSortInfo;
 
 
@@ -285,7 +285,7 @@ typedef struct SeqScanInstrumentation
 typedef struct SharedSeqScanInstrumentation
 {
 	int			num_workers;
-	SeqScanInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_workers);
+	pg_attribute_counted_by(num_workers) SeqScanInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER];
 } SharedSeqScanInstrumentation;
 
 
@@ -303,7 +303,7 @@ typedef struct TidRangeScanInstrumentation
 typedef struct SharedTidRangeScanInstrumentation
 {
 	int			num_workers;
-	TidRangeScanInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_workers);
+	pg_attribute_counted_by(num_workers) TidRangeScanInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER];
 } SharedTidRangeScanInstrumentation;
 
 #endif							/* INSTRUMENT_NODE_H */
diff --git a/src/include/fe_utils/parallel_slot.h b/src/include/fe_utils/parallel_slot.h
index 24f1fe3942f..5cab832434e 100644
--- a/src/include/fe_utils/parallel_slot.h
+++ b/src/include/fe_utils/parallel_slot.h
@@ -40,7 +40,7 @@ typedef struct ParallelSlotArray
 	const char *progname;
 	bool		echo;
 	const char *initcmd;
-	ParallelSlot slots[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(numslots);
+	pg_attribute_counted_by(numslots) ParallelSlot slots[FLEXIBLE_ARRAY_MEMBER];
 } ParallelSlotArray;
 
 static inline void
diff --git a/src/include/jit/jit.h b/src/include/jit/jit.h
index 8ffa60a2001..3b04eae73ae 100644
--- a/src/include/jit/jit.h
+++ b/src/include/jit/jit.h
@@ -51,7 +51,7 @@ typedef struct JitInstrumentation
 typedef struct SharedJitInstrumentation
 {
 	int			num_workers;
-	JitInstrumentation jit_instr[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(num_workers);
+	pg_attribute_counted_by(num_workers) JitInstrumentation jit_instr[FLEXIBLE_ARRAY_MEMBER];
 } SharedJitInstrumentation;
 
 typedef struct JitContext
diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h
index 350b9bb189e..fa91fc7356f 100644
--- a/src/include/nodes/bitmapset.h
+++ b/src/include/nodes/bitmapset.h
@@ -52,7 +52,7 @@ typedef struct Bitmapset
 
 	NodeTag		type;
 	int			nwords;			/* number of words in array */
-	bitmapword	words[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nwords);
+	pg_attribute_counted_by(nwords) bitmapword words[FLEXIBLE_ARRAY_MEMBER];
 } Bitmapset;
 
 
diff --git a/src/include/regex/regguts.h b/src/include/regex/regguts.h
index fce0f8981b5..6d618ec69fb 100644
--- a/src/include/regex/regguts.h
+++ b/src/include/regex/regguts.h
@@ -320,7 +320,7 @@ struct arcbatch
 {								/* for bulk allocation of arcs */
 	struct arcbatch *next;		/* chain link */
 	size_t		narcs;			/* number of arcs allocated in this arcbatch */
-	struct arc	a[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(narcs);
+	pg_attribute_counted_by(narcs) struct arc a[FLEXIBLE_ARRAY_MEMBER];
 };
 #define  ARCBATCHSIZE(n)  ((n) * sizeof(struct arc) + offsetof(struct arcbatch, a))
 /* first batch will have FIRSTABSIZE arcs; then double it until MAXABSIZE */
@@ -346,7 +346,7 @@ struct statebatch
 {								/* for bulk allocation of states */
 	struct statebatch *next;	/* chain link */
 	size_t		nstates;		/* number of states allocated in this batch */
-	struct state s[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nstates);
+	pg_attribute_counted_by(nstates) struct state s[FLEXIBLE_ARRAY_MEMBER];
 };
 #define  STATEBATCHSIZE(n)  ((n) * sizeof(struct state) + offsetof(struct statebatch, s))
 /* first batch will have FIRSTSBSIZE states; then double it until MAXSBSIZE */
diff --git a/src/include/statistics/extended_stats_internal.h b/src/include/statistics/extended_stats_internal.h
index db64e45f036..4d7151ab11d 100644
--- a/src/include/statistics/extended_stats_internal.h
+++ b/src/include/statistics/extended_stats_internal.h
@@ -45,7 +45,7 @@ typedef struct MultiSortSupportData
 {
 	int			ndims;			/* number of dimensions */
 	/* sort support data for each dimension: */
-	SortSupportData ssup[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(ndims);
+	pg_attribute_counted_by(ndims) SortSupportData ssup[FLEXIBLE_ARRAY_MEMBER];
 } MultiSortSupportData;
 
 typedef MultiSortSupportData *MultiSortSupport;
diff --git a/src/include/statistics/statistics.h b/src/include/statistics/statistics.h
index 550f9f27bb7..ff0cea4b0c5 100644
--- a/src/include/statistics/statistics.h
+++ b/src/include/statistics/statistics.h
@@ -36,7 +36,7 @@ typedef struct MVNDistinct
 	uint32		magic;			/* magic constant marker */
 	uint32		type;			/* type of ndistinct (BASIC) */
 	uint32		nitems;			/* number of items in the statistic */
-	MVNDistinctItem items[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nitems);
+	pg_attribute_counted_by(nitems) MVNDistinctItem items[FLEXIBLE_ARRAY_MEMBER];
 } MVNDistinct;
 
 /* Multivariate functional dependencies */
@@ -51,7 +51,7 @@ typedef struct MVDependency
 {
 	double		degree;			/* degree of validity (0-1) */
 	AttrNumber	nattributes;	/* number of attributes */
-	AttrNumber	attributes[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nattributes);	/* attribute numbers */
+	pg_attribute_counted_by(nattributes) AttrNumber attributes[FLEXIBLE_ARRAY_MEMBER];	/* attribute numbers */
 } MVDependency;
 
 typedef struct MVDependencies
@@ -59,7 +59,7 @@ typedef struct MVDependencies
 	uint32		magic;			/* magic constant marker */
 	uint32		type;			/* type of MV Dependencies (BASIC) */
 	uint32		ndeps;			/* number of dependencies */
-	MVDependency *deps[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(ndeps);	/* dependencies */
+	pg_attribute_counted_by(ndeps) MVDependency *deps[FLEXIBLE_ARRAY_MEMBER];	/* dependencies */
 } MVDependencies;
 
 /* used to flag stats serialized to bytea */
@@ -91,7 +91,7 @@ typedef struct MCVList
 	uint32		nitems;			/* number of MCV items in the array */
 	AttrNumber	ndimensions;	/* number of dimensions */
 	Oid			types[STATS_MAX_DIMENSIONS];	/* OIDs of data types */
-	MCVItem		items[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nitems);	/* array of MCV items */
+	pg_attribute_counted_by(nitems) MCVItem items[FLEXIBLE_ARRAY_MEMBER];	/* array of MCV items */
 } MCVList;
 
 extern MVNDistinct *statext_ndistinct_load(Oid mvoid, bool inh);
diff --git a/src/include/tsearch/dicts/spell.h b/src/include/tsearch/dicts/spell.h
index 315c74e3d57..7172ee9ab50 100644
--- a/src/include/tsearch/dicts/spell.h
+++ b/src/include/tsearch/dicts/spell.h
@@ -50,7 +50,7 @@ typedef struct
 typedef struct SPNode
 {
 	uint32		length;
-	SPNodeData	data[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(length);
+	pg_attribute_counted_by(length) SPNodeData data[FLEXIBLE_ARRAY_MEMBER];
 } SPNode;
 
 #define SPNHDRSZ	(offsetof(SPNode,data))
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index 57d7406c49d..cf3cd2e671d 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -179,7 +179,7 @@ typedef struct catclist
 	short		nkeys;			/* number of lookup keys specified */
 	int			n_members;		/* number of member tuples */
 	CatCache   *my_cache;		/* link to owning catcache */
-	CatCTup    *members[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(n_members); /* members */
+	pg_attribute_counted_by(n_members) CatCTup *members[FLEXIBLE_ARRAY_MEMBER];	/* members */
 } CatCList;
 
 
diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h
index 0cda0352bcc..607a4ca8ae5 100644
--- a/src/include/utils/datetime.h
+++ b/src/include/utils/datetime.h
@@ -216,7 +216,7 @@ typedef struct TimeZoneAbbrevTable
 {
 	Size		tblsize;		/* size in bytes of TimeZoneAbbrevTable */
 	int			numabbrevs;		/* number of entries in abbrevs[] array */
-	datetkn		abbrevs[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(numabbrevs);
+	pg_attribute_counted_by(numabbrevs) datetkn abbrevs[FLEXIBLE_ARRAY_MEMBER];
 	/* DynamicZoneAbbrev(s) may follow the abbrevs[] array */
 } TimeZoneAbbrevTable;
 
diff --git a/src/pl/plpython/plpy_procedure.h b/src/pl/plpython/plpy_procedure.h
index 0532c4ba72a..0cfd14cb037 100644
--- a/src/pl/plpython/plpy_procedure.h
+++ b/src/pl/plpython/plpy_procedure.h
@@ -26,7 +26,7 @@ typedef struct PLySavedArgs
 	PyObject   *args;			/* "args" element of globals dict */
 	PyObject   *td;				/* "TD" element of globals dict, if trigger */
 	int			nargs;			/* length of namedargs array */
-	PyObject   *namedargs[FLEXIBLE_ARRAY_MEMBER] pg_attribute_counted_by(nargs);	/* named args */
+	pg_attribute_counted_by(nargs) PyObject *namedargs[FLEXIBLE_ARRAY_MEMBER];	/* named args */
 } PLySavedArgs;
 
 /* saved state for a set-returning function */
-- 
Tristan Partin
https://tristan.partin.io

From 3c37743c7bf49d2f9fdb7a38a7bd8e40ea9eb32a Mon Sep 17 00:00:00 2001
From: Tristan Partin <[email protected]>
Date: Wed, 23 Sep 2026 06:42:15 +0000
Subject: [PATCH v3 7/7] Map pg_attribute_counted_by() to _Field_size_() under
 MSVC

MSVC has no counted_by attribute, but its source annotation language has
_Field_size_(), which states the same property: the field is a buffer
whose writable size in elements is given by the named expression.

The static analyzer will make use of the information when enabling the
static analyzer with /analyze.

Author: Tristan Partin <[email protected]>
Signed-off-by: Tristan Partin <[email protected]>
---
 src/include/c.h | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/src/include/c.h b/src/include/c.h
index 57d891e52a7..b377219559b 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -76,6 +76,9 @@
 #if defined(WIN32) || defined(__CYGWIN__)
 #include <fcntl.h>				/* ensure O_BINARY is available */
 #endif
+#ifdef _MSC_VER
+#include <sal.h>
+#endif
 #include <locale.h>
 #ifdef HAVE_XLOCALE_H
 #include <xlocale.h>
@@ -310,10 +313,17 @@ extern "C++"
  *   including after either member is updated.
  *
  * The attribute is ignored in C++ due to lack of compiler support.
+ *
+ * MSVC has no equivalent of its own, but its source annotation language spells
+ * the same property _Field_size_(), which its static analyzer understands.
+ * That only has an effect under /analyze; in an ordinary build it expands to
+ * nothing.
  */
 #ifndef __cplusplus
 #if __has_attribute (counted_by)
 #define pg_attribute_counted_by(count) __attribute__((counted_by(count)))
+#elif defined(_MSC_VER)
+#define pg_attribute_counted_by(count) _Field_size_(count)
 #else
 #define pg_attribute_counted_by(count)
 #endif
-- 
Tristan Partin
https://tristan.partin.io

Reply via email to