diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h index f07861f50fe8c..3ff66ddbf7edc 100644 --- a/clang/include/clang/AST/TypeBase.h +++ b/clang/include/clang/AST/TypeBase.h @@ -2601,6 +2601,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { bool isFunctionProtoType() const { return getAs(); } bool isPointerType() const; bool isPointerOrReferenceType() const; + bool isSpanLikeType() const; bool isSignableType(const ASTContext &Ctx) const; bool isSignablePointerType() const; bool isSignableIntegerType(const ASTContext &Ctx) const; diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 4548af17e37f2..9c59640c6ca49 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -5753,3 +5753,23 @@ StringRef PredefinedSugarType::getName(Kind KD) { } llvm_unreachable("unexpected kind"); } + +bool Type::isSpanLikeType() const { + // 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. + const RecordDecl *RD = 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(); +} diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 465f3f4e670c2..6c3aab528dc47 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -2507,7 +2507,8 @@ void CodeGenModule::ConstructAttributeList(StringRef Name, FuncAttrs.addAttribute(llvm::Attribute::NoUnwind); } if (const auto *RA = TargetDecl->getAttr(); - RA && RA->getDeallocator() == nullptr) + RA && RA->getDeallocator() == nullptr && + FI.getReturnType()->getAs()) RetAttrs.addAttribute(llvm::Attribute::NoAlias); if (TargetDecl->hasAttr() && !CodeGenOpts.NullPointerIsValid) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 964a2a791e18f..9f1943fd7f5a0 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -571,7 +571,7 @@ static void handleAllocSizeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { assert(isFuncOrMethodForAttrSubject(D) && hasFunctionProto(D)); QualType RetTy = getFunctionOrMethodResultType(D); - if (!RetTy->isPointerType()) { + if (!RetTy->isPointerType() && !RetTy->isSpanLikeType()) { S.Diag(AL.getLoc(), diag::warn_attribute_return_pointers_only) << AL; return; } @@ -1750,7 +1750,8 @@ static void handleTLSModelAttr(Sema &S, Decl *D, const ParsedAttr &AL) { static void handleRestrictAttr(Sema &S, Decl *D, const ParsedAttr &AL) { QualType ResultType = getFunctionOrMethodResultType(D); - if (!ResultType->isAnyPointerType() && !ResultType->isBlockPointerType()) { + if (!ResultType->isAnyPointerType() && !ResultType->isBlockPointerType() && + !ResultType->isSpanLikeType()) { S.Diag(AL.getLoc(), diag::warn_attribute_return_pointers_only) << AL << getFunctionOrMethodResultSourceRange(D); return; diff --git a/clang/test/CodeGen/attr-malloc.c b/clang/test/CodeGen/attr-malloc.c index e69f8bce55f3b..238ca95a13be1 100644 --- a/clang/test/CodeGen/attr-malloc.c +++ b/clang/test/CodeGen/attr-malloc.c @@ -3,10 +3,17 @@ int *Mem; void dealloc(int*); +typedef struct { + void *p; + int size; +} sized_ptr; + __attribute__((malloc)) int *MallocFunc(){ return Mem;} // CHECK: define[[BEFORE:.*]] noalias[[AFTER:.*]]@MallocFunc -// Ensure these two do not generate noalias here. +// Ensure these three do not generate noalias here. __attribute__((malloc(dealloc))) int *MallocFunc2(){ return Mem;} // CHECK: define[[BEFORE]][[AFTER]]@MallocFunc2 __attribute__((malloc(dealloc, 1))) int *MallocFunc3(){ return Mem;} // CHECK: define[[BEFORE]][[AFTER]]@MallocFunc3 +__attribute__((malloc)) sized_ptr MallocFunc4(){ return (sized_ptr){ .p = Mem };} +// CHECK: define[[BEFORE]] { ptr, i32 } @MallocFunc4 diff --git a/clang/test/Sema/alloc-size.c b/clang/test/Sema/alloc-size.c index 93714894a630a..29681fd483f41 100644 --- a/clang/test/Sema/alloc-size.c +++ b/clang/test/Sema/alloc-size.c @@ -30,6 +30,14 @@ void *KR() __attribute__((alloc_size(1))); //expected-warning{{'alloc_size' attr void *(__attribute__((alloc_size(1))) * func_ptr1)(int); void *(__attribute__((alloc_size(1, 2))) func_ptr2)(int, int); +// Applying alloc_size to functions returning a struct with a pointer as a first field should work. +typedef struct { + void* p; + int n; +} sized_ptr; + +sized_ptr sized_ptr_alloc(int len) __attribute__((alloc_size(1))); + // TODO: according to GCC documentation the following should actually be the type // “pointer to pointer to alloc_size attributed function returning void*” and should // therefore be supported diff --git a/clang/test/Sema/attr-malloc.c b/clang/test/Sema/attr-malloc.c index a431aa43969d7..e65787bbc3f6a 100644 --- a/clang/test/Sema/attr-malloc.c +++ b/clang/test/Sema/attr-malloc.c @@ -13,6 +13,32 @@ int returns_int (void) __attribute((malloc)); // expected-warning {{attribut int * returns_intptr(void) __attribute((malloc)); // no-warning typedef int * iptr; iptr returns_iptr (void) __attribute((malloc)); // no-warning +typedef struct { + void *ptr; + size_t n; +} sized_ptr; +sized_ptr returns_sized_ptr (void) __attribute((malloc)); // 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)); // expected-warning {{attribute only applies to return values that are pointers}} + +typedef struct { + void *ptr; + void *ptr2; +} invalid_span2; +invalid_span2 returns_non_std_span2 (void) __attribute((malloc)); // expected-warning {{attribute only applies to return values that are pointers}} + +typedef struct { + void *ptr; + size_t n; + size_t n2; +} invalid_span3; +invalid_span3 returns_non_std_span3 (void) __attribute((malloc)); // expected-warning {{attribute only applies to return values that are pointers}} __attribute((malloc)) void *(*f)(void); // expected-warning{{attribute only applies to functions}} __attribute((malloc)) int (*g)(void); // expected-warning{{attribute only applies to functions}}