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

0 commit comments

Comments
 (0)