Skip to content

Commit 4ee2a50

Browse files
authored
Merge pull request #10788 from AnthonyLatsis/stable/20250402
[stable/20250402][Clang] Permit noescape on non-pointer types
2 parents 7ad2696 + fabfb79 commit 4ee2a50

File tree

14 files changed

+79
-47
lines changed

14 files changed

+79
-47
lines changed

clang/include/clang/AST/Attr.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,13 @@ inline ParameterABI ParameterABIAttr::getABI() const {
409409
llvm_unreachable("bad parameter ABI attribute kind");
410410
}
411411
}
412+
413+
/// Determine if type T is a valid subject for a nonnull and similar
414+
/// attributes. Dependent types are considered valid so they can be checked
415+
/// during instantiation time. By default, we look through references (the
416+
/// behavior used by nonnull), but if the second parameter is true, then we
417+
/// treat a reference type as valid.
418+
bool isValidPointerAttrType(QualType T, bool RefOkay = false);
412419
} // end namespace clang
413420

414421
#endif

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3358,6 +3358,9 @@ def warn_attribute_return_pointers_refs_only : Warning<
33583358
def warn_attribute_pointer_or_reference_only : Warning<
33593359
"%0 attribute only applies to a pointer or reference (%1 is invalid)">,
33603360
InGroup<IgnoredAttributes>;
3361+
def warn_attribute_pointer_or_reference_or_record_only : Warning<
3362+
"%0 attribute only applies to a pointer, reference, class, struct, or union (%1 is invalid)">,
3363+
InGroup<IgnoredAttributes>;
33613364
def err_attribute_no_member_pointers : Error<
33623365
"%0 attribute cannot be used with pointers to members">;
33633366
def err_attribute_invalid_implicit_this_argument : Error<

clang/include/clang/Basic/Features.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ FEATURE(attribute_overloadable, true)
8888
FEATURE(attribute_unavailable_with_message, true)
8989
FEATURE(attribute_unused_on_fields, true)
9090
FEATURE(attribute_diagnose_if_objc, true)
91+
FEATURE(attribute_noescape_nonpointer, true)
9192
FEATURE(blocks, LangOpts.Blocks)
9293
FEATURE(c_thread_safety_attributes, true)
9394
FEATURE(cxx_exceptions, LangOpts.CXXExceptions)

clang/include/clang/Sema/Sema.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4967,13 +4967,6 @@ class Sema final : public SemaBase {
49674967
StringRef &Str,
49684968
SourceLocation *ArgLocation = nullptr);
49694969

4970-
/// Determine if type T is a valid subject for a nonnull and similar
4971-
/// attributes. Dependent types are considered valid so they can be checked
4972-
/// during instantiation time. By default, we look through references (the
4973-
/// behavior used by nonnull), but if the second parameter is true, then we
4974-
/// treat a reference type as valid.
4975-
bool isValidPointerAttrType(QualType T, bool RefOkay = false);
4976-
49774970
/// AddAssumeAlignedAttr - Adds an assume_aligned attribute to a particular
49784971
/// declaration.
49794972
void AddAssumeAlignedAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E,

clang/lib/AST/AttrImpl.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,4 +281,30 @@ StringLiteral *FormatMatchesAttr::getFormatString() const {
281281
return cast<StringLiteral>(getExpectedFormat());
282282
}
283283

284+
bool clang::isValidPointerAttrType(QualType T, bool RefOkay) {
285+
if (T->isDependentType())
286+
return true;
287+
if (RefOkay) {
288+
if (T->isReferenceType())
289+
return true;
290+
} else {
291+
T = T.getNonReferenceType();
292+
}
293+
294+
// The nonnull attribute, and other similar attributes, can be applied to a
295+
// transparent union that contains a pointer type.
296+
if (const RecordType *UT = T->getAsUnionType()) {
297+
if (UT && UT->getDecl()->hasAttr<TransparentUnionAttr>()) {
298+
RecordDecl *UD = UT->getDecl();
299+
for (const auto *I : UD->fields()) {
300+
QualType QT = I->getType();
301+
if (QT->isAnyPointerType() || QT->isBlockPointerType())
302+
return true;
303+
}
304+
}
305+
}
306+
307+
return T->isAnyPointerType() || T->isBlockPointerType();
308+
}
309+
284310
#include "clang/AST/AttrImpl.inc"

clang/lib/CodeGen/CGCall.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2926,7 +2926,8 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
29262926
break;
29272927
}
29282928

