Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Clang] Permit noescape on non-pointer types #9789

Open
wants to merge 1 commit into
base: stable/20240723
Choose a base branch
from
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
7 changes: 7 additions & 0 deletions clang/include/clang/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,13 @@ inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB,
DB.AddTaggedVal(reinterpret_cast<uint64_t>(At), DiagnosticsEngine::ak_attr);
return DB;
}

/// Determine if type T is a valid subject for a nonnull and similar
/// attributes. Dependent types are considered valid so they can be checked
/// during instantiation time. By default, we look through references (the
/// behavior used by nonnull), but if the second parameter is true, then we
/// treat a reference type as valid.
bool isValidPointerAttrType(QualType T, bool RefOkay = false);
} // end namespace clang

#endif
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -3355,6 +3355,9 @@ def warn_attribute_return_pointers_refs_only : Warning<
def warn_attribute_pointer_or_reference_only : Warning<
"%0 attribute only applies to a pointer or reference (%1 is invalid)">,
InGroup<IgnoredAttributes>;
def warn_attribute_pointer_or_reference_or_record_only : Warning<
"%0 attribute only applies to a pointer, reference, class, struct, or union (%1 is invalid)">,
InGroup<IgnoredAttributes>;
def err_attribute_no_member_pointers : Error<
"%0 attribute cannot be used with pointers to members">;
def err_attribute_invalid_implicit_this_argument : Error<
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ FEATURE(attribute_overloadable, true)
FEATURE(attribute_unavailable_with_message, true)
FEATURE(attribute_unused_on_fields, true)
FEATURE(attribute_diagnose_if_objc, true)
FEATURE(attribute_noescape_nonpointer, true)
FEATURE(blocks, LangOpts.Blocks)
FEATURE(c_thread_safety_attributes, true)
FEATURE(cxx_exceptions, LangOpts.CXXExceptions)
Expand Down
6 changes: 0 additions & 6 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -4663,12 +4663,6 @@ class Sema final : public SemaBase {
StringRef &Str,
SourceLocation *ArgLocation = nullptr);

/// Determine if type T is a valid subject for a nonnull and similar
/// attributes. By default, we look through references (the behavior used by
/// nonnull), but if the second parameter is true, then we treat a reference
/// type as valid.
bool isValidPointerAttrType(QualType T, bool RefOkay = false);

/// AddAssumeAlignedAttr - Adds an assume_aligned attribute to a particular
/// declaration.
void AddAssumeAlignedAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E,
Expand Down
26 changes: 26 additions & 0 deletions clang/lib/AST/AttrImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,4 +270,30 @@ unsigned AlignedAttr::getAlignment(ASTContext &Ctx) const {
return Ctx.getTargetDefaultAlignForAttributeAligned();
}

bool clang::isValidPointerAttrType(QualType T, bool RefOkay) {
if (T->isDependentType())
return true;
if (RefOkay) {
if (T->isReferenceType())
return true;
} else {
T = T.getNonReferenceType();
}

// The nonnull attribute, and other similar attributes, can be applied to a
// transparent union that contains a pointer type.
if (const RecordType *UT = T->getAsUnionType()) {
if (UT && UT->getDecl()->hasAttr<TransparentUnionAttr>()) {
RecordDecl *UD = UT->getDecl();
for (const auto *I : UD->fields()) {
QualType QT = I->getType();
if (QT->isAnyPointerType() || QT->isBlockPointerType())
return true;
}
}
}

return T->isAnyPointerType() || T->isBlockPointerType();
}

#include "clang/AST/AttrImpl.inc"
3 changes: 2 additions & 1 deletion clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2846,7 +2846,8 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
break;
}

if (FI.getExtParameterInfo(ArgNo).isNoEscape())
if (FI.getExtParameterInfo(ArgNo).isNoEscape() &&
isValidPointerAttrType(ParamType, /*RefOkay=*/true))
Attrs.addAttribute(llvm::Attribute::NoCapture);

