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

>From 2d0bf7d9a584cefda1e7a9435e345e13c24e40ed Mon Sep 17 00:00:00 2001
From: Aleksandr Nogikh <[email protected]>
Date: Fri, 17 Oct 2025 18:05:36 +0200
Subject: [PATCH 01/20] [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 c2da61e4d066a..369c0b53b371e 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -353,6 +353,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 0097476bc0d8d..8e5f7ef0bb82d 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -2072,6 +2072,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 4813191d2d602..d5c3756b11d0b 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 a6e60fe4692ee..e67c36c096f5b 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 bda7aa32a9348..7288cb439122c 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.
@@ -7268,6 +7300,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 54f97fc0d9cd4a61cba5144b8b2dbd0252aae4f8 Mon Sep 17 00:00:00 2001
From: Aleksandr Nogikh <[email protected]>
Date: Tue, 11 Nov 2025 08:48:27 +0000
Subject: [PATCH 02/20] 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/include/clang/Basic/AttrDocs.td |  6 +++---
 clang/lib/Sema/SemaDeclAttr.cpp       | 15 +++++++++------
 clang/test/Sema/attr-malloc_span.c    | 20 ++++++++++----------
 3 files changed, 22 insertions(+), 19 deletions(-)

diff --git a/clang/include/clang/Basic/AttrDocs.td 
b/clang/include/clang/Basic/AttrDocs.td
index d5c3756b11d0b..470fb0344ef5d 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -5256,9 +5256,9 @@ 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.
+In this context, a span-like structure is assumed to have two fields, one of
+which is a pointer to the allocated memory and another one is an integer type
+containing the size of the actually allocated memory.
   }];
 }
 
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 7288cb439122c..068939fd1b0a4 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}}

>From e54fb3ee923eb48cc291a96ed604bccbf46254f9 Mon Sep 17 00:00:00 2001
From: Aleksandr Nogikh <[email protected]>
Date: Thu, 13 Nov 2025 10:50:42 +0000
Subject: [PATCH 03/20] fixup: minor update to release notes

