Skip to content

Commit 06a77c5

Browse files
authored
Merge pull request #9789 from swiftlang/gaborh/noescape-nonptr
[Clang] Permit noescape on non-pointer types
2 parents 9f6c3d7 + e465c25 commit 06a77c5

File tree

14 files changed

+87
-46
lines changed

14 files changed

+87
-46
lines changed

clang/include/clang/AST/Attr.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,13 @@ inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB,
380380
DB.AddTaggedVal(reinterpret_cast<uint64_t>(At), DiagnosticsEngine::ak_attr);
381381
return DB;
382382
}
383+
384+
/// Determine if type T is a valid subject for a nonnull and similar
385+
/// attributes. Dependent types are considered valid so they can be checked
386+
/// during instantiation time. By default, we look through references (the
387+
/// behavior used by nonnull), but if the second parameter is true, then we
388+
/// treat a reference type as valid.
389+
bool isValidPointerAttrType(QualType T, bool RefOkay = false);
383390
} // end namespace clang
384391

385392
#endif

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3355,6 +3355,9 @@ def warn_attribute_return_pointers_refs_only : Warning<
33553355
def warn_attribute_pointer_or_reference_only : Warning<
33563356
"%0 attribute only applies to a pointer or reference (%1 is invalid)">,
33573357
InGroup<IgnoredAttributes>;
3358+
def warn_attribute_pointer_or_reference_or_record_only : Warning<
3359+
"%0 attribute only applies to a pointer, reference, class, struct, or union (%1 is invalid)">,
3360+
InGroup<IgnoredAttributes>;
33583361
def err_attribute_no_member_pointers : Error<
33593362
"%0 attribute cannot be used with pointers to members">;
33603363
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
@@ -86,6 +86,7 @@ FEATURE(attribute_overloadable, true)
8686
FEATURE(attribute_unavailable_with_message, true)
8787
FEATURE(attribute_unused_on_fields, true)
8888
FEATURE(attribute_diagnose_if_objc, true)
89+
FEATURE(attribute_noescape_nonpointer, true)
8990
FEATURE(blocks, LangOpts.Blocks)
9091
FEATURE(c_thread_safety_attributes, true)
9192
FEATURE(cxx_exceptions, LangOpts.CXXExceptions)

clang/include/clang/Sema/Sema.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4663,12 +4663,6 @@ class Sema final : public SemaBase {
46634663
StringRef &Str,
46644664
SourceLocation *ArgLocation = nullptr);
46654665

4666-
/// Determine if type T is a valid subject for a nonnull and similar
4667-
/// attributes. By default, we look through references (the behavior used by
4668-
/// nonnull), but if the second parameter is true, then we treat a reference
4669-
/// type as valid.
4670-
bool isValidPointerAttrType(QualType T, bool RefOkay = false);
4671-
46724666
/// AddAssumeAlignedAttr - Adds an assume_aligned attribute to a particular
46734667
/// declaration.
46744668
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
@@ -270,4 +270,30 @@ unsigned AlignedAttr::getAlignment(ASTContext &Ctx) const {
270270
return Ctx.getTargetDefaultAlignForAttributeAligned();
271271
}
272272

273+
bool clang::isValidPointerAttrType(QualType T, bool RefOkay) {
274+
if (T->isDependentType())
275+
return true;
276+
if (RefOkay) {
277+
if (T->isReferenceType())
278+
return true;
279+
} else {
280+
T = T.getNonReferenceType();
281+
}
282+
283+
// The nonnull attribute, and other similar attributes, can be applied to a
284+
// transparent union that contains a pointer type.
285+
if (const RecordType *UT = T->getAsUnionType()) {
286+
if (UT && UT->getDecl()->hasAttr<TransparentUnionAttr>()) {
287+
RecordDecl *UD = UT->getDecl();
288+
for (const auto *I : UD->fields()) {
289+
QualType QT = I->getType();
290+
if (QT->isAnyPointerType() || QT->isBlockPointerType())
291+
return true;
292+
}
293+
}
294+
}
295+
296+
return T->isAnyPointerType() || T->isBlockPointerType();
297+
}
298+
273299
#include "clang/AST/AttrImpl.inc"

clang/lib/CodeGen/CGCall.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2846,7 +2846,8 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
28462846
break;
28472847
}
28482848

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