2929-
if (FI.getExtParameterInfo(ArgNo).isNoEscape())
2929+
if (FI.getExtParameterInfo(ArgNo).isNoEscape() &&
2930+
isValidPointerAttrType(ParamType, /*RefOkay=*/true))
29302931
Attrs.addCapturesAttr(llvm::CaptureInfo::none());
29312932

29322933
if (Attrs.hasAttributes()) {

clang/lib/Sema/SemaChecking.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3115,7 +3115,7 @@ static void CheckNonNullArguments(Sema &S,
31153115
if (!NonNull->args_size()) {
31163116
// Easy case: all pointer arguments are nonnull.
31173117
for (const auto *Arg : Args)
3118-
if (S.isValidPointerAttrType(Arg->getType()))
3118+
if (isValidPointerAttrType(Arg->getType()))
31193119
CheckNonNullArgument(S, Arg, CallSiteLoc);
31203120
return;
31213121
}

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 6 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1273,37 +1273,11 @@ static void handleNoSpecializations(Sema &S, Decl *D, const ParsedAttr &AL) {
12731273
NoSpecializationsAttr::Create(S.Context, Message, AL));
12741274
}
12751275

1276-
bool Sema::isValidPointerAttrType(QualType T, bool RefOkay) {
1277-
if (T->isDependentType())
1278-
return true;
1279-
if (RefOkay) {
1280-
if (T->isReferenceType())
1281-
return true;
1282-
} else {
1283-
T = T.getNonReferenceType();
1284-
}
1285-
1286-
// The nonnull attribute, and other similar attributes, can be applied to a
1287-
// transparent union that contains a pointer type.
1288-
if (const RecordType *UT = T->getAsUnionType()) {
1289-
if (UT && UT->getDecl()->hasAttr<TransparentUnionAttr>()) {
1290-
RecordDecl *UD = UT->getDecl();
1291-
for (const auto *I : UD->fields()) {
1292-
QualType QT = I->getType();
1293-
if (QT->isAnyPointerType() || QT->isBlockPointerType())
1294-
return true;
1295-
}
1296-
}
1297-
}
1298-
1299-
return T->isAnyPointerType() || T->isBlockPointerType();
1300-
}
1301-
13021276
static bool attrNonNullArgCheck(Sema &S, QualType T, const ParsedAttr &AL,
13031277
SourceRange AttrParmRange,
13041278
SourceRange TypeRange,
13051279
bool isReturnValue = false) {
1306-
if (!S.isValidPointerAttrType(T)) {
1280+
if (!isValidPointerAttrType(T)) {
13071281
if (isReturnValue)
13081282
S.Diag(AL.getLoc(), diag::warn_attribute_return_pointers_only)
13091283
<< AL << AttrParmRange << TypeRange;
@@ -1351,7 +1325,7 @@ static void handleNonNullAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
13511325
for (unsigned I = 0, E = getFunctionOrMethodNumParams(D);
13521326
I != E && !AnyPointers; ++I) {
13531327
QualType T = getFunctionOrMethodParamType(D, I);
1354-
if (T->isDependentType() || S.isValidPointerAttrType(T)) {
1328+
if (T->isDependentType() || isValidPointerAttrType(T)) {
13551329
AnyPointers = true;
13561330
/*TO_UPSTREAM(BoundsSafety) ON*/
13571331
if (auto DCPTy = T->getAs<CountAttributedType>()) {
@@ -1410,10 +1384,11 @@ static void handleNoEscapeAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
14101384
if (D->isInvalidDecl())
14111385
return;
14121386

1413-
// noescape only applies to pointer types.
1387+
// noescape only applies to pointer and record types.
14141388
QualType T = cast<ParmVarDecl>(D)->getType();
1415-
if (!S.isValidPointerAttrType(T, /* RefOkay */ true)) {
1416-
S.Diag(AL.getLoc(), diag::warn_attribute_pointers_only)
1389+
if (!isValidPointerAttrType(T, /* RefOkay */ true) && !T->isRecordType()) {
1390+
S.Diag(AL.getLoc(),
1391+
diag::warn_attribute_pointer_or_reference_or_record_only)
14171392
<< AL << AL.getRange() << 0;
14181393
return;
14191394
}

clang/test/AST/ast-dump-attr.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,14 @@ __attribute__((external_source_symbol(generated_declaration, defined_in="module"
230230
// CHECK-NEXT: ExternalSourceSymbolAttr{{.*}} "Swift" "module" GeneratedDeclaration "testUSR"
231231

232232
namespace TestNoEscape {
233+
struct S { int *p; };
233234
void noescapeFunc(int *p0, __attribute__((noescape)) int *p1) {}
234-
// CHECK: `-FunctionDecl{{.*}} noescapeFunc 'void (int *, __attribute__((noescape)) int *)'
235+
// CHECK: |-FunctionDecl{{.*}} noescapeFunc 'void (int *, __attribute__((noescape)) int *)'
236+
// CHECK-NEXT: ParmVarDecl
237+
// CHECK-NEXT: ParmVarDecl
238+
// CHECK-NEXT: NoEscapeAttr
239+
void noescapeFunc2(int *p0, __attribute__((noescape)) S p1) {}
240+
// CHECK: `-FunctionDecl{{.*}} noescapeFunc2 'void (int *, __attribute__((noescape)) S)'
235241
// CHECK-NEXT: ParmVarDecl
236242
// CHECK-NEXT: ParmVarDecl
237243
// CHECK-NEXT: NoEscapeAttr

clang/test/CodeGenCXX/noescape.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ struct S {
66
S &operator=(int * __attribute__((noescape)));
77
void m0(int *, int * __attribute__((noescape)));
88
virtual void vm1(int *, int * __attribute__((noescape)));
9+
virtual void vm2(int *, S __attribute__((noescape)));
910
};
1011

1112
// CHECK: define{{.*}} void @_ZN1SC2EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} noundef captures(none) {{%.*}})
@@ -23,6 +24,9 @@ void S::m0(int *, int * __attribute__((noescape))) {}
2324
// CHECK: define{{.*}} void @_ZN1S3vm1EPiS0_(ptr {{.*}}, {{.*}} noundef captures(none) {{%.*}})
2425
void S::vm1(int *, int * __attribute__((noescape))) {}
2526

27+
// CHECK-NOT: nocapture
28+
void S::vm2(int *, S __attribute__((noescape))) {}
29+
2630
// CHECK-LABEL: define{{.*}} void @_Z5test0P1SPiS1_(
2731
// CHECK: call void @_ZN1SC1EPiS0_(ptr {{.*}}, {{.*}}, {{.*}} noundef captures(none) {{.*}})
2832
// CHECK: call {{.*}} ptr @_ZN1SaSEPi(ptr {{.*}}, {{.*}} noundef captures(none) {{.*}})

clang/test/CodeGenObjC/noescape.m

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,16 @@
88
long long *ll;
99
} __attribute__((transparent_union));
1010

11+
struct S {
12+
int *p;
13+
};
14+
1115
void escapingFunc0(BlockTy);
1216
void noescapeFunc0(id, __attribute__((noescape)) BlockTy);
1317
void noescapeFunc1(__attribute__((noescape)) int *);
1418
void noescapeFunc2(__attribute__((noescape)) id);
1519
void noescapeFunc3(__attribute__((noescape)) union U);
20+
void noescapeFunc4(__attribute__((noescape)) struct S s);
1621

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

61+
// CHECK-NOT: nocapture
62+
void testNonPtr(struct S s) {
63+
noescapeFunc4(s);
64+
}
65+
5666
// CHECK: define internal void @"\01-[C0 m0:]"({{.*}}, {{.*}}, {{.*}} captures(none) {{.*}})
5767

5868
// CHECK-LABEL: define{{.*}} void @test4(

clang/test/Sema/attr-noescape.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ int *global_var __attribute((noescape)); // expected-warning{{'noescape' attribu
88

99
void foo(__attribute__((noescape)) int *int_ptr,
1010
__attribute__((noescape)) int (^block)(int),
11-
__attribute((noescape)) int integer) { // expected-warning{{'noescape' attribute only applies to pointer arguments}}
11+
__attribute((noescape)) int integer) { // expected-warning{{'noescape' attribute only applies to a pointer, reference, class, struct, or union (0 is invalid)}}
1212
}

clang/test/SemaCXX/noescape-attr.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,11 @@
33
template<typename T>
44
void test1(T __attribute__((noescape)) arr, int size);
55

6-
// expected-warning@+1 {{'noescape' attribute only applies to pointer arguments}}
7-
void test2(int __attribute__((noescape)) arr, int size);
6+
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)}}
7+
8+
struct S { int *p; };
9+
void test3(S __attribute__((noescape)) s);
10+
11+
#if !__has_feature(attribute_noescape_nonpointer)
12+
#error "attribute_noescape_nonpointer should be supported"
13+
#endif

clang/test/SemaObjCXX/noescape.mm

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@
2323
template <class T>
2424
void noescapeFunc7(__attribute__((noescape)) T &&);
2525

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

3333
struct S1 {

0 commit comments

Comments
 (0)