On Wed Oct 7, 2026 at 5:15 AM UTC, Tristan Partin wrote:
> On Tue Sep 29, 2026 at 6:49 AM UTC, Peter Eisentraut wrote:
>> On 06.07.26 20:11, Tristan Partin wrote:
>>> On Mon Jul 6, 2026 at 6:10 PM UTC, Tristan Partin wrote:
>>>> Postgres memory allocators, by default, ERROR out on memory allocation
>>>> failures. An ERROR leads to a longjmp, which means that the caller of
>>>> the allocator will never see a NULL return value. We can explicitly let
>>>> the compiler know about this behavior by adding the returns_nonnull
>>>> attribute to the allocators that follow this behavior. Postgres does
>>>> support _extended versions of some of the allocators that take a flaks
>>>> argument. The caller can provide the MCXT_ALLOC_NO_OOM flag to these
>>>> allocators to request that they return NULL on allocation failure
>>>> instead of ERROR-ing out. The _extended allocators cannot be marked as
>>>> returns_nonnull because of that.
>>>>
>>>> By using returns_nonnull, we can help the compiler to optimize call
>>>> sites.
>>
>> Your patch marks palloc_mul_extended() as pg_attribute_returns_nonnull, 
>> which seems incorrect per the above description.
>
> Oops. Sorry about that. I must've not sent the most up to date version 
> of the patch because I definitely didn't have that locally.
>
>> Also, per the discussion in the counted_by thread, lets put these new 
>> attributes in their more correct position in front of the declaration. 
>> (Note that several of these already use pg_nodiscard, which is also an 
>> attribute.)  That way we can also use the MSVC annotation _Ret_notnull_.
>
> Good points. See the revised attached patch. You definitely have a knack 
> for knowing MSVC attributes. I'll have to try to keep those in mind.

Here is a v3 that adds missing annotations for the following functions 
in palloc.h:

- repalloc
- repalloc0
- repalloc_mul
- repalloc_huge

And for fe_memutils.h:

- pg_malloc
- pg_malloc0
- pg_malloc_mul
- pg_malloc0_mul
- palloc
- palloc0
- palloc_mul
- palloc0_mul