28522853
if (Attrs.hasAttributes()) {

clang/lib/Sema/SemaChecking.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3045,7 +3045,7 @@ static void CheckNonNullArguments(Sema &S,
30453045
if (!NonNull->args_size()) {
30463046
// Easy case: all pointer arguments are nonnull.
30473047
for (const auto *Arg : Args)
3048-
if (S.isValidPointerAttrType(Arg->getType()))
3048+
if (isValidPointerAttrType(Arg->getType()))
30493049
CheckNonNullArgument(S, Arg, CallSiteLoc);
30503050
return;
30513051
}

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1220,35 +1220,11 @@ static void handlePreferredName(Sema &S, Decl *D, const ParsedAttr &AL) {
12201220
<< TT->getDecl();
12211221
}
12221222

1223-
bool Sema::isValidPointerAttrType(QualType T, bool RefOkay) {
1224-
if (RefOkay) {
1225-
if (T->isReferenceType())
1226-
return true;
1227-
} else {
1228-
T = T.getNonReferenceType();
1229-
}
1230-
1231-
// The nonnull attribute, and other similar attributes, can be applied to a
1232-
// transparent union that contains a pointer type.
1233-
if (const RecordType *UT = T->getAsUnionType()) {
1234-
if (UT && UT->getDecl()->hasAttr<TransparentUnionAttr>()) {
1235-
RecordDecl *UD = UT->getDecl();
1236-
for (const auto *I : UD->fields()) {
1237-
QualType QT = I->getType();
1238-
if (QT->isAnyPointerType() || QT->isBlockPointerType())
1239-
return true;
1240-
}
1241-
}
1242-
}
1243-
1244-
return T->isAnyPointerType() || T->isBlockPointerType();
1245-
}
1246-
12471223
static bool attrNonNullArgCheck(Sema &S, QualType T, const ParsedAttr &AL,
12481224
SourceRange AttrParmRange,
12491225
SourceRange TypeRange,
12501226
bool isReturnValue = false) {
1251-
if (!S.isValidPointerAttrType(T)) {
1227+
if (!isValidPointerAttrType(T)) {
12521228
if (isReturnValue)
12531229
S.Diag(AL.getLoc(), diag::warn_attribute_return_pointers_only)
12541230
<< AL << AttrParmRange << TypeRange;
@@ -1296,7 +1272,7 @@ static void handleNonNullAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
12961272
for (unsigned I = 0, E = getFunctionOrMethodNumParams(D);
12971273
I != E && !AnyPointers; ++I) {
12981274
QualType T = getFunctionOrMethodParamType(D, I);
1299-
if (T->isDependentType() || S.isValidPointerAttrType(T)) {
1275+
if (T->isDependentType() || isValidPointerAttrType(T)) {
13001276
AnyPointers = true;
13011277
/*TO_UPSTREAM(BoundsSafety) ON*/
13021278
if (auto DCPTy = T->getAs<CountAttributedType>()) {
@@ -1355,10 +1331,11 @@ static void handleNoEscapeAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
13551331
if (D->isInvalidDecl())
13561332
return;
13571333

1358-
// noescape only applies to pointer types.
1334+
// noescape only applies to pointer and record types.
13591335
QualType T = cast<ParmVarDecl>(D)->getType();
1360-
if (!S.isValidPointerAttrType(T, /* RefOkay */ true)) {
1361-
S.Diag(AL.getLoc(), diag::warn_attribute_pointers_only)
1336+
if (!isValidPointerAttrType(T, /* RefOkay */ true) && !T->isRecordType()) {
1337+
S.Diag(AL.getLoc(),
1338+
diag::warn_attribute_pointer_or_reference_or_record_only)
13621339
<< AL << AL.getRange() << 0;
13631340
return;
13641341
}

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 {{.*}}, {{.*}}, {{.*}} nocapture noundef {{%.*}})
@@ -23,6 +24,9 @@ void S::m0(int *, int * __attribute__((noescape))) {}
2324
// CHECK: define{{.*}} void @_ZN1S3vm1EPiS0_(ptr {{.*}}, {{.*}} nocapture noundef {{%.*}})
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 {{.*}}, {{.*}}, {{.*}} nocapture noundef {{.*}})
2832
// CHECK: call {{.*}} ptr @_ZN1SaSEPi(ptr {{.*}}, {{.*}} nocapture noundef {{.*}})

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:]"({{.*}}, {{.*}}, {{.*}} nocapture {{.*}})
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: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify %s
2+
3+
template<typename T>
4+
void test1(T __attribute__((noescape)) arr, int size);
5+
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: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,17 @@
1717
void noescapeFunc2(int *); // expected-error {{conflicting types for 'noescapeFunc2'}}
1818

1919
template <class T>
20-
void noescapeFunc5(__attribute__((noescape)) T); // expected-warning {{'noescape' attribute only applies to pointer arguments}}
20+
void noescapeFunc5(__attribute__((noescape)) T);
2121
template <class T>
2222
void noescapeFunc6(__attribute__((noescape)) const T &);
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 {
@@ -144,7 +144,7 @@ -(void) m1:(int*) p {
144144

145145
struct S6 {
146146
S6();
147-
S6(const S6 &) = delete; // expected-note 12 {{'S6' has been explicitly marked deleted here}}
147+
S6(const S6 &) = delete; // expected-note 11 {{'S6' has been explicitly marked deleted here}}
148148
int f;
149149
};
150150

@@ -161,7 +161,7 @@ void test1(C0 *c0) {
161161
__block S6 b6; // expected-error {{call to deleted constructor of 'S6'}}
162162
__block S6 b7; // expected-error {{call to deleted constructor of 'S6'}}
163163
__block S6 b8; // expected-error {{call to deleted constructor of 'S6'}}
164-
__block S6 b9; // expected-error {{call to deleted constructor of 'S6'}}
164+
__block S6 b9;
165165
__block S6 b10; // expected-error {{call to deleted constructor of 'S6'}}
166166
__block S6 b11; // expected-error {{call to deleted constructor of 'S6'}}
167167
__block S6 b12;
@@ -199,7 +199,6 @@ void test1(C0 *c0) {
199199
(void)b8;
200200
});
201201

202-
// FIXME: clang shouldn't reject this.
203202
noescapeFunc5(^{
204203
(void)b9;
205204
});

0 commit comments

Comments
 (0)