Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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``,
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">];
Expand Down
15 changes: 15 additions & 0 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
like a system memory allocation function and returns a span-like structure
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 = [{
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
Expand Down
35 changes: 35 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be easy to allow both orderings (ptr+size, size+ptr).

For the malloc attribute I think we did it for simplicity and not expand the scope too much, but since we're introducing a new attribute entirely, maybe we can permit it right away?

Is there any technical reason we should not allow this now? Because I fear in future someone may want this, and then we're stuck again with version checks.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are to relax this requirement as well, I wonder if we should go one step further and also lift the restriction on having just 2 fields in this struct, i.e. only ensure that the struct has some pointer field and (at least one) integer field.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That on the other hand will cause more complexity, because then you have to potentially walk a large struct and find those fields. Also later where you may want to use this to improve codegen, it would have to go and find the pointer - what if there are 2 pointers?

I think if we talk about "span-like struct" it's exactly that: a struct with a pointer + size. Nothing more or less. But I think order of these members does not invalidate span semantics. On the other hand if you have a large struct with many members, is it a span? I can't tell.

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.
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
31 changes: 31 additions & 0 deletions clang/test/Sema/attr-malloc_span.c
Original file line number Diff line number Diff line change
@@ -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}}