diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h index 693c7a2be7c7d..0b1fcec3bb286 100644 --- a/clang/include/clang/AST/Attr.h +++ b/clang/include/clang/AST/Attr.h @@ -409,6 +409,13 @@ inline ParameterABI ParameterABIAttr::getABI() const { llvm_unreachable("bad parameter ABI attribute kind"); } } + +/// 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 diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 172158f6c437b..94387b1e22a8e 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3401,6 +3401,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; +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; def err_attribute_no_member_pointers : Error< "%0 attribute cannot be used with pointers to members">; def err_attribute_invalid_implicit_this_argument : Error< diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index 0943100247049..caac5de63192a 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -88,6 +88,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) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 1cad98f3d0cad..014710cc76641 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5249,13 +5249,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. 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); - /// AddAssumeAlignedAttr - Adds an assume_aligned attribute to a particular /// declaration. void AddAssumeAlignedAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E, diff --git a/clang/lib/AST/AttrImpl.cpp b/clang/lib/AST/AttrImpl.cpp index c0ca9d0722ee0..65efbc758878f 100644 --- a/clang/lib/AST/AttrImpl.cpp +++ b/clang/lib/AST/AttrImpl.cpp @@ -281,4 +281,30 @@ StringLiteral *FormatMatchesAttr::getFormatString() const { return cast(getExpectedFormat()); } +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()) { + 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" diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 7f8fada8e53ed..7842b12f0ea11 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -2971,7 +2971,8 @@ void CodeGenModule::ConstructAttributeList(StringRef Name, break; } - if (FI.getExtParameterInfo(ArgNo).isNoEscape()) + if (FI.getExtParameterInfo(ArgNo).isNoEscape() && + isValidPointerAttrType(ParamType, /*RefOkay=*/true)) Attrs.addCapturesAttr(llvm::CaptureInfo::none()); if (Attrs.hasAttributes()) { diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index c6a04cd4e9cb4..a4bc79e382b35 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -3215,7 +3215,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; } diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index baacc9832dacc..507a179ca03ca 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -1231,37 +1231,11 @@ static void handleNoSpecializations(Sema &S, Decl *D, const ParsedAttr &AL) { NoSpecializationsAttr::Create(S.Context, Message, AL)); } -bool Sema::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()) { - 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; @@ -1312,7 +1286,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()) { @@ -1371,10 +1345,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(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; } diff --git a/clang/test/AST/ast-dump-attr.cpp b/clang/test/AST/ast-dump-attr.cpp index f5a7481571421..ba32f91a8daf6 100644 --- a/clang/test/AST/ast-dump-attr.cpp +++ b/clang/test/AST/ast-dump-attr.cpp @@ -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 diff --git a/clang/test/CodeGenCXX/noescape.cpp b/clang/test/CodeGenCXX/noescape.cpp index c3fc90e2ea54d..b44a46ad855f9 100644 --- a/clang/test/CodeGenCXX/noescape.cpp +++ b/clang/test/CodeGenCXX/noescape.cpp @@ -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 {{.*}}, {{.*}}, {{.*}} noundef captures(none) {{%.*}}) @@ -23,6 +24,9 @@ void S::m0(int *, int * __attribute__((noescape))) {} // CHECK: define{{.*}} void @_ZN1S3vm1EPiS0_(ptr {{.*}}, {{.*}} noundef captures(none) {{%.*}}) 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 {{.*}}, {{.*}}, {{.*}} noundef captures(none) {{.*}}) // CHECK: call {{.*}} ptr @_ZN1SaSEPi(ptr {{.*}}, {{.*}} noundef captures(none) {{.*}}) diff --git a/clang/test/CodeGenObjC/noescape.m b/clang/test/CodeGenObjC/noescape.m index e1dbc0eb92e54..91321b565cc30 100644 --- a/clang/test/CodeGenObjC/noescape.m +++ b/clang/test/CodeGenObjC/noescape.m @@ -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. @@ -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:]"({{.*}}, {{.*}}, {{.*}} captures(none) {{.*}}) // CHECK-LABEL: define{{.*}} void @test4( diff --git a/clang/test/Sema/attr-noescape.c b/clang/test/Sema/attr-noescape.c index d342654281934..1748e403f12b0 100644 --- a/clang/test/Sema/attr-noescape.c +++ b/clang/test/Sema/attr-noescape.c @@ -8,5 +8,5 @@ int *global_var __attribute((noescape)); // expected-warning{{'noescape' attribu void foo(__attribute__((noescape)) int *int_ptr, __attribute__((noescape)) int (^block)(int), - __attribute((noescape)) int integer) { // expected-warning{{'noescape' attribute only applies to pointer arguments}} + __attribute((noescape)) int integer) { // expected-warning{{'noescape' attribute only applies to a pointer, reference, class, struct, or union (0 is invalid)}} } diff --git a/clang/test/SemaCXX/noescape-attr.cpp b/clang/test/SemaCXX/noescape-attr.cpp index 78dc4f07ffef8..fd54e09d1118b 100644 --- a/clang/test/SemaCXX/noescape-attr.cpp +++ b/clang/test/SemaCXX/noescape-attr.cpp @@ -3,5 +3,11 @@ template void test1(T __attribute__((noescape)) arr, int size); -// expected-warning@+1 {{'noescape' attribute only applies to pointer arguments}} -void test2(int __attribute__((noescape)) arr, int size); \ No newline at end of file +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 diff --git a/clang/test/SemaObjCXX/noescape.mm b/clang/test/SemaObjCXX/noescape.mm index 4484e3b955ac0..cccd9a2f28f3f 100644 --- a/clang/test/SemaObjCXX/noescape.mm +++ b/clang/test/SemaObjCXX/noescape.mm @@ -23,11 +23,11 @@ template 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 {