if (Attrs.hasAttributes()) {
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3045,7 +3045,7 @@ static void CheckNonNullArguments(Sema &S,
if (!NonNull->args_size()) {
// Easy case: all pointer arguments are nonnull.
for (const auto *Arg : Args)
if (S.isValidPointerAttrType(Arg->getType()))
if (isValidPointerAttrType(Arg->getType()))
CheckNonNullArgument(S, Arg, CallSiteLoc);
return;
}
Expand Down
35 changes: 6 additions & 29 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1220,35 +1220,11 @@ static void handlePreferredName(Sema &S, Decl *D, const ParsedAttr &AL) {
<< TT->getDecl();
}

bool Sema::isValidPointerAttrType(QualType T, bool RefOkay) {
if (RefOkay) {
if (T->isReferenceType())
return true;
} else {
T = T.getNonReferenceType();
}

// The nonnull attribute, and other similar attributes, can be applied to a
// transparent union that contains a pointer type.
if (const RecordType *UT = T->getAsUnionType()) {
if (UT && UT->getDecl()->hasAttr<TransparentUnionAttr>()) {
RecordDecl *UD = UT->getDecl();
for (const auto *I : UD->fields()) {
QualType QT = I->getType();
if (QT->isAnyPointerType() || QT->isBlockPointerType())
return true;
}
}
}

return T->isAnyPointerType() || T->isBlockPointerType();
}

static bool attrNonNullArgCheck(Sema &S, QualType T, const ParsedAttr &AL,
SourceRange AttrParmRange,
SourceRange TypeRange,
bool isReturnValue = false) {
if (!S.isValidPointerAttrType(T)) {
if (!isValidPointerAttrType(T)) {
if (isReturnValue)
S.Diag(AL.getLoc(), diag::warn_attribute_return_pointers_only)
<< AL << AttrParmRange << TypeRange;
Expand Down Expand Up @@ -1296,7 +1272,7 @@ static void handleNonNullAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
for (unsigned I = 0, E = getFunctionOrMethodNumParams(D);
I != E && !AnyPointers; ++I) {
QualType T = getFunctionOrMethodParamType(D, I);
if (T->isDependentType() || S.isValidPointerAttrType(T)) {
if (T->isDependentType() || isValidPointerAttrType(T)) {
AnyPointers = true;
/*TO_UPSTREAM(BoundsSafety) ON*/
if (auto DCPTy = T->getAs<CountAttributedType>()) {
Expand Down Expand Up @@ -1355,10 +1331,11 @@ static void handleNoEscapeAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
if (D->isInvalidDecl())
return;

// noescape only applies to pointer types.
// noescape only applies to pointer and record types.
QualType T = cast<ParmVarDecl>(D)->getType();
if (!S.isValidPointerAttrType(T, /* RefOkay */ true)) {
S.Diag(AL.getLoc(), diag::warn_attribute_pointers_only)
if (!isValidPointerAttrType(T, /* RefOkay */ true) && !T->isRecordType()) {
S.Diag(AL.getLoc(),
diag::warn_attribute_pointer_or_reference_or_record_only)
<< AL << AL.getRange() << 0;
return;
}
Expand Down
8 changes: 7 additions & 1 deletion clang/test/AST/ast-dump-attr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,14 @@ __attribute__((external_source_symbol(generated_declaration, defined_in="module"
// CHECK-NEXT: ExternalSourceSymbolAttr{{.*}} "Swift" "module" GeneratedDeclaration "testUSR"

namespace TestNoEscape {
struct S { int *p; };
void noescapeFunc(int *p0, __attribute__((noescape)) int *p1) {}
// CHECK: `-FunctionDecl{{.*}} noescapeFunc 'void (int *, __attribute__((noescape)) int *)'
// CHECK: |-FunctionDecl{{.*}} noescapeFunc 'void (int *, __attribute__((noescape)) int *)'
// CHECK-NEXT: ParmVarDecl
// CHECK-NEXT: ParmVarDecl
// CHECK-NEXT: NoEscapeAttr
void noescapeFunc2(int *p0, __attribute__((noescape)) S p1) {}
// CHECK: `-FunctionDecl{{.*}} noescapeFunc2 'void (int *, __attribute__((noescape)) S)'
// CHECK-NEXT: ParmVarDecl
// CHECK-NEXT: ParmVarDecl
// CHECK-NEXT: NoEscapeAttr
Expand Down
4 changes: 4 additions & 0 deletions clang/test/CodeGenCXX/noescape.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ struct S {
S &operator=(int * __attribute__((noescape)));
void m0(int *, int * __attribute__((noescape)));
virtual void vm1(int *, int * __attribute__((noescape)));
virtual void vm2(int *, S __attribute__((noescape)));
};

// CHECK: define{{.*}} void @_ZN1SC2EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} nocapture noundef {{%.*}})
Expand All @@ -23,6 +24,9 @@ void S::m0(int *, int * __attribute__((noescape))) {}
// CHECK: define{{.*}} void @_ZN1S3vm1EPiS0_(ptr {{.*}}, {{.*}} nocapture noundef {{%.*}})
void S::vm1(int *, int * __attribute__((noescape))) {}

// CHECK-NOT: nocapture
void S::vm2(int *, S __attribute__((noescape))) {}

// CHECK-LABEL: define{{.*}} void @_Z5test0P1SPiS1_(
// CHECK: call void @_ZN1SC1EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} nocapture noundef {{.*}})
// CHECK: call {{.*}} ptr @_ZN1SaSEPi(ptr {{.*}}, {{.*}} nocapture noundef {{.*}})
Expand Down
10 changes: 10 additions & 0 deletions clang/test/CodeGenObjC/noescape.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@
long long *ll;
} __attribute__((transparent_union));

struct S {
int *p;
};

void escapingFunc0(BlockTy);
void noescapeFunc0(id, __attribute__((noescape)) BlockTy);
void noescapeFunc1(__attribute__((noescape)) int *);
void noescapeFunc2(__attribute__((noescape)) id);
void noescapeFunc3(__attribute__((noescape)) union U);
void noescapeFunc4(__attribute__((noescape)) struct S s);

// Block descriptors of non-escaping blocks don't need pointers to copy/dispose
// helper functions.
Expand Down Expand Up @@ -53,6 +58,11 @@ void test3(union U u) {
noescapeFunc3(u);
}

// CHECK-NOT: nocapture
void testNonPtr(struct S s) {
noescapeFunc4(s);
}

// CHECK: define internal void @"\01-[C0 m0:]"({{.*}}, {{.*}}, {{.*}} nocapture {{.*}})

// CHECK-LABEL: define{{.*}} void @test4(
Expand Down
13 changes: 13 additions & 0 deletions clang/test/SemaCXX/noescape-attr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s

template<typename T>
void test1(T __attribute__((noescape)) arr, int size);

void test2(int __attribute__((noescape)) a, int b); // expected-warning {{'noescape' attribute only applies to a pointer, reference, class, struct, or union (0 is invalid)}}

struct S { int *p; };
void test3(S __attribute__((noescape)) s);

#if !__has_feature(attribute_noescape_nonpointer)
#error "attribute_noescape_nonpointer should be supported"
#endif
6 changes: 3 additions & 3 deletions clang/test/SemaObjCXX/noescape.mm
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@
template <class T>
void noescapeFunc7(__attribute__((noescape)) T &&);

void invalidFunc0(int __attribute__((noescape))); // expected-warning {{'noescape' attribute only applies to pointer arguments}}
void invalidFunc0(int __attribute__((noescape))); // expected-warning {{'noescape' attribute only applies to a pointer, reference, class, struct, or union (0 is invalid)}}
void invalidFunc1(int __attribute__((noescape(0)))); // expected-error {{'noescape' attribute takes no arguments}}
void invalidFunc2(int0 *__attribute__((noescape))); // expected-error {{use of undeclared identifier 'int0'; did you mean 'int'?}}
void invalidFunc3(__attribute__((noescape)) int (S::*Ty)); // expected-warning {{'noescape' attribute only applies to pointer arguments}}
void invalidFunc4(__attribute__((noescape)) void (S::*Ty)()); // expected-warning {{'noescape' attribute only applies to pointer arguments}}
void invalidFunc3(__attribute__((noescape)) int (S::*Ty)); // expected-warning {{'noescape' attribute only applies to a pointer, reference, class, struct, or union (0 is invalid)}}
void invalidFunc4(__attribute__((noescape)) void (S::*Ty)()); // expected-warning {{'noescape' attribute only applies to a pointer, reference, class, struct, or union (0 is invalid)}}
int __attribute__((noescape)) g; // expected-warning {{'noescape' attribute only applies to parameters}}

struct S1 {
Expand Down