-- 
Tristan Partin
PostgreSQL Contributors Team
AWS (https://aws.amazon.com)
From af0fb54aa1a97cce036f2799c7ef413f5cbb4365 Mon Sep 17 00:00:00 2001
From: Tristan Partin <[email protected]>
Date: Wed, 7 Oct 2026 05:01:11 +0000
Subject: [PATCH v3] Add pg_attribute_returns_nonnull to various memory
 allocation functions

This function attribute tells the compiler that the function will never
return NULL.  This allows the compiler to do better static analysis and
lets it optimize the caller given the non-NULL guarantee.

The attribute is placed in front of the declaration, rather than after
it, so that it can also expand to the MSVC annotation _Ret_notnull_,
which is only valid in that position.

Only functions that raise an error on allocation failure are marked.
Functions that take a flags argument, such as palloc_extended() and
palloc_mul_extended(), can return NULL when MCXT_ALLOC_NO_OOM is passed
and are therefore not marked.

Signed-off-by: Tristan Partin <[email protected]>
---
 src/include/c.h                  | 15 +++++++++++++++
 src/include/common/fe_memutils.h | 30 +++++++++++++++---------------
 src/include/utils/palloc.h       | 32 ++++++++++++++++----------------
 3 files changed, 46 insertions(+), 31 deletions(-)

diff --git a/src/include/c.h b/src/include/c.h
index 95e71d7e612..6e5481d2116 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -304,6 +304,21 @@ extern "C++"
 #define pg_attribute_nonnull(...)
 #endif
 
+/*
+ * pg_attribute_returns_nonnull means the function never returns NULL.  It
+ * must be placed in front of the declaration, which is where the MSVC
+ * annotation _Ret_notnull_ requires it to be.
+ *
+ * https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html#index-returns_005fnonnull
+ */
+#if __has_attribute (returns_nonnull)
+#define pg_attribute_returns_nonnull __attribute__((returns_nonnull))
+#elif defined(_MSC_VER)
+#define pg_attribute_returns_nonnull _Ret_notnull_
+#else
+#define pg_attribute_returns_nonnull
+#endif
+
 /*
  * pg_attribute_target allows specifying different target options that the
  * function should be compiled with (e.g., for using special CPU instructions).
diff --git a/src/include/common/fe_memutils.h b/src/include/common/fe_memutils.h
index d5c6d37bb66..63d621a2f15 100644
--- a/src/include/common/fe_memutils.h
+++ b/src/include/common/fe_memutils.h
@@ -34,11 +34,11 @@
  * "Safe" memory allocation functions --- these exit(1) on failure
  * (except pg_malloc_extended with MCXT_ALLOC_NO_OOM)
  */
-extern char *pg_strdup(const char *in);
-extern void *pg_malloc(size_t size);
-extern void *pg_malloc0(size_t size);
+pg_attribute_returns_nonnull extern char *pg_strdup(const char *in);
+pg_attribute_returns_nonnull extern void *pg_malloc(size_t size);
+pg_attribute_returns_nonnull extern void *pg_malloc0(size_t size);
 extern void *pg_malloc_extended(size_t size, int flags);
-extern void *pg_realloc(void *ptr, size_t size);
+pg_attribute_returns_nonnull extern void *pg_realloc(void *ptr, size_t size);
 extern void pg_free(void *ptr);
 
 /*
@@ -46,10 +46,10 @@ extern void pg_free(void *ptr);
  */
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
-extern void *pg_malloc_mul(Size s1, Size s2);
-extern void *pg_malloc0_mul(Size s1, Size s2);
+pg_attribute_returns_nonnull extern void *pg_malloc_mul(Size s1, Size s2);
+pg_attribute_returns_nonnull extern void *pg_malloc0_mul(Size s1, Size s2);
 extern void *pg_malloc_mul_extended(Size s1, Size s2, int flags);
-extern void *pg_realloc_mul(void *p, Size s1, Size s2);
+pg_attribute_returns_nonnull extern void *pg_realloc_mul(void *p, Size s1, Size s2);
 
 /*
  * Variants with easier notation and more type safety
@@ -75,17 +75,17 @@ extern void *pg_realloc_mul(void *p, Size s1, Size s2);
 #define pg_realloc_array(pointer, type, count) ((type *) pg_realloc_mul(pointer, sizeof(type), count))
 
 /* Equivalent functions, deliberately named the same as backend functions */
-extern char *pstrdup(const char *in);
-extern char *pnstrdup(const char *in, Size size);
-extern void *palloc(Size size);
-extern void *palloc0(Size size);
+pg_attribute_returns_nonnull extern char *pstrdup(const char *in);
+pg_attribute_returns_nonnull extern char *pnstrdup(const char *in, Size size);
+pg_attribute_returns_nonnull extern void *palloc(Size size);
+pg_attribute_returns_nonnull extern void *palloc0(Size size);
 extern void *palloc_extended(Size size, int flags);
-extern void *repalloc(void *pointer, Size size);
+pg_attribute_returns_nonnull extern void *repalloc(void *pointer, Size size);
 extern void pfree(void *pointer);
-extern void *palloc_mul(Size s1, Size s2);
-extern void *palloc0_mul(Size s1, Size s2);
+pg_attribute_returns_nonnull extern void *palloc_mul(Size s1, Size s2);
+pg_attribute_returns_nonnull extern void *palloc0_mul(Size s1, Size s2);
 extern void *palloc_mul_extended(Size s1, Size s2, int flags);
-extern void *repalloc_mul(void *p, Size s1, Size s2);
+pg_attribute_returns_nonnull extern void *repalloc_mul(void *p, Size s1, Size s2);
 
 #define palloc_object(type) ((type *) palloc(sizeof(type)))
 #define palloc0_object(type) ((type *) palloc0(sizeof(type)))
diff --git a/src/include/utils/palloc.h b/src/include/utils/palloc.h
index 0e934158b60..0c7452fe30d 100644
--- a/src/include/utils/palloc.h
+++ b/src/include/utils/palloc.h
@@ -68,21 +68,21 @@ extern PGDLLIMPORT MemoryContext CurrentMemoryContext;
 /*
  * Fundamental memory-allocation operations (more are in utils/memutils.h)
  */
-extern void *MemoryContextAlloc(MemoryContext context, Size size);
-extern void *MemoryContextAllocZero(MemoryContext context, Size size);
+pg_attribute_returns_nonnull extern void *MemoryContextAlloc(MemoryContext context, Size size);
+pg_attribute_returns_nonnull extern void *MemoryContextAllocZero(MemoryContext context, Size size);
 extern void *MemoryContextAllocExtended(MemoryContext context,
 										Size size, int flags);
 extern void *MemoryContextAllocAligned(MemoryContext context,
 									   Size size, Size alignto, int flags);
 
-extern void *palloc(Size size);
-extern void *palloc0(Size size);
+pg_attribute_returns_nonnull extern void *palloc(Size size);
+pg_attribute_returns_nonnull extern void *palloc0(Size size);
 extern void *palloc_extended(Size size, int flags);
 extern void *palloc_aligned(Size size, Size alignto, int flags);
-pg_nodiscard extern void *repalloc(void *pointer, Size size);
+pg_nodiscard pg_attribute_returns_nonnull extern void *repalloc(void *pointer, Size size);
 pg_nodiscard extern void *repalloc_extended(void *pointer,
 											Size size, int flags);
-pg_nodiscard extern void *repalloc0(void *pointer, Size oldsize, Size size);
+pg_nodiscard pg_attribute_returns_nonnull extern void *repalloc0(void *pointer, Size oldsize, Size size);
 extern void pfree(void *pointer);
 
 /*
@@ -90,10 +90,10 @@ extern void pfree(void *pointer);
  */
 extern Size add_size(Size s1, Size s2);
 extern Size mul_size(Size s1, Size s2);
-extern void *palloc_mul(Size s1, Size s2);
-extern void *palloc0_mul(Size s1, Size s2);
+pg_attribute_returns_nonnull extern void *palloc_mul(Size s1, Size s2);
+pg_attribute_returns_nonnull extern void *palloc0_mul(Size s1, Size s2);
 extern void *palloc_mul_extended(Size s1, Size s2, int flags);
-pg_nodiscard extern void *repalloc_mul(void *p, Size s1, Size s2);
+pg_nodiscard pg_attribute_returns_nonnull extern void *repalloc_mul(void *p, Size s1, Size s2);
 pg_nodiscard extern void *repalloc_mul_extended(void *p, Size s1, Size s2,
 												int flags);
 
@@ -123,8 +123,8 @@ pg_nodiscard extern void *repalloc_mul_extended(void *p, Size s1, Size s2,
 #define repalloc_array_extended(pointer, type, count, flags) ((type *) repalloc_mul_extended(pointer, sizeof(type), count, flags))
 
 /* Higher-limit allocators. */
-extern void *MemoryContextAllocHuge(MemoryContext context, Size size);
-pg_nodiscard extern void *repalloc_huge(void *pointer, Size size);
+pg_attribute_returns_nonnull extern void *MemoryContextAllocHuge(MemoryContext context, Size size);
+pg_nodiscard pg_attribute_returns_nonnull extern void *repalloc_huge(void *pointer, Size size);
 
 /*
  * Although this header file is nominally backend-only, certain frontend
@@ -154,14 +154,14 @@ extern void MemoryContextUnregisterResetCallback(MemoryContext context,
  * These are like standard strdup() except the copied string is
  * allocated in a context, not with malloc().
  */
-extern char *MemoryContextStrdup(MemoryContext context, const char *string);
-extern char *pstrdup(const char *in);
-extern char *pnstrdup(const char *in, Size len);
+pg_attribute_returns_nonnull extern char *MemoryContextStrdup(MemoryContext context, const char *string);
+pg_attribute_returns_nonnull extern char *pstrdup(const char *in);
+pg_attribute_returns_nonnull extern char *pnstrdup(const char *in, Size len);
 
-extern char *pchomp(const char *in);
+pg_attribute_returns_nonnull extern char *pchomp(const char *in);
 
 /* sprintf into a palloc'd buffer --- these are in psprintf.c */
-extern char *psprintf(const char *fmt, ...) pg_attribute_printf(1, 2);
+pg_attribute_returns_nonnull extern char *psprintf(const char *fmt, ...) pg_attribute_printf(1, 2);
 extern size_t pvsnprintf(char *buf, size_t len, const char *fmt, va_list args) pg_attribute_printf(3, 0);
 
 #endif							/* PALLOC_H */
-- 
Tristan Partin
https://tristan.partin.io

Reply via email to