Skip to content

Commit 1b291eb

Browse files
Gabor Horvathegorzhdan
authored andcommitted
[Clang] Permit noescape on non-pointer types
In modern C++ we often use span, string_view or other view objects instead of raw pointers. This PR opens the door to mark those noescape. This can be useful to document the API contracts, for interop with memory safe languages like Swift or Rust, and possibly in the future to implement take this into account in codegen. The PR also adds a feature test macro. The goal is to help avoiding warnings when the code is compiler by earlier versions of clang that does not permit this attribute on non-pointer types. rdar://140196481
1 parent cf9619b commit 1b291eb

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
@@ -3401,6 +3401,9 @@ def warn_attribute_return_pointers_refs_only : Warning<
34013401
def warn_attribute_pointer_or_reference_only : Warning<
34023402
"%0 attribute only applies to a pointer or reference (%1 is invalid)">,
34033403
InGroup<IgnoredAttributes>;
3404+
def warn_attribute_pointer_or_reference_or_record_only : Warning<
3405+
"%0 attribute only applies to a pointer, reference, class, struct, or union (%1 is invalid)">,
3406+
InGroup<IgnoredAttributes>;
34043407
def err_attribute_no_member_pointers : Error<
34053408
"%0 attribute cannot be used with pointers to members">;
34063409
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
@@ -5249,13 +5249,6 @@ class Sema final : public SemaBase {
52495249
StringRef &Str,
52505250
SourceLocation *ArgLocation = nullptr);
52515251

5252-
/// Determine if type T is a valid subject for a nonnull and similar
5253-
/// attributes. Dependent types are considered valid so they can be checked
5254-
/// during instantiation time. By default, we look through references (the
5255-
/// behavior used by nonnull), but if the second parameter is true, then we
5256-
/// treat a reference type as valid.
5257-
bool isValidPointerAttrType(QualType T, bool RefOkay = false);
5258-
52595252
/// AddAssumeAlignedAttr - Adds an assume_aligned attribute to a particular
52605253
/// declaration.
52615254
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
@@ -1231,37 +1231,11 @@ static void handleNoSpecializations(Sema &S, Decl *D, const ParsedAttr &AL) {
12311231
NoSpecializationsAttr::Create(S.Context, Message, AL));
12321232
}
12331233

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

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

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)