---
 clang/docs/ReleaseNotes.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 369c0b53b371e..760b2b18ece2b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -353,7 +353,7 @@ 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`
+- New function attribute `malloc_span` is added. It has semantics 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).
 

>From e89cee48eaa986a52820191e8021cc5b99650747 Mon Sep 17 00:00:00 2001
From: Aleksandr Nogikh <[email protected]>
Date: Thu, 13 Nov 2025 10:51:09 +0000
Subject: [PATCH 04/20] fixup: expand the diagnostic message

---
 clang/include/clang/Basic/DiagnosticSemaKinds.td | 5 +++--
 clang/test/Sema/attr-malloc_span.c               | 6 +++---
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e67c36c096f5b..2b7aba7776885 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3450,8 +3450,9 @@ 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">,
+    : Warning<"%0 attribute only applies to functions that return span-like 
structures: "
+    "one field is a pointer to the allocated memory and another field is an 
integer with "
+    "the size of the allocated memory">,
       InGroup<IgnoredAttributes>;
 def warn_attribute_return_pointers_refs_only : Warning<
   "%0 attribute only applies to return values that are pointers or 
references">,
diff --git a/clang/test/Sema/attr-malloc_span.c 
b/clang/test/Sema/attr-malloc_span.c
index 245de9d1af4ad..65b60bd03e49e 100644
--- a/clang/test/Sema/attr-malloc_span.c
+++ b/clang/test/Sema/attr-malloc_span.c
@@ -1,7 +1,7 @@
 // RUN: %clang_cc1 -verify -fsyntax-only %s
 // RUN: %clang_cc1 -emit-llvm -o %t %s
 
-#include <stddef.h>
+typedef __SIZE_TYPE__ size_t;
 
 typedef struct {
   void *ptr;
@@ -21,11 +21,11 @@ typedef struct {
   void *ptr;
   void *ptr2;
 } 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}}
+invalid_span1  returns_non_std_span1  (void) __attribute((malloc_span)); // 
expected-warning {{attribute only applies to functions that return span-like 
structures}}
 
 typedef struct {
   void *ptr;
   size_t n;
   size_t n2;
 } 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_span2  returns_non_std_span2  (void) __attribute((malloc_span)); // 
expected-warning {{attribute only applies to functions that return span-like 
structures}}

>From a8018b8fb549640994f4619f01c427060f6b7e43 Mon Sep 17 00:00:00 2001
From: Aleksandr Nogikh <[email protected]>
Date: Thu, 13 Nov 2025 10:51:43 +0000
Subject: [PATCH 05/20] fixup: isSpanLikeType -> checkSpanLikeType

---
 clang/lib/Sema/SemaDeclAttr.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 068939fd1b0a4..1d22621f42666 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1839,7 +1839,7 @@ static void handleRestrictAttr(Sema &S, Decl *D, const 
ParsedAttr &AL) {
                  RestrictAttr(S.Context, AL, DeallocE, DeallocPtrIdx));
 }
 
-static bool isSpanLikeType(const QualType &Ty) {
+static bool checkSpanLikeType(const QualType &Ty) {
   // 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.
@@ -1866,7 +1866,7 @@ static bool isSpanLikeType(const QualType &Ty) {
 
 static void handleMallocSpanAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   QualType ResultType = getFunctionOrMethodResultType(D);
-  if (!isSpanLikeType(ResultType)) {
+  if (!checkSpanLikeType(ResultType)) {
     S.Diag(AL.getLoc(), diag::warn_attribute_return_span_only)
         << AL << getFunctionOrMethodResultSourceRange(D);
     return;

>From fd68c1de16d3d39a5c58193133a37f0f6f0f14cf Mon Sep 17 00:00:00 2001
From: Aleksandr Nogikh <[email protected]>
Date: Thu, 13 Nov 2025 14:14:25 +0000
Subject: [PATCH 06/20] fixup: more restrictions on the pointer type

Disallow function pointers.
Add tests to verify that member/data member pointers also fail.
---
 clang/lib/Sema/SemaDeclAttr.cpp         |  8 ++++++--
 clang/test/Sema/attr-malloc_span.c      |  7 +++++++
 clang/test/SemaCXX/attr-malloc_span.cpp | 27 +++++++++++++++++++++++++
 3 files changed, 40 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/SemaCXX/attr-malloc_span.cpp

diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 1d22621f42666..7358d626bb7d5 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1857,11 +1857,15 @@ static bool checkSpanLikeType(const QualType &Ty) {
     return false;
   const QualType FirstFieldType = FieldsBegin->getType();
   const QualType SecondFieldType = std::next(FieldsBegin)->getType();
+  auto validatePointerType = [](const QualType &T) {
+    // It must not point to functions.
+    return T->isPointerType() && !T->isFunctionPointerType();
+  };
   // Verify two possible orderings.
-  return (FirstFieldType->isAnyPointerType() &&
+  return (validatePointerType(FirstFieldType) &&
           SecondFieldType->isIntegerType()) ||
          (FirstFieldType->isIntegerType() &&
-          SecondFieldType->isAnyPointerType());
+          validatePointerType(SecondFieldType));
 }
 
 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 65b60bd03e49e..c1a8e9e08a77d 100644
--- a/clang/test/Sema/attr-malloc_span.c
+++ b/clang/test/Sema/attr-malloc_span.c
@@ -29,3 +29,10 @@ typedef struct {
   size_t n2;
 } invalid_span2;
 invalid_span2  returns_non_std_span2  (void) __attribute((malloc_span)); // 
expected-warning {{attribute only applies to functions that return span-like 
structures}}
+
+// Function pointers are not allowed.
+typedef struct {
+  int (*func_ptr)(void);
+  size_t n;
+} func_span;
+func_span  returns_func_span  (void) __attribute((malloc_span)); // 
expected-warning {{attribute only applies to functions that return span-like 
structures}}
diff --git a/clang/test/SemaCXX/attr-malloc_span.cpp 
b/clang/test/SemaCXX/attr-malloc_span.cpp
new file mode 100644
index 0000000000000..c70a8c3a1a755
--- /dev/null
+++ b/clang/test/SemaCXX/attr-malloc_span.cpp
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+
+class SomeClass {
+public:
+  int Data;
+};
+
+// Returning pointers to data members is not allowed.
+struct DataMemberSpan {
+  int SomeClass::* member_ptr;
+  int n;
+};
+
+DataMemberSpan returns_data_member_span(void) __attribute((malloc_span)) { // 
expected-warning {{attribute only applies to functions that return span-like 
structures}}
+  return DataMemberSpan{};
+}
+
+// Returning pointers to member functions is not allowed.
+struct MemberFuncSpan {
+  void (SomeClass::*member_func_ptr)();
+  int n;
+};
+
+MemberFuncSpan returns_member_func_span(void) __attribute((malloc_span)) { // 
expected-warning {{attribute only applies to functions that return span-like 
structures}}
+  return MemberFuncSpan{};
+}
+

>From 1474a10145e4336ca635bb6410e201a9d83c4c41 Mon Sep 17 00:00:00 2001
From: Aleksandr Nogikh <[email protected]>
Date: Thu, 13 Nov 2025 17:59:12 +0000
Subject: [PATCH 07/20] fixup: rework malloc_span validation and error
 reporting

---
 .../clang/Basic/DiagnosticSemaKinds.td        | 13 +++-
 clang/lib/Sema/SemaDeclAttr.cpp               | 63 ++++++++++++++-----
 clang/test/Sema/attr-malloc_span.c            | 46 ++++++++++++--
 clang/test/SemaCXX/attr-malloc_span.cpp       |  8 ++-
 4 files changed, 102 insertions(+), 28 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 2b7aba7776885..919b92a9616cd 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3450,10 +3450,17 @@ 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 functions that return span-like 
structures: "
-    "one field is a pointer to the allocated memory and another field is an 
integer with "
-    "the size of the allocated memory">,
+    : Warning<"%0 attribute only applies to functions that return span-like "
+              "structures">,
       InGroup<IgnoredAttributes>;
+def note_span_must_be_struct : Note<"span-like type must be a struct">;
+def note_span_must_be_complete : Note<"span-like type must be complete">;
+def note_wrong_span_field_count : Note<"span-like type must have 2 fields">;
+def note_wrong_span_field_types
+    : Note<"span-like type must have a pointer and an integer field or two "
+           "pointer fields">;
+def note_span_invalid_integer : Note<"the integer field must be an actual "
+                                     "integer that is at least as big as int">;
 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 7358d626bb7d5..c090cee3f7d1e 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1839,43 +1839,72 @@ static void handleRestrictAttr(Sema &S, Decl *D, const 
ParsedAttr &AL) {
                  RestrictAttr(S.Context, AL, DeallocE, DeallocPtrIdx));
 }
 
-static bool checkSpanLikeType(const QualType &Ty) {
+static bool checkSpanLikeType(Sema &S, const ParsedAttr &AL,
+                              const QualType &Ty) {
   // 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
-  // this heuristic expectedly leads to false positive results.
+  // Note that there may also be numerous cases of pointer + integer /
+  // pointer + pointer / integer + pointer structures not actually exhibiting
+  // a span-like semantics, so sometimes these heuristics expectedly
+  // lead to false positive results.
+  auto emitWarning = [&S, &AL](unsigned NoteDiagID) {
+    S.Diag(AL.getLoc(), diag::warn_attribute_return_span_only) << AL;
+    S.Diag(AL.getLoc(), NoteDiagID);
+  };
   const RecordDecl *RD = Ty->getAsRecordDecl();
-  if (!RD || RD->isUnion())
+  if (!RD || RD->isUnion()) {
+    emitWarning(diag::note_span_must_be_struct);
     return false;
+  }
   const RecordDecl *Def = RD->getDefinition();
-  if (!Def)
+  if (!Def) {
+    emitWarning(diag::note_span_must_be_complete);
     return false; // This is an incomplete type.
+  }
   auto FieldsBegin = Def->field_begin();
-  if (std::distance(FieldsBegin, Def->field_end()) != 2)
+  if (std::distance(FieldsBegin, Def->field_end()) != 2) {
+    emitWarning(diag::note_wrong_span_field_count);
     return false;
+  }
   const QualType FirstFieldType = FieldsBegin->getType();
   const QualType SecondFieldType = std::next(FieldsBegin)->getType();
   auto validatePointerType = [](const QualType &T) {
     // It must not point to functions.
     return T->isPointerType() && !T->isFunctionPointerType();
   };
-  // Verify two possible orderings.
-  return (validatePointerType(FirstFieldType) &&
-          SecondFieldType->isIntegerType()) ||
-         (FirstFieldType->isIntegerType() &&
-          validatePointerType(SecondFieldType));
+  auto checkIntegerType = [&S, emitWarning](const QualType &T) {
+    bool valid = false;
+    // Must be an actual integer and at least as bit as int.
+    if (const auto *BT = dyn_cast<BuiltinType>(T.getCanonicalType())) {
+      const auto IntSize = S.Context.getTypeSize(S.Context.IntTy);
+      valid = BT->isInteger() && S.Context.getTypeSize(BT) >= IntSize;
+    }
+    if (!valid) {
+      emitWarning(diag::note_span_invalid_integer);
+    }
+    return valid;
+  };
+  if (validatePointerType(FirstFieldType) &&
+      validatePointerType(SecondFieldType)) {
+    // Pointer + pointer.
+    return true;
+  } else if (validatePointerType(FirstFieldType)) {
+    // Pointer + integer?
+    return checkIntegerType(SecondFieldType);
+  } else if (validatePointerType(SecondFieldType)) {
+    // Integer + pointer?
+    return checkIntegerType(FirstFieldType);
+  }
+  emitWarning(diag::note_wrong_span_field_types);
+  return false;
 }
 
 static void handleMallocSpanAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   QualType ResultType = getFunctionOrMethodResultType(D);
-  if (!checkSpanLikeType(ResultType)) {
-    S.Diag(AL.getLoc(), diag::warn_attribute_return_span_only)
-        << AL << getFunctionOrMethodResultSourceRange(D);
-    return;
+  if (checkSpanLikeType(S, AL, ResultType)) {
+    D->addAttr(::new (S.Context) MallocSpanAttr(S.Context, AL));
   }
-  D->addAttr(::new (S.Context) MallocSpanAttr(S.Context, AL));
 }
 
 static void handleCPUSpecificAttr(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 c1a8e9e08a77d..8203e9df4d330 100644
--- a/clang/test/Sema/attr-malloc_span.c
+++ b/clang/test/Sema/attr-malloc_span.c
@@ -16,23 +16,57 @@ typedef struct {
 } 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_span1;
-invalid_span1  returns_non_std_span1  (void) __attribute((malloc_span)); // 
expected-warning {{attribute only applies to functions that return span-like 
structures}}
+} span3;
+span3  returns_span3  (void) __attribute((malloc_span)); // no-warning
+// expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
+// expected-note@+1 {{span-like type must be a struct}}
+int *returns_int_ptr  (void) __attribute((malloc_span));
 
 typedef struct {
   void *ptr;
   size_t n;
   size_t n2;
-} invalid_span2;
-invalid_span2  returns_non_std_span2  (void) __attribute((malloc_span)); // 
expected-warning {{attribute only applies to functions that return span-like 
structures}}
+} too_long_span;
+// expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
+// expected-note@+1 {{span-like type must have 2 fields}}
+too_long_span  returns_too_long_span  (void) __attribute((malloc_span));
 
 // Function pointers are not allowed.
 typedef struct {
   int (*func_ptr)(void);
   size_t n;
 } func_span;
-func_span  returns_func_span  (void) __attribute((malloc_span)); // 
expected-warning {{attribute only applies to functions that return span-like 
structures}}
+// expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
+// expected-note@+1 {{span-like type must have a pointer and an integer field 
or two pointer fields}}
+func_span  returns_func_span  (void) __attribute((malloc_span));
+
+// Integer should not be an enum.
+enum some_enum { some_value, other_value };
+typedef struct {
+  void *ptr;
+  enum some_enum field;
+} enum_span;
+// expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
+// expected-note@+1 {{the integer field must be an actual integer}}
+enum_span  returns_enum_span  (void) __attribute((malloc_span));
+
+// Bit integers are also not supported.
+typedef struct {
+  void *ptr;
+  _BitInt(16) n;
+} bit_span;
+// expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
+// expected-note@+1 {{the integer field must be an actual integer}}
+bit_span  returns_bit_span  (void) __attribute((malloc_span));
+
+// Integer must be at least as big as int.
+typedef struct {
+  void *ptr;
+  short n;
+} short_span;
+// expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
+// expected-note@+1 {{the integer field must be an actual integer}}
+short_span  returns_short_span  (void) __attribute((malloc_span));
diff --git a/clang/test/SemaCXX/attr-malloc_span.cpp 
b/clang/test/SemaCXX/attr-malloc_span.cpp
index c70a8c3a1a755..8748038f8d133 100644
--- a/clang/test/SemaCXX/attr-malloc_span.cpp
+++ b/clang/test/SemaCXX/attr-malloc_span.cpp
@@ -11,7 +11,9 @@ struct DataMemberSpan {
   int n;
 };
 
-DataMemberSpan returns_data_member_span(void) __attribute((malloc_span)) { // 
expected-warning {{attribute only applies to functions that return span-like 
structures}}
+// expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
+// expected-note@+1 {{span-like type must have a pointer and an integer field 
or two pointer fields}}
+DataMemberSpan returns_data_member_span(void) __attribute((malloc_span)) {
   return DataMemberSpan{};
 }
 
@@ -21,7 +23,9 @@ struct MemberFuncSpan {
   int n;
 };
 
-MemberFuncSpan returns_member_func_span(void) __attribute((malloc_span)) { // 
expected-warning {{attribute only applies to functions that return span-like 
structures}}
+// expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
+// expected-note@+1 {{span-like type must have a pointer and an integer field 
or two pointer fields}}
+MemberFuncSpan returns_member_func_span(void) __attribute((malloc_span)) {
   return MemberFuncSpan{};
 }
 

>From 9f81371ee5198d8ef869379c638010bd7b6d8824 Mon Sep 17 00:00:00 2001
From: Aleksandr Nogikh <[email protected]>
Date: Thu, 13 Nov 2025 18:50:27 +0000
Subject: [PATCH 08/20] fixup: update documentation

Reflect that pointer+pointer is also possible now.
---
 clang/docs/ReleaseNotes.rst           | 2 +-
 clang/include/clang/Basic/AttrDocs.td | 5 +++--
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 760b2b18ece2b..e69a33438ab7b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -355,7 +355,7 @@ Attribute Changes in Clang
 
 - New function attribute `malloc_span` is added. It has semantics 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).
+  span-like structures (i.e. those that contain a pointer field and a size 
integer field or two pointers).
 
 Improvements to Clang's diagnostics
 -----------------------------------
diff --git a/clang/include/clang/Basic/AttrDocs.td 
b/clang/include/clang/Basic/AttrDocs.td
index 470fb0344ef5d..786444ba4d653 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -5257,8 +5257,9 @@ 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 two fields, one of
-which is a pointer to the allocated memory and another one is an integer type
-containing the size of the actually allocated memory.
+which is a pointer to the start of theallocated memory and another one is
+either an integer type containing the size of the actually allocated memory
+or a pointer the end of the allocated region.
   }];
 }
 

>From 955ac6268e192e66329814fb8092c3ff6db16807 Mon Sep 17 00:00:00 2001
From: Aleksandr Nogikh <[email protected]>
Date: Fri, 14 Nov 2025 11:20:40 +0000
Subject: [PATCH 09/20] fixup: refactor semantic checks

Change error descriptions and restructure the checks code.
---
 .../clang/Basic/DiagnosticSemaKinds.td        | 20 ++++---
 clang/lib/Sema/SemaDeclAttr.cpp               | 56 ++++++++-----------
 clang/test/Sema/attr-malloc_span.c            | 25 ++++++---
 clang/test/SemaCXX/attr-malloc_span.cpp       |  4 +-
 4 files changed, 56 insertions(+), 49 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 919b92a9616cd..9a51fd7a9b7ee 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3453,14 +3453,18 @@ def warn_attribute_return_span_only
     : Warning<"%0 attribute only applies to functions that return span-like "
               "structures">,
       InGroup<IgnoredAttributes>;
-def note_span_must_be_struct : Note<"span-like type must be a struct">;
-def note_span_must_be_complete : Note<"span-like type must be complete">;
-def note_wrong_span_field_count : Note<"span-like type must have 2 fields">;
-def note_wrong_span_field_types
-    : Note<"span-like type must have a pointer and an integer field or two "
-           "pointer fields">;
-def note_span_invalid_integer : Note<"the integer field must be an actual "
-                                     "integer that is at least as big as int">;
+def note_returned_not_struct : Note<"returned type is not a struct/class 
type">;
+def note_returned_incomlete_type : Note<"returned type is incomplete">;
+def note_returned_not_two_field_struct
+    : Note<"returned struct/class has %0 fields, expected 2">;
+def note_returned_not_span_struct
+    : Note<"returned struct/class fields are not a supported combination for a 
"
+           "span-like type (expected pointer/integer or pointer/pointer)">;
+def note_returned_not_integer_field
+    : Note<"field #%0 expected to be an integer">;
+def note_returned_not_wide_enough_field
+    : Note<"integer field #%0 of span-like type is not wide enough (minimum "
+           "width: %1)">;
 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 c090cee3f7d1e..d6bb5cacb8f88 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1850,59 +1850,51 @@ static bool checkSpanLikeType(Sema &S, const ParsedAttr 
&AL,
   // lead to false positive results.
   auto emitWarning = [&S, &AL](unsigned NoteDiagID) {
     S.Diag(AL.getLoc(), diag::warn_attribute_return_span_only) << AL;
-    S.Diag(AL.getLoc(), NoteDiagID);
+    return S.Diag(AL.getLoc(), NoteDiagID);
   };
+  if (Ty->isIncompleteType())
+    return emitWarning(diag::note_returned_incomlete_type);
   const RecordDecl *RD = Ty->getAsRecordDecl();
-  if (!RD || RD->isUnion()) {
-    emitWarning(diag::note_span_must_be_struct);
-    return false;
-  }
-  const RecordDecl *Def = RD->getDefinition();
-  if (!Def) {
-    emitWarning(diag::note_span_must_be_complete);
-    return false; // This is an incomplete type.
-  }
-  auto FieldsBegin = Def->field_begin();
-  if (std::distance(FieldsBegin, Def->field_end()) != 2) {
-    emitWarning(diag::note_wrong_span_field_count);
-    return false;
-  }
+  if (!RD || RD->isUnion())
+    return emitWarning(diag::note_returned_not_struct);
+  auto FieldsBegin = RD->field_begin();
+  const auto FieldsCount = std::distance(FieldsBegin, RD->field_end());
+  if (FieldsCount != 2)
+    return emitWarning(diag::note_returned_not_two_field_struct) << 
FieldsCount;
   const QualType FirstFieldType = FieldsBegin->getType();
   const QualType SecondFieldType = std::next(FieldsBegin)->getType();
   auto validatePointerType = [](const QualType &T) {
     // It must not point to functions.
     return T->isPointerType() && !T->isFunctionPointerType();
   };
-  auto checkIntegerType = [&S, emitWarning](const QualType &T) {
-    bool valid = false;
-    // Must be an actual integer and at least as bit as int.
-    if (const auto *BT = dyn_cast<BuiltinType>(T.getCanonicalType())) {
-      const auto IntSize = S.Context.getTypeSize(S.Context.IntTy);
-      valid = BT->isInteger() && S.Context.getTypeSize(BT) >= IntSize;
-    }
-    if (!valid) {
-      emitWarning(diag::note_span_invalid_integer);
-    }
-    return valid;
+  auto checkIntegerType = [&S, emitWarning](const QualType &T,
+                                            const int FieldNo) -> bool {
+    const auto *BT = dyn_cast<BuiltinType>(T.getCanonicalType());
+    if (!BT || !BT->isInteger())
+      return emitWarning(diag::note_returned_not_integer_field) << FieldNo;
+    const auto IntSize = S.Context.getTypeSize(S.Context.IntTy);
+    if (S.Context.getTypeSize(BT) < IntSize)
+      return emitWarning(diag::note_returned_not_wide_enough_field)
+             << FieldNo << IntSize;
+    return false;
   };
   if (validatePointerType(FirstFieldType) &&
       validatePointerType(SecondFieldType)) {
     // Pointer + pointer.
-    return true;
+    return false;
   } else if (validatePointerType(FirstFieldType)) {
     // Pointer + integer?
-    return checkIntegerType(SecondFieldType);
+    return checkIntegerType(SecondFieldType, 2);
   } else if (validatePointerType(SecondFieldType)) {
     // Integer + pointer?
-    return checkIntegerType(FirstFieldType);
+    return checkIntegerType(FirstFieldType, 1);
   }
-  emitWarning(diag::note_wrong_span_field_types);
-  return false;
+  return emitWarning(diag::note_returned_not_span_struct);
 }
 
 static void handleMallocSpanAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   QualType ResultType = getFunctionOrMethodResultType(D);
-  if (checkSpanLikeType(S, AL, ResultType)) {
+  if (!checkSpanLikeType(S, AL, ResultType)) {
     D->addAttr(::new (S.Context) MallocSpanAttr(S.Context, AL));
   }
 }
diff --git a/clang/test/Sema/attr-malloc_span.c 
b/clang/test/Sema/attr-malloc_span.c
index 8203e9df4d330..6dc94ed3797f3 100644
--- a/clang/test/Sema/attr-malloc_span.c
+++ b/clang/test/Sema/attr-malloc_span.c
@@ -9,7 +9,6 @@ typedef struct {
 } span;
 span  returns_span  (void) __attribute((malloc_span)); // no-warning
 
-// Try out a different field ordering.
 typedef struct {
   size_t n;
   void *ptr;
@@ -21,8 +20,20 @@ typedef struct {
   void *ptr2;
 } span3;
 span3  returns_span3  (void) __attribute((malloc_span)); // no-warning
+
+typedef struct {
+  void *ptr;
+  int n;
+} span4;
+span4  returns_span4  (void) __attribute((malloc_span)); // no-warning
+
+typedef struct incomplete_span incomplete_span;
+// expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
+// expected-note@+1 {{returned type is incomplete}}
+incomplete_span returns_incomplete_span (void) __attribute((malloc_span));
+
 // expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
-// expected-note@+1 {{span-like type must be a struct}}
+// expected-note@+1 {{returned type is not a struct/class type}}
 int *returns_int_ptr  (void) __attribute((malloc_span));
 
 typedef struct {
@@ -31,7 +42,7 @@ typedef struct {
   size_t n2;
 } too_long_span;
 // expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
-// expected-note@+1 {{span-like type must have 2 fields}}
+// expected-note@+1 {{returned struct/class has 3 fields, expected 2}}
 too_long_span  returns_too_long_span  (void) __attribute((malloc_span));
 
 // Function pointers are not allowed.
@@ -40,7 +51,7 @@ typedef struct {
   size_t n;
 } func_span;
 // expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
-// expected-note@+1 {{span-like type must have a pointer and an integer field 
or two pointer fields}}
+// expected-note@+1 {{returned struct/class fields are not a supported 
combination}}
 func_span  returns_func_span  (void) __attribute((malloc_span));
 
 // Integer should not be an enum.
@@ -50,7 +61,7 @@ typedef struct {
   enum some_enum field;
 } enum_span;
 // expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
-// expected-note@+1 {{the integer field must be an actual integer}}
+// expected-note@+1 {{field #2 expected to be an integer}}
 enum_span  returns_enum_span  (void) __attribute((malloc_span));
 
 // Bit integers are also not supported.
@@ -59,7 +70,7 @@ typedef struct {
   _BitInt(16) n;
 } bit_span;
 // expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
-// expected-note@+1 {{the integer field must be an actual integer}}
+// expected-note@+1 {{field #2 expected to be an integer}}
 bit_span  returns_bit_span  (void) __attribute((malloc_span));
 
 // Integer must be at least as big as int.
@@ -68,5 +79,5 @@ typedef struct {
   short n;
 } short_span;
 // expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
-// expected-note@+1 {{the integer field must be an actual integer}}
+// expected-note@+1 {{integer field #2 of span-like type is not wide enough 
(minimum width: 32)}}
 short_span  returns_short_span  (void) __attribute((malloc_span));
diff --git a/clang/test/SemaCXX/attr-malloc_span.cpp 
b/clang/test/SemaCXX/attr-malloc_span.cpp
index 8748038f8d133..747216b637c5f 100644
--- a/clang/test/SemaCXX/attr-malloc_span.cpp
+++ b/clang/test/SemaCXX/attr-malloc_span.cpp
@@ -12,7 +12,7 @@ struct DataMemberSpan {
 };
 
 // expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
-// expected-note@+1 {{span-like type must have a pointer and an integer field 
or two pointer fields}}
+// expected-note@+1 {{returned struct/class fields are not a supported 
combination}}
 DataMemberSpan returns_data_member_span(void) __attribute((malloc_span)) {
   return DataMemberSpan{};
 }
@@ -24,7 +24,7 @@ struct MemberFuncSpan {
 };
 
 // expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
-// expected-note@+1 {{span-like type must have a pointer and an integer field 
or two pointer fields}}
+// expected-note@+1 {{returned struct/class fields are not a supported 
combination}}
 MemberFuncSpan returns_member_func_span(void) __attribute((malloc_span)) {
   return MemberFuncSpan{};
 }

>From e21436b5537e760d0e871b252af1ad6213058d2c Mon Sep 17 00:00:00 2001
From: Aleksandr Nogikh <[email protected]>
Date: Fri, 14 Nov 2025 18:42:03 +0000
Subject: [PATCH 10/20] fixup: remove typos

Fix typos in docs and diagnostic messages.
Remove unnecessary line in a test file.
---
 clang/include/clang/Basic/AttrDocs.td            | 2 +-
 clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +-
 clang/lib/Sema/SemaDeclAttr.cpp                  | 2 +-
 clang/test/Sema/attr-malloc_span.c               | 1 -
 4 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/clang/include/clang/Basic/AttrDocs.td 
b/clang/include/clang/Basic/AttrDocs.td
index 786444ba4d653..922af709cb578 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -5257,7 +5257,7 @@ 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 two fields, one of
-which is a pointer to the start of theallocated memory and another one is
+which is a pointer to the start of the allocated memory and another one is
 either an integer type containing the size of the actually allocated memory
 or a pointer the end of the allocated region.
   }];
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 9a51fd7a9b7ee..283b41edd4ef9 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3454,7 +3454,7 @@ def warn_attribute_return_span_only
               "structures">,
       InGroup<IgnoredAttributes>;
 def note_returned_not_struct : Note<"returned type is not a struct/class 
type">;
-def note_returned_incomlete_type : Note<"returned type is incomplete">;
+def note_returned_incomplete_type : Note<"returned type is incomplete">;
 def note_returned_not_two_field_struct
     : Note<"returned struct/class has %0 fields, expected 2">;
 def note_returned_not_span_struct
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index d6bb5cacb8f88..6ec75fd18400b 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1853,7 +1853,7 @@ static bool checkSpanLikeType(Sema &S, const ParsedAttr 
&AL,
     return S.Diag(AL.getLoc(), NoteDiagID);
   };
   if (Ty->isIncompleteType())
-    return emitWarning(diag::note_returned_incomlete_type);
+    return emitWarning(diag::note_returned_incomplete_type);
   const RecordDecl *RD = Ty->getAsRecordDecl();
   if (!RD || RD->isUnion())
     return emitWarning(diag::note_returned_not_struct);
diff --git a/clang/test/Sema/attr-malloc_span.c 
b/clang/test/Sema/attr-malloc_span.c
index 6dc94ed3797f3..bcfb4e6a14225 100644
--- a/clang/test/Sema/attr-malloc_span.c
+++ b/clang/test/Sema/attr-malloc_span.c
@@ -1,5 +1,4 @@
 // RUN: %clang_cc1 -verify -fsyntax-only %s
-// RUN: %clang_cc1 -emit-llvm -o %t %s
 
 typedef __SIZE_TYPE__ size_t;
 

>From 68faf471423e11c5db1eabe7f2dce23911e099b5 Mon Sep 17 00:00:00 2001
From: Aleksandr Nogikh <[email protected]>
Date: Fri, 14 Nov 2025 18:23:58 +0000
Subject: [PATCH 11/20] [experimental] Support templates

Support returned dependent types and class tempate instantiations.
---
 clang/include/clang/Sema/Sema.h               |  5 +++
 clang/lib/Sema/SemaDeclAttr.cpp               | 33 ++++++++------
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  | 19 ++++++++
 clang/test/SemaCXX/attr-malloc_span.cpp       | 43 +++++++++++++++++++
 4 files changed, 87 insertions(+), 13 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index fd2a2469142e4..cbfcc9bc0ea99 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -5111,6 +5111,11 @@ class Sema final : public SemaBase {
   /// Essentially, this just moves them to the current pool.
   void redelayDiagnostics(sema::DelayedDiagnosticPool &pool);
 
+  /// 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.
+  bool CheckSpanLikeType(const AttributeCommonInfo &CI, const QualType &Ty);
+
   /// Check if IdxExpr is a valid parameter index for a function or
   /// instance method D.  May output an error.
   ///
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 6ec75fd18400b..cb0c1c96dbbad 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1839,19 +1839,24 @@ static void handleRestrictAttr(Sema &S, Decl *D, const 
ParsedAttr &AL) {
                  RestrictAttr(S.Context, AL, DeallocE, DeallocPtrIdx));
 }
 
-static bool checkSpanLikeType(Sema &S, const ParsedAttr &AL,
-                              const QualType &Ty) {
-  // 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.
+bool Sema::CheckSpanLikeType(const AttributeCommonInfo &CI,
+                             const QualType &Ty) {
   // Note that there may also be numerous cases of pointer + integer /
   // pointer + pointer / integer + pointer structures not actually exhibiting
   // a span-like semantics, so sometimes these heuristics expectedly
   // lead to false positive results.
-  auto emitWarning = [&S, &AL](unsigned NoteDiagID) {
-    S.Diag(AL.getLoc(), diag::warn_attribute_return_span_only) << AL;
-    return S.Diag(AL.getLoc(), NoteDiagID);
+  auto emitWarning = [this, &CI](unsigned NoteDiagID) {
+    Diag(CI.getLoc(), diag::warn_attribute_return_span_only) << CI;
+    return Diag(CI.getLoc(), NoteDiagID);
   };
+  if (!Ty->isDependentType()) {
+    // If the type is a class template specialization, it may not be
+    // instantiated at this stage. We must force it to be complete to examine
+    // its fields.
+    // The returned value is discarded since the code below emits a warning
+    // if the type keeps being incomplete.
+    (void)isCompleteType(CI.getLoc(), Ty);
+  }
   if (Ty->isIncompleteType())
     return emitWarning(diag::note_returned_incomplete_type);
   const RecordDecl *RD = Ty->getAsRecordDecl();
@@ -1867,13 +1872,13 @@ static bool checkSpanLikeType(Sema &S, const ParsedAttr 
&AL,
     // It must not point to functions.
     return T->isPointerType() && !T->isFunctionPointerType();
   };
-  auto checkIntegerType = [&S, emitWarning](const QualType &T,
-                                            const int FieldNo) -> bool {
+  auto checkIntegerType = [this, emitWarning](const QualType &T,
+                                              const int FieldNo) -> bool {
     const auto *BT = dyn_cast<BuiltinType>(T.getCanonicalType());
     if (!BT || !BT->isInteger())
       return emitWarning(diag::note_returned_not_integer_field) << FieldNo;
-    const auto IntSize = S.Context.getTypeSize(S.Context.IntTy);
-    if (S.Context.getTypeSize(BT) < IntSize)
+    const auto IntSize = Context.getTypeSize(Context.IntTy);
+    if (Context.getTypeSize(BT) < IntSize)
       return emitWarning(diag::note_returned_not_wide_enough_field)
              << FieldNo << IntSize;
     return false;
@@ -1894,7 +1899,9 @@ static bool checkSpanLikeType(Sema &S, const ParsedAttr 
&AL,
 
 static void handleMallocSpanAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   QualType ResultType = getFunctionOrMethodResultType(D);
-  if (!checkSpanLikeType(S, AL, ResultType)) {
+  if (ResultType->isDependentType() || !S.CheckSpanLikeType(AL, ResultType)) {
+    // If it's a dependent type, the attribute will be re-checked upon
+    // instantiation.
     D->addAttr(::new (S.Context) MallocSpanAttr(S.Context, AL));
   }
 }
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp 
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 3a4b2ccc74350..e3740becba03c 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -796,6 +796,20 @@ static void instantiateDependentHLSLParamModifierAttr(
       "out or inout parameter type must be a reference and restrict 
qualified");
 }
 
+static void instantiateDependentMallocSpanAttr(Sema &S,
+                                               const MallocSpanAttr *Attr,
+                                               Decl *New) {
+  QualType RT = getFunctionOrMethodResultType(New);
+  if (RT->isDependentType()) {
+    // The type is still dependent.
+    // Clone the attribute, it will be checked later.
+    New->addAttr(Attr->clone(S.getASTContext()));
+  } else if (!S.CheckSpanLikeType(*Attr, RT)) {
+    // The conditions have been successfully validated.
+    New->addAttr(Attr->clone(S.getASTContext()));
+  }
+}
+
 void Sema::InstantiateAttrsForDecl(
     const MultiLevelTemplateArgumentList &TemplateArgs, const Decl *Tmpl,
     Decl *New, LateInstantiatedAttrVec *LateAttrs,
@@ -1007,6 +1021,11 @@ void Sema::InstantiateAttrs(const 
MultiLevelTemplateArgumentList &TemplateArgs,
       continue;
     }
 
+    if (auto *A = dyn_cast<MallocSpanAttr>(TmplAttr)) {
+      instantiateDependentMallocSpanAttr(*this, A, New);
+      continue;
+    }
+
     if (auto *A = dyn_cast<CleanupAttr>(TmplAttr)) {
       if (!New->hasAttr<CleanupAttr>()) {
         auto *NewAttr = A->clone(Context);
diff --git a/clang/test/SemaCXX/attr-malloc_span.cpp 
b/clang/test/SemaCXX/attr-malloc_span.cpp
index 747216b637c5f..be54050c4ee7a 100644
--- a/clang/test/SemaCXX/attr-malloc_span.cpp
+++ b/clang/test/SemaCXX/attr-malloc_span.cpp
@@ -29,3 +29,46 @@ MemberFuncSpan returns_member_func_span(void) 
__attribute((malloc_span)) {
   return MemberFuncSpan{};
 }
 
+template<typename FirstType, typename SecondType>
+struct Pair {
+  FirstType first;
+  SecondType second;
+};
+
+Pair<int*, int> returns_templated_span1(void) __attribute((malloc_span)) { // 
no-warning
+  return Pair<int*, int>{};
+}
+
+Pair<int*, int*> returns_templated_span2(void) __attribute((malloc_span)) { // 
no-warning
+  return Pair<int*, int*>{};
+}
+
+// expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
+// expected-note@+1 {{returned struct/class fields are not a supported 
combination for a span-like type}}
+Pair<int, int> returns_templated_span3(void) __attribute((malloc_span)) {
+  return Pair<int, int>{};
+}
+
+// Verify that semantic checks are done on dependent types.
+
+struct GoodSpan {
+  void *ptr;
+  int n;
+};
+
+struct BadSpan {
+  int n;
+};
+
+template <typename T>
+// expected-warning@+2 {{'malloc_span' attribute only applies to functions 
that return span-like structures}}
+// expected-note@+1 {{returned struct/class has 1 fields, expected 2}}
+T produce_span() __attribute((malloc_span)) {
+  return T{};
+}
+
+void TestGoodBadSpan() {
+  produce_span<GoodSpan>(); // no-warnings
+  // expected-note@+1 {{in instantiation of function template specialization 
'produce_span<BadSpan>' requested here}}
+  produce_span<BadSpan>();
+}

>From f238a87046c732d6d36a4cbbb11f8f6b4499eb9a Mon Sep 17 00:00:00 2001
From: Aleksandr Nogikh <[email protected]>
Date: Mon, 17 Nov 2025 13:09:36 +0000
Subject: [PATCH 12/20] fixup: add trailing return type tests

---
 clang/test/SemaCXX/attr-malloc_span.cpp | 28 +++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/clang/test/SemaCXX/attr-malloc_span.cpp 
b/clang/test/SemaCXX/attr-malloc_span.cpp
index be54050c4ee7a..b16d3075344d0 100644
--- a/clang/test/SemaCXX/attr-malloc_span.cpp
+++ b/clang/test/SemaCXX/attr-malloc_span.cpp
@@ -72,3 +72,31 @@ void TestGoodBadSpan() {
   // expected-note@+1 {{in instantiation of function template specialization 
'produce_span<BadSpan>' requested here}}
   produce_span<BadSpan>();
 }
+
+// Ensure that trailing return types are also supported.
+__attribute__((malloc_span)) auto trailling_return_type(int size)  -> GoodSpan 
{ // no-warning
+  return GoodSpan{};
+}
+
+template<typename T>
+// expected-warning@+2 {{'malloc_span' attribute only applies to functions 
that return span-like structures}}
+// expected-note@+1 {{returned struct/class has 1 fields, expected 2}}
+__attribute__((malloc_span)) auto templated_trailling_return_type()  -> T { // 
no-warning
+  return T{};
+}
+
+void TestGoodBadTrailingReturnType() {
+  templated_trailling_return_type<GoodSpan>(); // no-warnings
+  // expected-note@+1 {{in instantiation of function template specialization 
'templated_trailling_return_type<BadSpan>' requested here}}
+  templated_trailling_return_type<BadSpan>();
+}
+
+__attribute((malloc_span)) auto trailling_return_temmplate_good(void) -> 
Pair<int*, int> { // no-warning
+  return Pair<int*, int>{};
+}
+
+// expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
+// expected-note@+1 {{returned struct/class fields are not a supported 
combination for a span-like type}}
+__attribute((malloc_span)) auto trailling_return_temmplate_bad(void) -> 
Pair<int, int> {
+  return Pair<int, int>{};
+}

>From 3f81d138fd180dd07eea554871dab823cb62b427 Mon Sep 17 00:00:00 2001
From: Aleksandr Nogikh <[email protected]>
Date: Mon, 17 Nov 2025 14:09:55 +0000
Subject: [PATCH 13/20] fixup: simplify dependent type processing code

---
 clang/lib/Sema/SemaDeclAttr.cpp                | 18 +++++-------------
 clang/lib/Sema/SemaTemplateInstantiateDecl.cpp |  8 +-------
 2 files changed, 6 insertions(+), 20 deletions(-)

diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index cb0c1c96dbbad..25283a9bac43e 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1849,15 +1849,10 @@ bool Sema::CheckSpanLikeType(const AttributeCommonInfo 
&CI,
     Diag(CI.getLoc(), diag::warn_attribute_return_span_only) << CI;
     return Diag(CI.getLoc(), NoteDiagID);
   };
-  if (!Ty->isDependentType()) {
-    // If the type is a class template specialization, it may not be
-    // instantiated at this stage. We must force it to be complete to examine
-    // its fields.
-    // The returned value is discarded since the code below emits a warning
-    // if the type keeps being incomplete.
-    (void)isCompleteType(CI.getLoc(), Ty);
-  }
-  if (Ty->isIncompleteType())
+  if (Ty->isDependentType())
+    return false;
+  // isCompleteType is used to force template class instantiation.
+  if (!isCompleteType(CI.getLoc(), Ty))
     return emitWarning(diag::note_returned_incomplete_type);
   const RecordDecl *RD = Ty->getAsRecordDecl();
   if (!RD || RD->isUnion())
@@ -1899,11 +1894,8 @@ bool Sema::CheckSpanLikeType(const AttributeCommonInfo 
&CI,
 
 static void handleMallocSpanAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   QualType ResultType = getFunctionOrMethodResultType(D);
-  if (ResultType->isDependentType() || !S.CheckSpanLikeType(AL, ResultType)) {
-    // If it's a dependent type, the attribute will be re-checked upon
-    // instantiation.
+  if (!S.CheckSpanLikeType(AL, ResultType))
     D->addAttr(::new (S.Context) MallocSpanAttr(S.Context, AL));
-  }
 }
 
 static void handleCPUSpecificAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp 
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index e3740becba03c..26693514bb278 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -800,14 +800,8 @@ static void instantiateDependentMallocSpanAttr(Sema &S,
                                                const MallocSpanAttr *Attr,
                                                Decl *New) {
   QualType RT = getFunctionOrMethodResultType(New);
-  if (RT->isDependentType()) {
-    // The type is still dependent.
-    // Clone the attribute, it will be checked later.
+  if (!S.CheckSpanLikeType(*Attr, RT))
     New->addAttr(Attr->clone(S.getASTContext()));
-  } else if (!S.CheckSpanLikeType(*Attr, RT)) {
-    // The conditions have been successfully validated.
-    New->addAttr(Attr->clone(S.getASTContext()));
-  }
 }
 
 void Sema::InstantiateAttrsForDecl(

>From 541961c3b7e41a520311b353f7ece319a047523f Mon Sep 17 00:00:00 2001
From: Aleksandr Nogikh <[email protected]>
Date: Mon, 17 Nov 2025 21:29:15 +0000
Subject: [PATCH 14/20] fixup: fix typos

---
 clang/test/SemaCXX/attr-malloc_span.cpp | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/clang/test/SemaCXX/attr-malloc_span.cpp 
b/clang/test/SemaCXX/attr-malloc_span.cpp
index b16d3075344d0..acaf4407210e4 100644
--- a/clang/test/SemaCXX/attr-malloc_span.cpp
+++ b/clang/test/SemaCXX/attr-malloc_span.cpp
@@ -74,29 +74,29 @@ void TestGoodBadSpan() {
 }
 
 // Ensure that trailing return types are also supported.
-__attribute__((malloc_span)) auto trailling_return_type(int size)  -> GoodSpan 
{ // no-warning
+__attribute__((malloc_span)) auto trailing_return_type(int size)  -> GoodSpan 
{ // no-warning
   return GoodSpan{};
 }
 
 template<typename T>
 // expected-warning@+2 {{'malloc_span' attribute only applies to functions 
that return span-like structures}}
 // expected-note@+1 {{returned struct/class has 1 fields, expected 2}}
-__attribute__((malloc_span)) auto templated_trailling_return_type()  -> T { // 
no-warning
+__attribute__((malloc_span)) auto templated_trailing_return_type()  -> T {
   return T{};
 }
 
 void TestGoodBadTrailingReturnType() {
-  templated_trailling_return_type<GoodSpan>(); // no-warnings
-  // expected-note@+1 {{in instantiation of function template specialization 
'templated_trailling_return_type<BadSpan>' requested here}}
-  templated_trailling_return_type<BadSpan>();
+  templated_trailing_return_type<GoodSpan>(); // no-warnings
+  // expected-note@+1 {{in instantiation of function template specialization 
'templated_trailing_return_type<BadSpan>' requested here}}
+  templated_trailing_return_type<BadSpan>();
 }
 
-__attribute((malloc_span)) auto trailling_return_temmplate_good(void) -> 
Pair<int*, int> { // no-warning
+__attribute((malloc_span)) auto trailing_return_temmplate_good(void) -> 
Pair<int*, int> { // no-warning
   return Pair<int*, int>{};
 }
 
 // expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
 // expected-note@+1 {{returned struct/class fields are not a supported 
combination for a span-like type}}
-__attribute((malloc_span)) auto trailling_return_temmplate_bad(void) -> 
Pair<int, int> {
+__attribute((malloc_span)) auto trailing_return_temmplate_bad(void) -> 
Pair<int, int> {
   return Pair<int, int>{};
 }

>From e939e179a12557b5c61e80476eeef2717b0201b0 Mon Sep 17 00:00:00 2001
From: Aleksandr Nogikh <[email protected]>
Date: Mon, 17 Nov 2025 21:34:34 +0000
Subject: [PATCH 15/20] fixup: add a test for static struct field members

Ensure static fields are ignored.
---
 clang/test/SemaCXX/attr-malloc_span.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/clang/test/SemaCXX/attr-malloc_span.cpp 
b/clang/test/SemaCXX/attr-malloc_span.cpp
index acaf4407210e4..8ce01f64879cf 100644
--- a/clang/test/SemaCXX/attr-malloc_span.cpp
+++ b/clang/test/SemaCXX/attr-malloc_span.cpp
@@ -1,5 +1,13 @@
 // RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
 
+struct span_with_static {
+  void *ptr;
+  int n;
+  static int static_field;
+};
+
+span_with_static  returns_span_with_static  (void) __attribute((malloc_span)); 
// no-warning
+
 class SomeClass {
 public:
   int Data;

>From cff934fac53d159b5c47077fb7a093c18ca93094 Mon Sep 17 00:00:00 2001
From: Aleksandr Nogikh <[email protected]>
Date: Mon, 17 Nov 2025 21:38:25 +0000
Subject: [PATCH 16/20] fixup: drop const for non-pointers

Conform to the coding style.
---
 clang/lib/Sema/SemaDeclAttr.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 25283a9bac43e..71447be71689e 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1858,11 +1858,11 @@ bool Sema::CheckSpanLikeType(const AttributeCommonInfo 
&CI,
   if (!RD || RD->isUnion())
     return emitWarning(diag::note_returned_not_struct);
   auto FieldsBegin = RD->field_begin();
-  const auto FieldsCount = std::distance(FieldsBegin, RD->field_end());
+  auto FieldsCount = std::distance(FieldsBegin, RD->field_end());
   if (FieldsCount != 2)
     return emitWarning(diag::note_returned_not_two_field_struct) << 
FieldsCount;
-  const QualType FirstFieldType = FieldsBegin->getType();
-  const QualType SecondFieldType = std::next(FieldsBegin)->getType();
+  QualType FirstFieldType = FieldsBegin->getType();
+  QualType SecondFieldType = std::next(FieldsBegin)->getType();
   auto validatePointerType = [](const QualType &T) {
     // It must not point to functions.
     return T->isPointerType() && !T->isFunctionPointerType();
@@ -1872,7 +1872,7 @@ bool Sema::CheckSpanLikeType(const AttributeCommonInfo 
&CI,
     const auto *BT = dyn_cast<BuiltinType>(T.getCanonicalType());
     if (!BT || !BT->isInteger())
       return emitWarning(diag::note_returned_not_integer_field) << FieldNo;
-    const auto IntSize = Context.getTypeSize(Context.IntTy);
+    auto IntSize = Context.getTypeSize(Context.IntTy);
     if (Context.getTypeSize(BT) < IntSize)
       return emitWarning(diag::note_returned_not_wide_enough_field)
              << FieldNo << IntSize;

>From 0380a0d2aca198312aadcb720de58282ee2e483b Mon Sep 17 00:00:00 2001
From: Aleksandr Nogikh <[email protected]>
Date: Mon, 17 Nov 2025 22:02:27 +0000
Subject: [PATCH 17/20] fixup: verify span-like struct/class bases

If present, they must be empty.
---
 .../clang/Basic/DiagnosticSemaKinds.td        |  2 ++
 clang/lib/Sema/SemaDeclAttr.cpp               |  8 +++++++
 clang/test/SemaCXX/attr-malloc_span.cpp       | 23 +++++++++++++++++++
 3 files changed, 33 insertions(+)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 283b41edd4ef9..4ffdbb033cd3d 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3465,6 +3465,8 @@ def note_returned_not_integer_field
 def note_returned_not_wide_enough_field
     : Note<"integer field #%0 of span-like type is not wide enough (minimum "
            "width: %1)">;
+def note_inherits_not_empty_base
+    : Note<"returned struct/class inherits from a non-empty base">;
 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 71447be71689e..973e14eca4213 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1857,6 +1857,14 @@ bool Sema::CheckSpanLikeType(const AttributeCommonInfo 
&CI,
   const RecordDecl *RD = Ty->getAsRecordDecl();
   if (!RD || RD->isUnion())
     return emitWarning(diag::note_returned_not_struct);
+  if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
+    for (const auto &Base : CXXRD->bases()) {
+      const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
+      if (!BaseDecl || !BaseDecl->isEmpty()) {
+        return emitWarning(diag::note_inherits_not_empty_base);
+      }
+    }
+  }
   auto FieldsBegin = RD->field_begin();
   auto FieldsCount = std::distance(FieldsBegin, RD->field_end());
   if (FieldsCount != 2)
diff --git a/clang/test/SemaCXX/attr-malloc_span.cpp 
b/clang/test/SemaCXX/attr-malloc_span.cpp
index 8ce01f64879cf..ff5527546fe5e 100644
--- a/clang/test/SemaCXX/attr-malloc_span.cpp
+++ b/clang/test/SemaCXX/attr-malloc_span.cpp
@@ -108,3 +108,26 @@ __attribute((malloc_span)) auto 
trailing_return_temmplate_good(void) -> Pair<int
 __attribute((malloc_span)) auto trailing_return_temmplate_bad(void) -> 
Pair<int, int> {
   return Pair<int, int>{};
 }
+
+struct EmptyBase {
+};
+
+struct GoodChildSpan : EmptyBase {
+  void *p;
+  int n;
+};
+
+__attribute((malloc_span)) GoodChildSpan return_span_with_good_base(void); // 
no-warning
+
+struct NonEmptyBase {
+  void *other_p;
+};
+
+struct BadChildSpan : EmptyBase, NonEmptyBase {
+  void *p;
+  int n;
+};
+
+// expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
+// expected-note@+1 {{returned struct/class inherits from a non-empty base}}
+__attribute((malloc_span)) BadChildSpan return_span_with_bad_base(void);

>From 46f32990218be86f678ccf557414ad5a85374b4f Mon Sep 17 00:00:00 2001
From: Aleksandr Nogikh <[email protected]>
Date: Tue, 18 Nov 2025 19:56:54 +0000
Subject: [PATCH 18/20] fixup: improve the docs and diagnostic messages

---
 clang/include/clang/Basic/AttrDocs.td            |  9 +++++----
 clang/include/clang/Basic/DiagnosticSemaKinds.td | 14 +++++++-------
 clang/test/Sema/attr-malloc_span.c               | 12 ++++++------
 clang/test/SemaCXX/attr-malloc_span.cpp          | 14 +++++++-------
 4 files changed, 25 insertions(+), 24 deletions(-)

diff --git a/clang/include/clang/Basic/AttrDocs.td 
b/clang/include/clang/Basic/AttrDocs.td
index 922af709cb578..c1b1510f363d4 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -5256,10 +5256,11 @@ 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 two fields, one of
-which is a pointer to the start of the allocated memory and another one is
-either an integer type containing the size of the actually allocated memory
-or a pointer the end of the allocated region.
+In this context, a span-like structure is assumed to have two non-static data
+members, one of which is a pointer to the start of the allocated memory and
+the other one is either an integer type containing the size of the actually
+allocated memory or a pointer to the end of the allocated region. Note, static
+data members do not impact whether a type is span-like or not.
   }];
 }
 
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 4ffdbb033cd3d..900d195fa2c71 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3453,20 +3453,20 @@ def warn_attribute_return_span_only
     : Warning<"%0 attribute only applies to functions that return span-like "
               "structures">,
       InGroup<IgnoredAttributes>;
-def note_returned_not_struct : Note<"returned type is not a struct/class 
type">;
+def note_returned_not_struct : Note<"returned type is not a struct type">;
 def note_returned_incomplete_type : Note<"returned type is incomplete">;
 def note_returned_not_two_field_struct
-    : Note<"returned struct/class has %0 fields, expected 2">;
+    : Note<"returned struct has %0 fields, expected 2">;
 def note_returned_not_span_struct
-    : Note<"returned struct/class fields are not a supported combination for a 
"
+    : Note<"returned struct fields are not a supported combination for a "
            "span-like type (expected pointer/integer or pointer/pointer)">;
 def note_returned_not_integer_field
-    : Note<"field #%0 expected to be an integer">;
+    : Note<"%ordinal0 field is expected to be an integer">;
 def note_returned_not_wide_enough_field
-    : Note<"integer field #%0 of span-like type is not wide enough (minimum "
-           "width: %1)">;
+    : Note<"%ordinal0 field of span-like type is not a wide enough integer "
+           "(minimum width: %1)">;
 def note_inherits_not_empty_base
-    : Note<"returned struct/class inherits from a non-empty base">;
+    : Note<"returned class inherits from a non-empty base">;
 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/test/Sema/attr-malloc_span.c 
b/clang/test/Sema/attr-malloc_span.c
index bcfb4e6a14225..9238b601c5f0d 100644
--- a/clang/test/Sema/attr-malloc_span.c
+++ b/clang/test/Sema/attr-malloc_span.c
@@ -32,7 +32,7 @@ typedef struct incomplete_span incomplete_span;
 incomplete_span returns_incomplete_span (void) __attribute((malloc_span));
 
 // expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
-// expected-note@+1 {{returned type is not a struct/class type}}
+// expected-note@+1 {{returned type is not a struct type}}
 int *returns_int_ptr  (void) __attribute((malloc_span));
 
 typedef struct {
@@ -41,7 +41,7 @@ typedef struct {
   size_t n2;
 } too_long_span;
 // expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
-// expected-note@+1 {{returned struct/class has 3 fields, expected 2}}
+// expected-note@+1 {{returned struct has 3 fields, expected 2}}
 too_long_span  returns_too_long_span  (void) __attribute((malloc_span));
 
 // Function pointers are not allowed.
@@ -50,7 +50,7 @@ typedef struct {
   size_t n;
 } func_span;
 // expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
-// expected-note@+1 {{returned struct/class fields are not a supported 
combination}}
+// expected-note@+1 {{returned struct fields are not a supported combination}}
 func_span  returns_func_span  (void) __attribute((malloc_span));
 
 // Integer should not be an enum.
@@ -60,7 +60,7 @@ typedef struct {
   enum some_enum field;
 } enum_span;
 // expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
-// expected-note@+1 {{field #2 expected to be an integer}}
+// expected-note@+1 {{2nd field is expected to be an integer}}
 enum_span  returns_enum_span  (void) __attribute((malloc_span));
 
 // Bit integers are also not supported.
@@ -69,7 +69,7 @@ typedef struct {
   _BitInt(16) n;
 } bit_span;
 // expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
-// expected-note@+1 {{field #2 expected to be an integer}}
+// expected-note@+1 {{2nd field is expected to be an integer}}
 bit_span  returns_bit_span  (void) __attribute((malloc_span));
 
 // Integer must be at least as big as int.
@@ -78,5 +78,5 @@ typedef struct {
   short n;
 } short_span;
 // expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
-// expected-note@+1 {{integer field #2 of span-like type is not wide enough 
(minimum width: 32)}}
+// expected-note@+1 {{2nd field of span-like type is not a wide enough integer 
(minimum width: 32)}}
 short_span  returns_short_span  (void) __attribute((malloc_span));
diff --git a/clang/test/SemaCXX/attr-malloc_span.cpp 
b/clang/test/SemaCXX/attr-malloc_span.cpp
index ff5527546fe5e..fb3f215427fb8 100644
--- a/clang/test/SemaCXX/attr-malloc_span.cpp
+++ b/clang/test/SemaCXX/attr-malloc_span.cpp
@@ -20,7 +20,7 @@ struct DataMemberSpan {
 };
 
 // expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
-// expected-note@+1 {{returned struct/class fields are not a supported 
combination}}
+// expected-note@+1 {{returned struct fields are not a supported combination}}
 DataMemberSpan returns_data_member_span(void) __attribute((malloc_span)) {
   return DataMemberSpan{};
 }
@@ -32,7 +32,7 @@ struct MemberFuncSpan {
 };
 
 // expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
-// expected-note@+1 {{returned struct/class fields are not a supported 
combination}}
+// expected-note@+1 {{returned struct fields are not a supported combination}}
 MemberFuncSpan returns_member_func_span(void) __attribute((malloc_span)) {
   return MemberFuncSpan{};
 }
@@ -52,7 +52,7 @@ Pair<int*, int*> returns_templated_span2(void) 
__attribute((malloc_span)) { // n
 }
 
 // expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
-// expected-note@+1 {{returned struct/class fields are not a supported 
combination for a span-like type}}
+// expected-note@+1 {{returned struct fields are not a supported combination 
for a span-like type}}
 Pair<int, int> returns_templated_span3(void) __attribute((malloc_span)) {
   return Pair<int, int>{};
 }
@@ -70,7 +70,7 @@ struct BadSpan {
 
 template <typename T>
 // expected-warning@+2 {{'malloc_span' attribute only applies to functions 
that return span-like structures}}
-// expected-note@+1 {{returned struct/class has 1 fields, expected 2}}
+// expected-note@+1 {{returned struct has 1 fields, expected 2}}
 T produce_span() __attribute((malloc_span)) {
   return T{};
 }
@@ -88,7 +88,7 @@ __attribute__((malloc_span)) auto trailing_return_type(int 
size)  -> GoodSpan {
 
 template<typename T>
 // expected-warning@+2 {{'malloc_span' attribute only applies to functions 
that return span-like structures}}
-// expected-note@+1 {{returned struct/class has 1 fields, expected 2}}
+// expected-note@+1 {{returned struct has 1 fields, expected 2}}
 __attribute__((malloc_span)) auto templated_trailing_return_type()  -> T {
   return T{};
 }
@@ -104,7 +104,7 @@ __attribute((malloc_span)) auto 
trailing_return_temmplate_good(void) -> Pair<int
 }
 
 // expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
-// expected-note@+1 {{returned struct/class fields are not a supported 
combination for a span-like type}}
+// expected-note@+1 {{returned struct fields are not a supported combination 
for a span-like type}}
 __attribute((malloc_span)) auto trailing_return_temmplate_bad(void) -> 
Pair<int, int> {
   return Pair<int, int>{};
 }
@@ -129,5 +129,5 @@ struct BadChildSpan : EmptyBase, NonEmptyBase {
 };
 
 // expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
-// expected-note@+1 {{returned struct/class inherits from a non-empty base}}
+// expected-note@+1 {{returned class inherits from a non-empty base}}
 __attribute((malloc_span)) BadChildSpan return_span_with_bad_base(void);

>From 3ebd212af7faa8d799a9a43de88afc6f2d13e907 Mon Sep 17 00:00:00 2001
From: Aleksandr Nogikh <[email protected]>
Date: Tue, 18 Nov 2025 20:08:57 +0000
Subject: [PATCH 19/20] fixup: reject types inheriting from any base

---
 .../include/clang/Basic/DiagnosticSemaKinds.td |  4 ++--
 clang/lib/Sema/SemaDeclAttr.cpp                |  7 ++-----
 clang/test/SemaCXX/attr-malloc_span.cpp        | 18 ++++--------------
 3 files changed, 8 insertions(+), 21 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 900d195fa2c71..22b60c5fcd812 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3465,8 +3465,8 @@ def note_returned_not_integer_field
 def note_returned_not_wide_enough_field
     : Note<"%ordinal0 field of span-like type is not a wide enough integer "
            "(minimum width: %1)">;
-def note_inherits_not_empty_base
-    : Note<"returned class inherits from a non-empty base">;
+def note_type_inherits_from_base
+    : Note<"returned type inherits from a base class">;
 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 973e14eca4213..7bfc70a7fc869 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1858,11 +1858,8 @@ bool Sema::CheckSpanLikeType(const AttributeCommonInfo 
&CI,
   if (!RD || RD->isUnion())
     return emitWarning(diag::note_returned_not_struct);
   if (const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
-    for (const auto &Base : CXXRD->bases()) {
-      const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl();
-      if (!BaseDecl || !BaseDecl->isEmpty()) {
-        return emitWarning(diag::note_inherits_not_empty_base);
-      }
+    if (CXXRD->getNumBases() > 0) {
+      return emitWarning(diag::note_type_inherits_from_base);
     }
   }
   auto FieldsBegin = RD->field_begin();
diff --git a/clang/test/SemaCXX/attr-malloc_span.cpp 
b/clang/test/SemaCXX/attr-malloc_span.cpp
index fb3f215427fb8..41e71994201cf 100644
--- a/clang/test/SemaCXX/attr-malloc_span.cpp
+++ b/clang/test/SemaCXX/attr-malloc_span.cpp
@@ -109,25 +109,15 @@ __attribute((malloc_span)) auto 
trailing_return_temmplate_bad(void) -> Pair<int,
   return Pair<int, int>{};
 }
 
-struct EmptyBase {
-};
-
-struct GoodChildSpan : EmptyBase {
-  void *p;
-  int n;
-};
-
-__attribute((malloc_span)) GoodChildSpan return_span_with_good_base(void); // 
no-warning
-
-struct NonEmptyBase {
+struct Base {
   void *other_p;
 };
 
-struct BadChildSpan : EmptyBase, NonEmptyBase {
+struct ChildSpan : Base {
   void *p;
   int n;
 };
 
 // expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
-// expected-note@+1 {{returned class inherits from a non-empty base}}
-__attribute((malloc_span)) BadChildSpan return_span_with_bad_base(void);
+// expected-note@+1 {{returned type inherits from a base class}}
+__attribute((malloc_span)) ChildSpan return_child_span(void);

>From 8972a1dee88fccdfcda03c0b48ed715a329c41a7 Mon Sep 17 00:00:00 2001
From: Aleksandr Nogikh <[email protected]>
Date: Wed, 19 Nov 2025 15:27:53 +0000
Subject: [PATCH 20/20] fixup: add a virtual base class semantic test

---
 clang/test/SemaCXX/attr-malloc_span.cpp | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/clang/test/SemaCXX/attr-malloc_span.cpp 
b/clang/test/SemaCXX/attr-malloc_span.cpp
index 41e71994201cf..86622f6c154ea 100644
--- a/clang/test/SemaCXX/attr-malloc_span.cpp
+++ b/clang/test/SemaCXX/attr-malloc_span.cpp
@@ -121,3 +121,12 @@ struct ChildSpan : Base {
 // expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
 // expected-note@+1 {{returned type inherits from a base class}}
 __attribute((malloc_span)) ChildSpan return_child_span(void);
+
+class VirtualBaseSpan : public virtual Base {
+  void *p;
+  int n;
+};
+
+// expected-warning@+2 {{attribute only applies to functions that return 
span-like structures}}
+// expected-note@+1 {{returned type inherits from a base class}}
+__attribute((malloc_span)) VirtualBaseSpan return_virtual_base_span(void);

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

Reply via email to