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. It has the `malloc` semantics, but must be applied
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,
returns pointer to memory that 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 an integer with 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
3 changes: 2 additions & 1 deletion clang/lib/CodeGen/CGExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6642,7 +6642,8 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType,
CalleeDecl);
}
if (CalleeDecl->hasAttr<RestrictAttr>() ||
CalleeDecl->hasAttr<AllocSizeAttr>()) {
CalleeDecl->hasAttr<AllocSizeAttr>() ||
CalleeDecl->hasAttr<MallocSpanAttr>()) {
// Function has 'malloc' (aka. 'restrict') or 'alloc_size' attribute.
if (SanOpts.has(SanitizerKind::AllocToken)) {
// Set !alloc_token metadata.
Expand Down
36 changes: 36 additions & 0 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1839,6 +1839,39 @@ 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 std::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.
Expand Down Expand Up @@ -7278,6 +7311,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}}