From a1286df5e39faf4b58535c5a01f0bd69780c2d78 Mon Sep 17 00:00:00 2001
From: Tristan Partin <tristan@partin.io>
Date: Mon, 6 Jul 2026 17:55:00 +0000
Subject: [PATCH v1] Add returns_nonnull attribute to infallible allocators

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 flags
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.

Signed-off-by: Tristan Partin <tristan@partin.io>
---
 src/include/c.h                  | 10 ++++++++++
 src/include/common/fe_memutils.h | 32 +++++++++++++++---------------
 src/include/utils/palloc.h       | 34 ++++++++++++++++----------------
 3 files changed, 43 insertions(+), 33 deletions(-)

diff --git a/src/include/c.h b/src/include/c.h
index 0e4aea5d5a3..8011775c81b 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -285,6 +285,16 @@ extern "C++"
 #define pg_attribute_nonnull(...)
 #endif
 
+/*
+ * pg_attribute_returns_nonnull lets the compiler optimize function callers
+ * based on the knowledge that the return value will never be NULL.
+ */
+#if __has_attribute (returns_nonnull)
+#define pg_attribute_returns_nonnull __attribute__((returns_nonnull))
+#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..c86966a95cc 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);
+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) pg_attribute_returns_nonnull;
 extern void *pg_malloc_extended(size_t size, int flags);
-extern void *pg_realloc(void *ptr, size_t size);
+extern void *pg_realloc(void *ptr, size_t size) pg_attribute_returns_nonnull;
 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);
+extern void *pg_malloc_mul(Size s1, Size s2) pg_attribute_returns_nonnull;
+extern void *pg_malloc0_mul(Size s1, Size s2) pg_attribute_returns_nonnull;
 extern void *pg_malloc_mul_extended(Size s1, Size s2, int flags);
-extern void *pg_realloc_mul(void *p, Size s1, Size s2);
+extern void *pg_realloc_mul(void *p, Size s1, Size s2) pg_attribute_returns_nonnull;
 
 /*
  * 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);
+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) pg_attribute_returns_nonnull;
 extern void *palloc_extended(Size size, int flags);
-extern void *repalloc(void *pointer, Size size);
+extern void *repalloc(void *pointer, Size size) pg_attribute_returns_nonnull;
 extern void pfree(void *pointer);
-extern void *palloc_mul(Size s1, Size s2);
-extern void *palloc0_mul(Size s1, Size s2);
+extern void *palloc_mul(Size s1, Size s2) pg_attribute_returns_nonnull;
+extern void *palloc0_mul(Size s1, Size s2) pg_attribute_returns_nonnull;
 extern void *palloc_mul_extended(Size s1, Size s2, int flags);
-extern void *repalloc_mul(void *p, Size s1, Size s2);
+extern void *repalloc_mul(void *p, Size s1, Size s2) pg_attribute_returns_nonnull;
 
 #define palloc_object(type) ((type *) palloc(sizeof(type)))
 #define palloc0_object(type) ((type *) palloc0(sizeof(type)))
@@ -95,7 +95,7 @@ extern void *repalloc_mul(void *p, Size s1, Size s2);
 #define repalloc_array(pointer, type, count) ((type *) repalloc_mul(pointer, sizeof(type), count))
 
 /* sprintf into a palloc'd buffer --- these are in psprintf.c */
-extern char *psprintf(const char *fmt, ...) pg_attribute_printf(1, 2);
+extern char *psprintf(const char *fmt, ...) pg_attribute_printf(1, 2) pg_attribute_returns_nonnull;
 extern size_t pvsnprintf(char *buf, size_t len, const char *fmt, va_list args) pg_attribute_printf(3, 0);
 
 #endif							/* FE_MEMUTILS_H */
diff --git a/src/include/utils/palloc.h b/src/include/utils/palloc.h
index 0e934158b60..3496b375433 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);
+extern void *MemoryContextAlloc(MemoryContext context, Size size) pg_attribute_returns_nonnull;
+extern void *MemoryContextAllocZero(MemoryContext context, Size size) pg_attribute_returns_nonnull;
 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);
+extern void *palloc(Size size) pg_attribute_returns_nonnull;
+extern void *palloc0(Size size) pg_attribute_returns_nonnull;
 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 extern void *repalloc(void *pointer, Size size) pg_attribute_returns_nonnull;
 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 extern void *repalloc0(void *pointer, Size oldsize, Size size) pg_attribute_returns_nonnull;
 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);
-extern void *palloc_mul_extended(Size s1, Size s2, int flags);
-pg_nodiscard extern void *repalloc_mul(void *p, Size s1, Size s2);
+extern void *palloc_mul(Size s1, Size s2) pg_attribute_returns_nonnull;
+extern void *palloc0_mul(Size s1, Size s2) pg_attribute_returns_nonnull;
+extern void *palloc_mul_extended(Size s1, Size s2, int flags) pg_attribute_returns_nonnull;
+pg_nodiscard extern void *repalloc_mul(void *p, Size s1, Size s2) pg_attribute_returns_nonnull;
 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);
+extern void *MemoryContextAllocHuge(MemoryContext context, Size size) pg_attribute_returns_nonnull;
+pg_nodiscard extern void *repalloc_huge(void *pointer, Size size) pg_attribute_returns_nonnull;
 
 /*
  * 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);
+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) pg_attribute_returns_nonnull;
 
-extern char *pchomp(const char *in);
+extern char *pchomp(const char *in) pg_attribute_returns_nonnull;
 
 /* sprintf into a palloc'd buffer --- these are in psprintf.c */
-extern char *psprintf(const char *fmt, ...) pg_attribute_printf(1, 2);
+extern char *psprintf(const char *fmt, ...) pg_attribute_printf(1, 2) pg_attribute_returns_nonnull;
 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

