https://github.com/a-nogikh updated 
https://github.com/llvm/llvm-project/pull/167010

>From e0fa07d42042a391fc92c71e6e2e378dd58a775b Mon Sep 17 00:00:00 2001
From: Aleksandr Nogikh <[email protected]>
Date: Fri, 17 Oct 2025 18:05:36 +0200
Subject: [PATCH 1/2] [Clang] Introduce malloc_span attribute

The "malloc" attribute restricts the possible function signatures to
the ones returning a pointer, which is not the case for some non-standard
allocation function variants. For example, P0901R11 proposed ::operator new
overloads that return a return_size_t result - a struct that contains
a pointer to the allocated memory as well as the actual size of the
allocated memory. Another example is __size_returning_new.

Introduce a new "malloc_span" attribute that exhibits similar semantics,
but applies to functions returning records whose first member is
a pointer (assumed to point to the allocated memory). This is the case for
return_size_t as well as std::span, should it be returned from such
an annotated function.

An alternative approach would be to relax the restrictions of the
existing "malloc" attribute to be applied to both functions returning
pointers and functions returning span-like structs. However, it would
complicate the user-space code by requiring specific Clang version
checks. In contrast, the presence of a new attribute can be
straightforwardly verified via the __has_attribute macro. Introducing
a new attribute also avoids concerns about the potential incompatibility
with GCC's "malloc" semantics.

In future commits, codegen can be improved to recognize the
noalias-ness of the pointer returned inside a span-like struct.

This change helps unlock the alloc token instrumentation for such
non-standard allocation functions:
https://clang.llvm.org/docs/AllocToken.html#instrumenting-non-standard-allocation-functions
---
 clang/docs/ReleaseNotes.rst                   |  4 +++
 clang/include/clang/Basic/Attr.td             |  6 ++++
 clang/include/clang/Basic/AttrDocs.td         | 15 ++++++++
 .../clang/Basic/DiagnosticSemaKinds.td        |  4 +++
 clang/lib/Sema/SemaDeclAttr.cpp               | 35 +++++++++++++++++++
 ...a-attribute-supported-attributes-list.test |  1 +
 clang/test/Sema/attr-malloc_span.c            | 31 ++++++++++++++++
 7 files changed, 96 insertions(+)
 create mode 100644 clang/test/Sema/attr-malloc_span.c

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index e8339fa13ffba..ed23af353494a 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -327,6 +327,10 @@ Attribute Changes in Clang
 - New format attributes ``gnu_printf``, ``gnu_scanf``, ``gnu_strftime`` and 
``gnu_strfmon`` are added
   as aliases for ``printf``, ``scanf``, ``strftime`` and ``strfmon``. 
(#GH16219)
 
+- New function attribute `malloc_span` is added. Its semantics is similar to 
that of the `malloc`
+  attribute, but `malloc_span` applies not to functions returning pointers, 
but to functions returning
+  span-like structures (i.e. those that contain a pointer field and a size 
integer field).
+
 Improvements to Clang's diagnostics
 -----------------------------------
 - Diagnostics messages now refer to ``structured binding`` instead of 
``decomposition``,
diff --git a/clang/include/clang/Basic/Attr.td 
b/clang/include/clang/Basic/Attr.td
index 1013bfc575747..27987b4da3cc9 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -2068,6 +2068,12 @@ def Restrict : InheritableAttr {
   let Documentation = [RestrictDocs];
 }
 
+def MallocSpan : InheritableAttr {
+  let Spellings = [Clang<"malloc_span">];
+  let Subjects = SubjectList<[Function]>;
+  let Documentation = [MallocSpanDocs];
+}
+
 def LayoutVersion : InheritableAttr, 
TargetSpecificAttr<TargetMicrosoftRecordLayout> {
   let Spellings = [Declspec<"layout_version">];
   let Args = [UnsignedArgument<"Version">];
diff --git a/clang/include/clang/Basic/AttrDocs.td 
b/clang/include/clang/Basic/AttrDocs.td
index 1be9a96aa44de..a3556790ff07b 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -5247,6 +5247,21 @@ yet implemented in clang.
   }];
 }
 
