Skip to content

Commit ac8c2f1

Browse files
authored
Merge pull request #10735 from swiftlang/egorzhdan/next-noescape
🍒[Clang] Permit noescape on non-pointer types
2 parents 37d6eda + 1b291eb commit ac8c2f1

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
@@ -3424,6 +3424,9 @@ def warn_attribute_return_pointers_refs_only : Warning<
34243424
def warn_attribute_pointer_or_reference_only : Warning<
34253425
"%0 attribute only applies to a pointer or reference (%1 is invalid)">,
34263426
InGroup<IgnoredAttributes>;
3427+
def warn_attribute_pointer_or_reference_or_record_only : Warning<
3428+
"%0 attribute only applies to a pointer, reference, class, struct, or union (%1 is invalid)">,
3429+
InGroup<IgnoredAttributes>;
34273430
def err_attribute_no_member_pointers : Error<
34283431
"%0 attribute cannot be used with pointers to members">;
34293432
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
@@ -5262,13 +5262,6 @@ class Sema final : public SemaBase {
52625262
StringRef &Str,
52635263
SourceLocation *ArgLocation = nullptr);
52645264

5265-
/// Determine if type T is a valid subject for a nonnull and similar
5266-
/// attributes. Dependent types are considered valid so they can be checked
5267-
/// during instantiation time. By default, we look through references (the
5268-
/// behavior used by nonnull), but if the second parameter is true, then we
5269-
/// treat a reference type as valid.
5270-
bool isValidPointerAttrType(QualType T, bool RefOkay = false);
5271-
52725265
/// AddAssumeAlignedAttr - Adds an assume_aligned attribute to a particular
52735266
/// declaration.
52745267
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
@@ -2971,7 +2971,8 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
29712971
break;
29722972
}
29732973

2974-
if (FI.getExtParameterInfo(ArgNo).isNoEscape())
2974+
if (FI.getExtParameterInfo(ArgNo).isNoEscape() &&
2975+
isValidPointerAttrType(ParamType, /*RefOkay=*/true))
29752976
Attrs.addCapturesAttr(llvm::CaptureInfo::none());
29762977

29772978
if (Attrs.hasAttributes()) {

clang/lib/Sema/SemaChecking.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3215,7 +3215,7 @@ static void CheckNonNullArguments(Sema &S,
32153215
if (!NonNull->args_size()) {
32163216
// Easy case: all pointer arguments are nonnull.
32173217
for (const auto *Arg : Args)
3218-
if (S.isValidPointerAttrType(Arg->getType()))
3218+
if (isValidPointerAttrType(Arg->getType()))
32193219
CheckNonNullArgument(S, Arg, CallSiteLoc);
32203220
return;
32213221
}

clang/lib/Sema/SemaDeclAttr.cpp

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

1233-
bool Sema::isValidPointerAttrType(QualType T, bool RefOkay) {
1234-
if (T->isDependentType())
1235-
return true;
1236-
if (RefOkay) {
1237-
if (T->isReferenceType())
1238-
return true;
1239-
} else {
1240-
T = T.getNonReferenceType();
1241-
}
1242-
1243-
// The nonnull attribute, and other similar attributes, can be applied to a
1244-
// transparent union that contains a pointer type.
1245-
if (const RecordType *UT = T->getAsUnionType()) {
1246-
if (UT && UT->getDecl()->hasAttr<TransparentUnionAttr>()) {
1247-
RecordDecl *UD = UT->getDecl();
1248-
for (const auto *I : UD->fields()) {
1249-
QualType QT = I->getType();
1250-
if (QT->isAnyPointerType() || QT->isBlockPointerType())
1251-
return true;
1252-
}
1253-
}
1254-
}
1255-
1256-
return T->isAnyPointerType() || T->isBlockPointerType();
1257-
}
1258-
12591233
static bool attrNonNullArgCheck(Sema &S, QualType T, const ParsedAttr &AL,
12601234
SourceRange AttrParmRange,
12611235
SourceRange TypeRange,
12621236
bool isReturnValue = false) {
1263-
if (!S.isValidPointerAttrType(T)) {
1237+
if (!isValidPointerAttrType(T)) {
12641238
if (isReturnValue)
12651239
S.Diag(AL.getLoc(), diag::warn_attribute_return_pointers_only)
12661240
<< AL << AttrParmRange << TypeRange;
@@ -1311,7 +1285,7 @@ static void handleNonNullAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
13111285
for (unsigned I = 0, E = getFunctionOrMethodNumParams(D);
13121286
I != E && !AnyPointers; ++I) {
13131287
QualType T = getFunctionOrMethodParamType(D, I);
1314-
if (T->isDependentType() || S.isValidPointerAttrType(T)) {
1288+
if (T->isDependentType() || isValidPointerAttrType(T)) {
13151289
AnyPointers = true;
13161290
/*TO_UPSTREAM(BoundsSafety) ON*/
13171291
if (auto DCPTy = T->getAs<CountAttributedType>()) {
@@ -1370,10 +1344,11 @@ static void handleNoEscapeAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
13701344
if (D->isInvalidDecl())
13711345
return;
13721346

1373-
// noescape only applies to pointer types.
1347+
// noescape only applies to pointer and record types.
13741348
QualType T = cast<ParmVarDecl>(D)->getType();
1375-
if (!S.isValidPointerAttrType(T, /* RefOkay */ true)) {
1376-
S.Diag(AL.getLoc(), diag::warn_attribute_pointers_only)
1349+
if (!isValidPointerAttrType(T, /* RefOkay */ true) && !T->isRecordType()) {
1350+
S.Diag(AL.getLoc(),
1351+
diag::warn_attribute_pointer_or_reference_or_record_only)
13771352
<< AL << AL.getRange() << 0;
13781353
return;
13791354
}

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)