Skip to content

Commit e465c25

Browse files
author
Gabor Horvath
committed
[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 9f6c3d7 commit e465c25

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 {{.*}})

0 commit comments

Comments
 (0)