+def MallocSpanDocs : Documentation {
+  let Category = DocCatFunction;
+  let Heading = "malloc_span";
+  let Content = [{
+The ``malloc_span`` attribute can be used to mark that a function which acts
+like a system memory allocation function and returns a span-like structure,
+where the returned memory range does not alias storage from any other object
+accessible to the caller.
+
+In this context, a span-like structure is assumed to have a pointer to the
+allocated memory as its first field and any integer type containing the size
+of the actually allocated memory as the second field.
+  }];
+}
+
 def ReturnsNonNullDocs : Documentation {
   let Category = NullabilityDocs;
   let Content = [{
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 04f2e8d654fd5..7687eb5ca0ec6 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3449,6 +3449,10 @@ def err_attribute_integers_only : Error<
 def warn_attribute_return_pointers_only : Warning<
   "%0 attribute only applies to return values that are pointers">,
   InGroup<IgnoredAttributes>;
+def warn_attribute_return_span_only
+    : Warning<"%0 attribute only applies to return values that are span-like "
+              "structures">,
+      InGroup<IgnoredAttributes>;
 def warn_attribute_return_pointers_refs_only : Warning<
   "%0 attribute only applies to return values that are pointers or 
references">,
   InGroup<IgnoredAttributes>;
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index a9e7b44ac9d73..4fdfc9c13c05b 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1839,6 +1839,38 @@ static void handleRestrictAttr(Sema &S, Decl *D, const 
ParsedAttr &AL) {
                  RestrictAttr(S.Context, AL, DeallocE, DeallocPtrIdx));
 }
 
+static bool isSpanLikeType(const QualType &Ty) {
+  // Check that the type is a plain record with the first field being a pointer
+  // type and the second field being an integer. This matches the common
+  // implementation of std::span or sized_allocation_t in P0901R11.
+  // Note that there may also be numerous cases of pointer+integer structures
+  // not actually exhibiting a span-like semantics, so sometimes
+  // this heuristic expectedly leads to false positive results.
+  const RecordDecl *RD = Ty->getAsRecordDecl();
+  if (!RD || RD->isUnion())
+    return false;
+  const RecordDecl *Def = RD->getDefinition();
+  if (!Def)
+    return false; // This is an incomplete type.
+  auto FieldsBegin = Def->field_begin();
+  if (std::distance(FieldsBegin, Def->field_end()) != 2)
+    return false;
+  const FieldDecl *FirstField = *FieldsBegin;
+  const FieldDecl *SecondField = *std::next(FieldsBegin);
+  return FirstField->getType()->isAnyPointerType() &&
+         SecondField->getType()->isIntegerType();
+}
+
+static void handleMallocSpanAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+  QualType ResultType = getFunctionOrMethodResultType(D);
+  if (!isSpanLikeType(ResultType)) {
+    S.Diag(AL.getLoc(), diag::warn_attribute_return_span_only)
+        << AL << getFunctionOrMethodResultSourceRange(D);
+    return;
+  }
+  D->addAttr(::new (S.Context) MallocSpanAttr(S.Context, AL));
+}
+
 static void handleCPUSpecificAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   // Ensure we don't combine these with themselves, since that causes some
   // confusing behavior.
@@ -7278,6 +7310,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, 
const ParsedAttr &AL,
   case ParsedAttr::AT_Restrict:
     handleRestrictAttr(S, D, AL);
     break;
+  case ParsedAttr::AT_MallocSpan:
+    handleMallocSpanAttr(S, D, AL);
+    break;
   case ParsedAttr::AT_Mode:
     handleModeAttr(S, D, AL);
     break;
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test 
b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index ab4153a64f028..747eb17446c87 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -102,6 +102,7 @@
 // CHECK-NEXT: MIGServerRoutine (SubjectMatchRule_function, 
SubjectMatchRule_objc_method, SubjectMatchRule_block)
 // CHECK-NEXT: MSConstexpr (SubjectMatchRule_function)
 // CHECK-NEXT: MSStruct (SubjectMatchRule_record)
+// CHECK-NEXT: MallocSpan (SubjectMatchRule_function)
 // CHECK-NEXT: MaybeUndef (SubjectMatchRule_variable_is_parameter)
 // CHECK-NEXT: MicroMips (SubjectMatchRule_function)
 // CHECK-NEXT: MinSize (SubjectMatchRule_function, 
SubjectMatchRule_objc_method)
diff --git a/clang/test/Sema/attr-malloc_span.c 
b/clang/test/Sema/attr-malloc_span.c
new file mode 100644
index 0000000000000..05f29ccf6dd83
--- /dev/null
+++ b/clang/test/Sema/attr-malloc_span.c
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -verify -fsyntax-only %s
+// RUN: %clang_cc1 -emit-llvm -o %t %s
+
+#include <stddef.h>
+
+typedef struct {
+  void *ptr;
+  size_t n;
+} sized_ptr;
+sized_ptr  returns_sized_ptr  (void) __attribute((malloc_span)); // no-warning
+
+// The first struct field must be pointer and the second must be an integer.
+// Check the possible ways to violate it.
+typedef struct {
+  size_t n;
+  void *ptr;
+} invalid_span1;
+invalid_span1  returns_non_std_span1  (void) __attribute((malloc_span)); // 
expected-warning {{attribute only applies to return values that are span-like 
structures}}
+
+typedef struct {
+  void *ptr;
+  void *ptr2;
+} invalid_span2;
+invalid_span2  returns_non_std_span2  (void) __attribute((malloc_span)); // 
expected-warning {{attribute only applies to return values that are span-like 
structures}}
+
+typedef struct {
+  void *ptr;
+  size_t n;
+  size_t n2;
+} invalid_span3;
+invalid_span3  returns_non_std_span3  (void) __attribute((malloc_span)); // 
expected-warning {{attribute only applies to return values that are span-like 
structures}}

