From e25261a90222de75781726a93ab809c660208afd Mon Sep 17 00:00:00 2001
From: Tavian Barnes <tavianator@tavianator.com>
Date: Thu, 9 Nov 2023 15:16:04 -0500
Subject: config: Add (de)allocator attributes

---
 src/alloc.h   | 40 +++++++++++++++++++++++-----------------
 src/config.h  | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/dstring.h | 21 +++++++++++++--------
 tests/alloc.c |  4 ++++
 4 files changed, 89 insertions(+), 25 deletions(-)

diff --git a/src/alloc.h b/src/alloc.h
index fd3e5f0..34f6949 100644
--- a/src/alloc.h
+++ b/src/alloc.h
@@ -9,7 +9,7 @@
 #define BFS_ALLOC_H
 
 #include "config.h"
-#include <stddef.h>
+#include <stdlib.h>
 
 /** Round down to a multiple of an alignment. */
 static inline size_t align_floor(size_t align, size_t size) {
@@ -108,6 +108,8 @@ static inline size_t flex_size(size_t align, size_t min, size_t offset, size_t s
  * @return
  *         The allocated memory, or NULL on failure.
  */
+attr_malloc(free, 1)
+attr_aligned_alloc(1, 2)
 void *alloc(size_t align, size_t size);
 
 /**
@@ -120,6 +122,8 @@ void *alloc(size_t align, size_t size);
  * @return
  *         The allocated memory, or NULL on failure.
  */
+attr_malloc(free, 1)
+attr_aligned_alloc(1, 2)
 void *zalloc(size_t align, size_t size);
 
 /** Allocate memory for the given type. */
@@ -176,14 +180,15 @@ void arena_init(struct arena *arena, size_t align, size_t size);
 	arena_init((arena), alignof(type), sizeof(type))
 
 /**
- * Allocate an object out of the arena.
+ * Free an object from the arena.
  */
-void *arena_alloc(struct arena *arena);
+void arena_free(struct arena *arena, void *ptr);
 
 /**
- * Free an object from the arena.
+ * Allocate an object out of the arena.
  */
-void arena_free(struct arena *arena, void *ptr);
+attr_malloc(arena_free, 2)
+void *arena_alloc(struct arena *arena);
 
 /**
  * Free all allocations from an arena.
@@ -242,6 +247,18 @@ void varena_init(struct varena *varena, size_t align, size_t min, size_t offset,
 #define VARENA_INIT(varena, type, member) \
 	varena_init(varena, alignof(type), sizeof(type), offsetof(type, member), sizeof_member(type, member[0]))
 
+/**
+ * Free an arena-allocated flexible struct.
+ *
+ * @param varena
+ *         The that allocated the object.
+ * @param ptr
+ *         The object to free.
+ * @param count
+ *         The length of the flexible array.
+ */
+void varena_free(struct varena *varena, void *ptr, size_t count);
+
 /**
  * Arena-allocate a flexible struct.
  *
@@ -252,6 +269,7 @@ void varena_init(struct varena *varena, size_t align, size_t min, size_t offset,
  * @return
  *         The allocated struct, or NULL on failure.
  */
+attr_malloc(varena_free, 2)
 void *varena_alloc(struct varena *varena, size_t count);
 
 /**
@@ -284,18 +302,6 @@ void *varena_realloc(struct varena *varena, void *ptr, size_t old_count, size_t
  */
 void *varena_grow(struct varena *varena, void *ptr, size_t *count);
 
-/**
- * Free an arena-allocated flexible struct.
- *
- * @param varena
- *         The that allocated the object.
- * @param ptr
- *         The object to free.
- * @param count
- *         The length of the flexible array.
- */
-void varena_free(struct varena *varena, void *ptr, size_t count);
-
 /**
  * Free all allocations from a varena.
  */
diff --git a/src/config.h b/src/config.h
index b95abaa..db62ef8 100644
--- a/src/config.h
+++ b/src/config.h
@@ -184,6 +184,17 @@
 #  define fallthru ((void)0)
 #endif
 
+/**
+ * Warn if a value is unused.
+ */
+#if __has_c_attribute(nodiscard)
+#  define attr_nodiscard [[nodiscard]]
+#elif __has_attribute(nodiscard)
+#  define attr_nodiscard __attribute__((nodiscard))
+#else
+#  define attr_nodiscard
+#endif
+
 /**
  * Hint to avoid inlining a function.
  */
@@ -211,6 +222,44 @@
 #  define attr_format(fmt, args)
 #endif
 
+/**
+ * Annotates allocator-like functions.
+ */
+#if __has_attribute(malloc)
+#  if __clang__
+#    define attr_malloc(...) attr_nodiscard __attribute__((malloc))
+#  else
+#    define attr_malloc(...) attr_nodiscard __attribute__((malloc(__VA_ARGS__)))
+#  endif
+#else
+#  define attr_malloc(...) attr_nodiscard
+#endif
+
+/**
+ * Specifies that a function returns allocations with a given alignment.
+ */
+#if __has_attribute(alloc_align)
+#  define attr_alloc_align(param) __attribute__((alloc_align(param)))
+#else
+#  define attr_alloc_align(param)
+#endif
+
+/**
+ * Specifies that a function returns allocations with a given size.
+ */
+#if __has_attribute(alloc_size)
+#  define attr_alloc_size(...) __attribute__((alloc_size(__VA_ARGS__)))
+#else
+#  define attr_alloc_size(...)
+#endif
+
+/**
+ * Shorthand for attr_alloc_align() and attr_alloc_size().
+ */
+#define attr_aligned_alloc(align, ...) \
+	attr_alloc_align(align) \
+	attr_alloc_size(__VA_ARGS__)
+
 /**
  * Check if function multiversioning via GNU indirect functions (ifunc) is supported.
  */
diff --git a/src/dstring.h b/src/dstring.h
index fd98df8..07b4ee9 100644
--- a/src/dstring.h
+++ b/src/dstring.h
@@ -27,12 +27,21 @@ typedef __attribute__((aligned(alignof(size_t)))) char dchar;
 typedef char dchar;
 #endif
 
+/**
+ * Free a dynamic string.
+ *
+ * @param dstr
+ *         The string to free.
+ */
+void dstrfree(dchar *dstr);
+
 /**
  * Allocate a dynamic string.
  *
  * @param capacity
  *         The initial capacity of the string.
  */
+attr_malloc(dstrfree, 1)
 dchar *dstralloc(size_t capacity);
 
 /**
@@ -41,6 +50,7 @@ dchar *dstralloc(size_t capacity);
  * @param str
  *         The NUL-terminated string to copy.
  */
+attr_malloc(dstrfree, 1)
 dchar *dstrdup(const char *str);
 
 /**
@@ -51,6 +61,7 @@ dchar *dstrdup(const char *str);
  * @param n
  *         The maximum number of characters to copy from str.
  */
+attr_malloc(dstrfree, 1)
 dchar *dstrndup(const char *str, size_t n);
 
 /**
@@ -59,6 +70,7 @@ dchar *dstrndup(const char *str, size_t n);
  * @param dstr
  *         The dynamic string to copy.
  */
+attr_malloc(dstrfree, 1)
 dchar *dstrddup(const dchar *dstr);
 
 /**
@@ -69,6 +81,7 @@ dchar *dstrddup(const dchar *dstr);
  * @param len
  *         The length of the string, which may include internal NUL bytes.
  */
+attr_malloc(dstrfree, 1)
 dchar *dstrxdup(const char *str, size_t len);
 
 /**
@@ -306,12 +319,4 @@ int dstrescat(dchar **dest, const char *str, enum wesc_flags flags);
  */
 int dstrnescat(dchar **dest, const char *str, size_t n, enum wesc_flags flags);
 
-/**
- * Free a dynamic string.
- *
- * @param dstr
- *         The string to free.
- */
-void dstrfree(dchar *dstr);
-
 #endif // BFS_DSTRING_H
diff --git a/tests/alloc.c b/tests/alloc.c
index 2334241..37b70bf 100644
--- a/tests/alloc.c
+++ b/tests/alloc.c
@@ -24,6 +24,10 @@ int main(void) {
 	bfs_verify(flex_size(8, 16, 4, 4, 1) == 16);
 
 	// Make sure we detect allocation size overflows
+#if __GNUC__ && !__clang__
+#  pragma GCC diagnostic ignored "-Walloc-size-larger-than="
+#endif
+
 	bfs_verify(ALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW);
 	bfs_verify(ZALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW);
 	bfs_verify(ALLOC_FLEX(struct flexible, bar, too_many) == NULL && errno == EOVERFLOW);
-- 
cgit v1.2.3