>From 90c8120ccfbb061dbe25feedac8f89fd255cfff8 Mon Sep 17 00:00:00 2001
From: Aleksandr Nogikh <[email protected]>
Date: Tue, 11 Nov 2025 08:48:27 +0000
Subject: [PATCH 2/2] fixup: support different span struct field orderings

Do not demand that the pointer should be the first field and the integer
type the second field.
---
 clang/lib/Sema/SemaDeclAttr.cpp    | 15 +++++++++------
 clang/test/Sema/attr-malloc_span.c | 20 ++++++++++----------
 2 files changed, 19 insertions(+), 16 deletions(-)

diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 4fdfc9c13c05b..dfe2ed88e216f 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1840,8 +1840,8 @@ static void handleRestrictAttr(Sema &S, Decl *D, const 
ParsedAttr &AL) {
 }
 
 static bool isSpanLikeType(const QualType &Ty) {
-  // Check that the type is a plain record with the first field being a pointer
-  // type and the second field being an integer. This matches the common
+  // Check that the type is a plain record with one field being a pointer
+  // type and the other field being an integer. This matches the common
   // implementation of std::span or sized_allocation_t in P0901R11.
   // Note that there may also be numerous cases of pointer+integer structures
   // not actually exhibiting a span-like semantics, so sometimes
@@ -1855,10 +1855,13 @@ static bool isSpanLikeType(const QualType &Ty) {
   auto FieldsBegin = Def->field_begin();
   if (std::distance(FieldsBegin, Def->field_end()) != 2)
     return false;
-  const FieldDecl *FirstField = *FieldsBegin;
-  const FieldDecl *SecondField = *std::next(FieldsBegin);
-  return FirstField->getType()->isAnyPointerType() &&
-         SecondField->getType()->isIntegerType();
+  const QualType FirstFieldType = FieldsBegin->getType();
+  const QualType SecondFieldType = std::next(FieldsBegin)->getType();
+  // Verify two possible orderings.
+  return (FirstFieldType->isAnyPointerType() &&
+          SecondFieldType->isIntegerType()) ||
+         (FirstFieldType->isIntegerType() &&
+          SecondFieldType->isAnyPointerType());
 }
 
 static void handleMallocSpanAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
diff --git a/clang/test/Sema/attr-malloc_span.c 
b/clang/test/Sema/attr-malloc_span.c
index 05f29ccf6dd83..245de9d1af4ad 100644
--- a/clang/test/Sema/attr-malloc_span.c
+++ b/clang/test/Sema/attr-malloc_span.c
@@ -6,26 +6,26 @@
 typedef struct {
   void *ptr;
   size_t n;
-} sized_ptr;
-sized_ptr  returns_sized_ptr  (void) __attribute((malloc_span)); // no-warning
+} span;
+span  returns_span  (void) __attribute((malloc_span)); // no-warning
 
-// The first struct field must be pointer and the second must be an integer.
-// Check the possible ways to violate it.
+// Try out a different field ordering.
 typedef struct {
   size_t n;
   void *ptr;
-} invalid_span1;
-invalid_span1  returns_non_std_span1  (void) __attribute((malloc_span)); // 
expected-warning {{attribute only applies to return values that are span-like 
structures}}
+} span2;
+span2  returns_span2  (void) __attribute((malloc_span)); // no-warning
 
+// Ensure that a warning is produced on malloc_span precondition violation.
 typedef struct {
   void *ptr;
   void *ptr2;
-} invalid_span2;
-invalid_span2  returns_non_std_span2  (void) __attribute((malloc_span)); // 
expected-warning {{attribute only applies to return values that are span-like 
structures}}
+} invalid_span1;
+invalid_span1  returns_non_std_span1  (void) __attribute((malloc_span)); // 
expected-warning {{attribute only applies to return values that are span-like 
structures}}
 
 typedef struct {
   void *ptr;
   size_t n;
   size_t n2;
-} invalid_span3;
-invalid_span3  returns_non_std_span3  (void) __attribute((malloc_span)); // 
expected-warning {{attribute only applies to return values that are span-like 
structures}}
+} invalid_span2;
+invalid_span2  returns_non_std_span2  (void) __attribute((malloc_span)); // 
expected-warning {{attribute only applies to return values that are span-like 
structures}}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to