Index: cfe/trunk/include/clang/AST/ASTContext.h =================================================================== --- cfe/trunk/include/clang/AST/ASTContext.h +++ cfe/trunk/include/clang/AST/ASTContext.h @@ -2413,9 +2413,30 @@ QualType mergeObjCGCQualifiers(QualType, QualType); - bool doFunctionTypesMatchOnExtParameterInfos( - const FunctionProtoType *FromFunctionType, - const FunctionProtoType *ToFunctionType); + /// This function merges the ExtParameterInfo lists of two functions. It + /// returns true if the lists are compatible. The merged list is returned in + /// NewParamInfos. + /// + /// \param FirstFnType The type of the first function. + /// + /// \param SecondFnType The type of the second function. + /// + /// \param CanUseFirst This flag is set to true if the first function's + /// ExtParameterInfo list can be used as the composite list of + /// ExtParameterInfo. + /// + /// \param CanUseSecond This flag is set to true if the second function's + /// ExtParameterInfo list can be used as the composite list of + /// ExtParameterInfo. + /// + /// \param NewParamInfos The composite list of ExtParameterInfo. The list is + /// empty if none of the flags are set. + /// + bool mergeExtParameterInfo( + const FunctionProtoType *FirstFnType, + const FunctionProtoType *SecondFnType, + bool &CanUseFirst, bool &CanUseSecond, + SmallVectorImpl &NewParamInfos); void ResetObjCLayout(const ObjCContainerDecl *CD); Index: cfe/trunk/include/clang/AST/Type.h =================================================================== --- cfe/trunk/include/clang/AST/Type.h +++ cfe/trunk/include/clang/AST/Type.h @@ -3149,6 +3149,7 @@ ABIMask = 0x0F, IsConsumed = 0x10, HasPassObjSize = 0x20, + IsNoEscape = 0x40, }; unsigned char Data; @@ -3189,6 +3190,19 @@ return Copy; } + bool isNoEscape() const { + return Data & IsNoEscape; + } + + ExtParameterInfo withIsNoEscape(bool NoEscape) const { + ExtParameterInfo Copy = *this; + if (NoEscape) + Copy.Data |= IsNoEscape; + else + Copy.Data &= ~IsNoEscape; + return Copy; + } + unsigned char getOpaqueValue() const { return Data; } static ExtParameterInfo getFromOpaqueValue(unsigned char data) { ExtParameterInfo result; Index: cfe/trunk/include/clang/Basic/Attr.td =================================================================== --- cfe/trunk/include/clang/Basic/Attr.td +++ cfe/trunk/include/clang/Basic/Attr.td @@ -1396,6 +1396,12 @@ let Documentation = [Undocumented]; } +def NoEscape : Attr { + let Spellings = [GNU<"noescape">, CXX11<"clang", "noescape">]; + let Subjects = SubjectList<[ParmVar]>; + let Documentation = [NoEscapeDocs]; +} + def AssumeAligned : InheritableAttr { let Spellings = [GCC<"assume_aligned">]; let Subjects = SubjectList<[ObjCMethod, Function]>; Index: cfe/trunk/include/clang/Basic/AttrDocs.td =================================================================== --- cfe/trunk/include/clang/Basic/AttrDocs.td +++ cfe/trunk/include/clang/Basic/AttrDocs.td @@ -130,6 +130,47 @@ }]; } +def NoEscapeDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +``noescape`` placed on a function parameter of a pointer type is used to inform +the compiler that the pointer cannot escape: that is, no reference to the object +the pointer points to that is derived from the parameter value will survive +after the function returns. Users are responsible for making sure parameters +annotated with ``noescape`` do not actuallly escape. + +For example: + +.. code-block:: c + int *gp; + + void nonescapingFunc(__attribute__((noescape)) int *p) { + *p += 100; // OK. + } + + void escapingFunc(__attribute__((noescape)) int *p) { + gp = p; // Not OK. + } + +Additionally, when the parameter is a `block pointer +`, the same restriction +applies to copies of the block. For example: + + typedef void (^BlockTy)(); + BlockTy g0, g1; + + void nonescapingFunc(__attribute__((noescape)) BlockTy block) { + block(); // OK. + } + + void escapingFunc(__attribute__((noescape)) BlockTy block) { + g0 = block; // Not OK. + g1 = Block_copy(block); // Not OK either. + } + + }]; +} + def CarriesDependencyDocs : Documentation { let Category = DocCatFunction; let Content = [{ Index: cfe/trunk/include/clang/Basic/DiagnosticGroups.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticGroups.td +++ cfe/trunk/include/clang/Basic/DiagnosticGroups.td @@ -90,6 +90,7 @@ DiagGroup<"gnu-string-literal-operator-template">; def UndefinedVarTemplate : DiagGroup<"undefined-var-template">; def UndefinedFuncTemplate : DiagGroup<"undefined-func-template">; +def MissingNoEscape : DiagGroup<"missing-noescape">; def DeleteIncomplete : DiagGroup<"delete-incomplete">; def DeleteNonVirtualDtor : DiagGroup<"delete-non-virtual-dtor">; Index: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td @@ -1661,6 +1661,11 @@ "virtual function %0 has different calling convention attributes " "%diff{($) than the function it overrides (which has calling convention $)|" "than the function it overrides}1,2">; +def warn_overriding_method_missing_noescape : Warning< + "parameter of overriding method should be annotated with " + "__attribute__((noescape))">, InGroup; +def note_overridden_marked_noescape : Note< + "parameter of overridden method is annotated with __attribute__((noescape))">; def err_covariant_return_inaccessible_base : Error< "invalid covariant return for virtual function: %1 is a " Index: cfe/trunk/lib/AST/ASTContext.cpp =================================================================== --- cfe/trunk/lib/AST/ASTContext.cpp +++ cfe/trunk/lib/AST/ASTContext.cpp @@ -7968,9 +7968,17 @@ if (lproto->getTypeQuals() != rproto->getTypeQuals()) return QualType(); - if (!doFunctionTypesMatchOnExtParameterInfos(rproto, lproto)) + SmallVector newParamInfos; + bool canUseLeft, canUseRight; + if (!mergeExtParameterInfo(lproto, rproto, canUseLeft, canUseRight, + newParamInfos)) return QualType(); + if (!canUseLeft) + allLTypes = false; + if (!canUseRight) + allRTypes = false; + // Check parameter type compatibility SmallVector types; for (unsigned i = 0, n = lproto->getNumParams(); i < n; i++) { @@ -8001,6 +8009,8 @@ FunctionProtoType::ExtProtoInfo EPI = lproto->getExtProtoInfo(); EPI.ExtInfo = einfo; + EPI.ExtParameterInfos = + newParamInfos.empty() ? nullptr : newParamInfos.data(); return getFunctionType(retType, types, EPI); } @@ -8360,26 +8370,50 @@ llvm_unreachable("Invalid Type::Class!"); } -bool ASTContext::doFunctionTypesMatchOnExtParameterInfos( - const FunctionProtoType *firstFnType, - const FunctionProtoType *secondFnType) { - // Fast path: if the first type doesn't have ext parameter infos, - // we match if and only if they second type also doesn't have them. - if (!firstFnType->hasExtParameterInfos()) - return !secondFnType->hasExtParameterInfos(); +bool ASTContext::mergeExtParameterInfo( + const FunctionProtoType *FirstFnType, const FunctionProtoType *SecondFnType, + bool &CanUseFirst, bool &CanUseSecond, + SmallVectorImpl &NewParamInfos) { + assert(NewParamInfos.empty() && "param info list not empty"); + CanUseFirst = CanUseSecond = true; + bool FirstHasInfo = FirstFnType->hasExtParameterInfos(); + bool SecondHasInfo = SecondFnType->hasExtParameterInfos(); - // Otherwise, we can only match if the second type has them. - if (!secondFnType->hasExtParameterInfos()) - return false; + // Fast path: if the first type doesn't have ext parameter infos, + // we match if and only if the second type also doesn't have them. + if (!FirstHasInfo && !SecondHasInfo) + return true; - auto firstEPI = firstFnType->getExtParameterInfos(); - auto secondEPI = secondFnType->getExtParameterInfos(); - assert(firstEPI.size() == secondEPI.size()); + bool NeedParamInfo = false; + size_t E = FirstHasInfo ? FirstFnType->getExtParameterInfos().size() + : SecondFnType->getExtParameterInfos().size(); + + for (size_t I = 0; I < E; ++I) { + FunctionProtoType::ExtParameterInfo FirstParam, SecondParam; + if (FirstHasInfo) + FirstParam = FirstFnType->getExtParameterInfo(I); + if (SecondHasInfo) + SecondParam = SecondFnType->getExtParameterInfo(I); - for (size_t i = 0, n = firstEPI.size(); i != n; ++i) { - if (firstEPI[i] != secondEPI[i]) + // Cannot merge unless everything except the noescape flag matches. + if (FirstParam.withIsNoEscape(false) != SecondParam.withIsNoEscape(false)) return false; + + bool FirstNoEscape = FirstParam.isNoEscape(); + bool SecondNoEscape = SecondParam.isNoEscape(); + bool IsNoEscape = FirstNoEscape && SecondNoEscape; + NewParamInfos.push_back(FirstParam.withIsNoEscape(IsNoEscape)); + if (NewParamInfos.back().getOpaqueValue()) + NeedParamInfo = true; + if (FirstNoEscape != IsNoEscape) + CanUseFirst = false; + if (SecondNoEscape != IsNoEscape) + CanUseSecond = false; } + + if (!NeedParamInfo) + NewParamInfos.clear(); + return true; } Index: cfe/trunk/lib/AST/ItaniumMangle.cpp =================================================================== --- cfe/trunk/lib/AST/ItaniumMangle.cpp +++ cfe/trunk/lib/AST/ItaniumMangle.cpp @@ -2643,6 +2643,9 @@ if (PI.isConsumed()) mangleVendorQualifier("ns_consumed"); + + if (PI.isNoEscape()) + mangleVendorQualifier("noescape"); } // ::= Index: cfe/trunk/lib/AST/TypePrinter.cpp =================================================================== --- cfe/trunk/lib/AST/TypePrinter.cpp +++ cfe/trunk/lib/AST/TypePrinter.cpp @@ -665,6 +665,8 @@ auto EPI = T->getExtParameterInfo(i); if (EPI.isConsumed()) OS << "__attribute__((ns_consumed)) "; + if (EPI.isNoEscape()) + OS << "__attribute__((noescape)) "; auto ABI = EPI.getABI(); if (ABI != ParameterABI::Ordinary) OS << "__attribute__((" << getParameterABISpelling(ABI) << ")) "; Index: cfe/trunk/lib/CodeGen/CGCall.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGCall.cpp +++ cfe/trunk/lib/CodeGen/CGCall.cpp @@ -455,11 +455,15 @@ CodeGenTypes::arrangeObjCMessageSendSignature(const ObjCMethodDecl *MD, QualType receiverType) { SmallVector argTys; + SmallVector extParamInfos(2); argTys.push_back(Context.getCanonicalParamType(receiverType)); argTys.push_back(Context.getCanonicalParamType(Context.getObjCSelType())); // FIXME: Kill copy? for (const auto *I : MD->parameters()) { argTys.push_back(Context.getCanonicalParamType(I->getType())); + auto extParamInfo = FunctionProtoType::ExtParameterInfo().withIsNoEscape( + I->hasAttr()); + extParamInfos.push_back(extParamInfo); } FunctionType::ExtInfo einfo; @@ -475,7 +479,7 @@ return arrangeLLVMFunctionInfo( GetReturnType(MD->getReturnType()), /*instanceMethod=*/false, - /*chainCall=*/false, argTys, einfo, {}, required); + /*chainCall=*/false, argTys, einfo, extParamInfos, required); } const CGFunctionInfo & @@ -2093,6 +2097,9 @@ break; } + if (FI.getExtParameterInfo(ArgNo).isNoEscape()) + Attrs.addAttribute(llvm::Attribute::NoCapture); + if (Attrs.hasAttributes()) { unsigned FirstIRArg, NumIRArgs; std::tie(FirstIRArg, NumIRArgs) = IRFunctionArgs.getIRArgs(ArgNo); Index: cfe/trunk/lib/Sema/SemaDeclAttr.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaDeclAttr.cpp +++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp @@ -1531,6 +1531,22 @@ Attr.getAttributeSpellingListIndex())); } +static void handleNoEscapeAttr(Sema &S, Decl *D, const AttributeList &Attr) { + if (D->isInvalidDecl()) + return; + + // noescape only applies to pointer types. + QualType T = cast(D)->getType(); + if (!S.isValidPointerAttrType(T, /* RefOkay */ true)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_pointers_only) + << Attr.getName() << Attr.getRange() << 0; + return; + } + + D->addAttr(::new (S.Context) NoEscapeAttr( + Attr.getRange(), S.Context, Attr.getAttributeSpellingListIndex())); +} + static void handleAssumeAlignedAttr(Sema &S, Decl *D, const AttributeList &Attr) { Expr *E = Attr.getArgAsExpr(0), @@ -6167,6 +6183,9 @@ case AttributeList::AT_ReturnsNonNull: handleReturnsNonNullAttr(S, D, Attr); break; + case AttributeList::AT_NoEscape: + handleNoEscapeAttr(S, D, Attr); + break; case AttributeList::AT_AssumeAligned: handleAssumeAlignedAttr(S, D, Attr); break; Index: cfe/trunk/lib/Sema/SemaDeclCXX.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaDeclCXX.cpp +++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp @@ -14081,8 +14081,21 @@ bool Sema::CheckOverridingFunctionAttributes(const CXXMethodDecl *New, const CXXMethodDecl *Old) { - const FunctionType *NewFT = New->getType()->getAs(); - const FunctionType *OldFT = Old->getType()->getAs(); + const auto *NewFT = New->getType()->getAs(); + const auto *OldFT = Old->getType()->getAs(); + + if (OldFT->hasExtParameterInfos()) { + for (unsigned I = 0, E = OldFT->getNumParams(); I != E; ++I) + // A parameter of the overriding method should be annotated with noescape + // if the corresponding parameter of the overridden method is annotated. + if (OldFT->getExtParameterInfo(I).isNoEscape() && + !NewFT->getExtParameterInfo(I).isNoEscape()) { + Diag(New->getParamDecl(I)->getLocation(), + diag::warn_overriding_method_missing_noescape); + Diag(Old->getParamDecl(I)->getLocation(), + diag::note_overridden_marked_noescape); + } + } CallingConv NewCC = NewFT->getCallConv(), OldCC = OldFT->getCallConv(); Index: cfe/trunk/lib/Sema/SemaDeclObjC.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaDeclObjC.cpp +++ cfe/trunk/lib/Sema/SemaDeclObjC.cpp @@ -188,6 +188,14 @@ Diag(newDecl->getLocation(), diag::warn_nsconsumed_attribute_mismatch); Diag(oldDecl->getLocation(), diag::note_previous_decl) << "parameter"; } + + // A parameter of the overriding method should be annotated with noescape + // if the corresponding parameter of the overridden method is annotated. + if (oldDecl->hasAttr() && !newDecl->hasAttr()) { + Diag(newDecl->getLocation(), + diag::warn_overriding_method_missing_noescape); + Diag(oldDecl->getLocation(), diag::note_overridden_marked_noescape); + } } } Index: cfe/trunk/lib/Sema/SemaOverload.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaOverload.cpp +++ cfe/trunk/lib/Sema/SemaOverload.cpp @@ -1481,6 +1481,23 @@ .getTypePtr()); Changed = true; } + + // Convert FromFPT's ExtParameterInfo if necessary. The conversion is valid + // only if the ExtParameterInfo lists of the two function prototypes can be + // merged and the merged list is identical to ToFPT's ExtParameterInfo list. + SmallVector NewParamInfos; + bool CanUseToFPT, CanUseFromFPT; + if (Context.mergeExtParameterInfo(ToFPT, FromFPT, CanUseToFPT, + CanUseFromFPT, NewParamInfos) && + CanUseToFPT && !CanUseFromFPT) { + FunctionProtoType::ExtProtoInfo ExtInfo = FromFPT->getExtProtoInfo(); + ExtInfo.ExtParameterInfos = + NewParamInfos.empty() ? nullptr : NewParamInfos.data(); + QualType QT = Context.getFunctionType(FromFPT->getReturnType(), + FromFPT->getParamTypes(), ExtInfo); + FromFn = QT->getAs(); + Changed = true; + } } if (!Changed) @@ -2663,8 +2680,12 @@ // Argument types are too different. Abort. return false; } - if (!Context.doFunctionTypesMatchOnExtParameterInfos(FromFunctionType, - ToFunctionType)) + + SmallVector NewParamInfos; + bool CanUseToFPT, CanUseFromFPT; + if (!Context.mergeExtParameterInfo(ToFunctionType, FromFunctionType, + CanUseToFPT, CanUseFromFPT, + NewParamInfos)) return false; ConvertedType = ToType; Index: cfe/trunk/lib/Sema/SemaType.cpp =================================================================== --- cfe/trunk/lib/Sema/SemaType.cpp +++ cfe/trunk/lib/Sema/SemaType.cpp @@ -4479,6 +4479,11 @@ HasAnyInterestingExtParameterInfos = true; } + if (Param->hasAttr()) { + ExtParameterInfos[i] = ExtParameterInfos[i].withIsNoEscape(true); + HasAnyInterestingExtParameterInfos = true; + } + ParamTys.push_back(ParamTy); } Index: cfe/trunk/test/CodeGenCXX/noescape.cpp =================================================================== --- cfe/trunk/test/CodeGenCXX/noescape.cpp +++ cfe/trunk/test/CodeGenCXX/noescape.cpp @@ -0,0 +1,67 @@ +// RUN: %clang_cc1 -std=c++11 -emit-llvm -o - %s | FileCheck %s + +struct S { + int a[4]; + S(int *, int * __attribute__((noescape))); + S &operator=(int * __attribute__((noescape))); + void m0(int *, int * __attribute__((noescape))); + virtual void vm1(int *, int * __attribute__((noescape))); +}; + +// CHECK: define void @_ZN1SC2EPiS0_(%struct.S* {{.*}}, {{.*}}, {{.*}} nocapture) +// CHECK: define void @_ZN1SC1EPiS0_(%struct.S* {{.*}}, {{.*}}, {{.*}} nocapture) {{.*}} { +// CHECK: call void @_ZN1SC2EPiS0_(%struct.S* {{.*}}, {{.*}}, {{.*}} nocapture {{.*}}) + +S::S(int *, int * __attribute__((noescape))) {} + +// CHECK: define {{.*}} %struct.S* @_ZN1SaSEPi(%struct.S* {{.*}}, {{.*}} nocapture) +S &S::operator=(int * __attribute__((noescape))) { return *this; } + +// CHECK: define void @_ZN1S2m0EPiS0_(%struct.S* {{.*}}, {{.*}} nocapture) +void S::m0(int *, int * __attribute__((noescape))) {} + +// CHECK: define void @_ZN1S3vm1EPiS0_(%struct.S* {{.*}}, {{.*}} nocapture) +void S::vm1(int *, int * __attribute__((noescape))) {} + +// CHECK-LABEL: define void @_Z5test0P1SPiS1_( +// CHECK: call void @_ZN1SC1EPiS0_(%struct.S* {{.*}}, {{.*}}, {{.*}} nocapture {{.*}}) +// CHECK: call {{.*}} %struct.S* @_ZN1SaSEPi(%struct.S* {{.*}}, {{.*}} nocapture {{.*}}) +// CHECK: call void @_ZN1S2m0EPiS0_(%struct.S* {{.*}}, {{.*}}, {{.*}} nocapture {{.*}}) +// CHECK: call void {{.*}}(%struct.S* {{.*}}, {{.*}}, {{.*}} nocapture {{.*}}) +void test0(S *s, int *p0, int *p1) { + S t(p0, p1); + t = p1; + s->m0(p0, p1); + s->vm1(p0, p1); +} + +namespace std { + typedef decltype(sizeof(0)) size_t; +} + +// CHECK: define {{.*}} @_ZnwmPv({{.*}}, {{.*}} nocapture {{.*}}) +void *operator new(std::size_t, void * __attribute__((noescape)) p) { + return p; +} + +// CHECK-LABEL: define i8* @_Z5test1Pv( +// CHECK : %call = call {{.*}} @_ZnwmPv({{.*}}, {{.*}} nocapture {{.*}}) +void *test1(void *p0) { + return ::operator new(16, p0); +} + +// CHECK-LABEL: define void @_Z5test2PiS_( +// CHECK: call void @"_ZZ5test2PiS_ENK3$_0clES_S_"({{.*}}, {{.*}}, {{.*}} nocapture {{.*}}) +// CHECK: define internal void @"_ZZ5test2PiS_ENK3$_0clES_S_"({{.*}}, {{.*}}, {{.*}} nocapture) +void test2(int *p0, int *p1) { + auto t = [](int *, int * __attribute__((noescape))){}; + t(p0, p1); +} + +// CHECK-LABEL: define void @_Z5test3PFvU8noescapePiES_( +// CHECK: call void {{.*}}(i32* nocapture {{.*}}) +typedef void (*NoEscapeFunc)(__attribute__((noescape)) int *); + +void test3(NoEscapeFunc f, int *p) { + f(p); +} Index: cfe/trunk/test/CodeGenObjC/noescape.m =================================================================== --- cfe/trunk/test/CodeGenObjC/noescape.m +++ cfe/trunk/test/CodeGenObjC/noescape.m @@ -0,0 +1,71 @@ +// RUN: %clang_cc1 -fblocks -emit-llvm -o - %s | FileCheck %s + +typedef void (^BlockTy)(void); + +union U { + int *i; + long long *ll; +} __attribute__((transparent_union)); + +void noescapeFunc0(id, __attribute__((noescape)) BlockTy); +void noescapeFunc1(__attribute__((noescape)) int *); +void noescapeFunc2(__attribute__((noescape)) id); +void noescapeFunc3(__attribute__((noescape)) union U); + +// CHECK-LABEL: define void @test0( +// CHECK: call void @noescapeFunc0({{.*}}, {{.*}} nocapture {{.*}}) +// CHECK: declare void @noescapeFunc0(i8*, {{.*}} nocapture) +void test0(BlockTy b) { + noescapeFunc0(0, b); +} + +// CHECK-LABEL: define void @test1( +// CHECK: call void @noescapeFunc1({{.*}} nocapture {{.*}}) +// CHECK: declare void @noescapeFunc1({{.*}} nocapture) +void test1(int *i) { + noescapeFunc1(i); +} + +// CHECK-LABEL: define void @test2( +// CHECK: call void @noescapeFunc2({{.*}} nocapture {{.*}}) +// CHECK: declare void @noescapeFunc2({{.*}} nocapture) +void test2(id i) { + noescapeFunc2(i); +} + +// CHECK-LABEL: define void @test3( +// CHECK: call void @noescapeFunc3({{.*}} nocapture {{.*}}) +// CHECK: declare void @noescapeFunc3({{.*}} nocapture) +void test3(union U u) { + noescapeFunc3(u); +} + +// CHECK: define internal void @"\01-[C0 m0:]"({{.*}}, {{.*}}, {{.*}} nocapture {{.*}}) + +// CHECK-LABEL: define void @test4( +// CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*, i32*)*)(i8* {{.*}}, i8* {{.*}}, i32* nocapture {{.*}}) + +@interface C0 +-(void) m0:(int*)__attribute__((noescape)) p0; +@end + +@implementation C0 +-(void) m0:(int*)__attribute__((noescape)) p0 { +} +@end + +void test4(C0 *c0, int *p) { + [c0 m0:p]; +} + +// CHECK-LABEL: define void @test5( +// CHECK: call void {{.*}}(i8* bitcast ({ i8**, i32, i32, i8*, {{.*}} }* @{{.*}} to i8*), i32* nocapture {{.*}}) +// CHECK: call void {{.*}}(i8* {{.*}}, i32* nocapture {{.*}}) +// CHECK: define internal void @{{.*}}(i8* {{.*}}, i32* nocapture {{.*}}) + +typedef void (^BlockTy2)(__attribute__((noescape)) int *); + +void test5(BlockTy2 b, int *p) { + ^(int *__attribute__((noescape)) p0){}(p); + b(p); +} Index: cfe/trunk/test/Misc/ast-dump-attr.cpp =================================================================== --- cfe/trunk/test/Misc/ast-dump-attr.cpp +++ cfe/trunk/test/Misc/ast-dump-attr.cpp @@ -180,6 +180,14 @@ // CHECK: FunctionDecl{{.*}} TestExternalSourceSymbolAttr5 // CHECK-NEXT: ExternalSourceSymbolAttr{{.*}} "Swift" "module" GeneratedDeclaration +namespace TestNoEscape { + void noescapeFunc(int *p0, __attribute__((noescape)) int *p1) {} + // CHECK: `-FunctionDecl{{.*}} noescapeFunc 'void (int *, __attribute__((noescape)) int *)' + // CHECK-NEXT: ParmVarDecl + // CHECK-NEXT: ParmVarDecl + // CHECK-NEXT: NoEscapeAttr +} + namespace TestSuppress { [[gsl::suppress("at-namespace")]]; // CHECK: NamespaceDecl{{.*}} TestSuppress Index: cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test =================================================================== --- cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test +++ cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test @@ -2,7 +2,7 @@ // The number of supported attributes should never go down! -// CHECK: #pragma clang attribute supports 64 attributes: +// CHECK: #pragma clang attribute supports 65 attributes: // CHECK-NEXT: AMDGPUFlatWorkGroupSize (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumSGPR (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumVGPR (SubjectMatchRule_function) @@ -35,6 +35,7 @@ // CHECK-NEXT: MipsShortCall (SubjectMatchRule_function) // CHECK-NEXT: NoDebug (SubjectMatchRule_hasType_functionType, SubjectMatchRule_objc_method, SubjectMatchRule_variable_not_is_parameter) // CHECK-NEXT: NoDuplicate (SubjectMatchRule_function) +// CHECK-NEXT: NoEscape (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: NoMicroMips (SubjectMatchRule_function) // CHECK-NEXT: NoSanitize (SubjectMatchRule_function, SubjectMatchRule_objc_method, SubjectMatchRule_variable_is_global) // CHECK-NEXT: NoSanitizeSpecific (SubjectMatchRule_function, SubjectMatchRule_variable_is_global) Index: cfe/trunk/test/Sema/noescape.c =================================================================== --- cfe/trunk/test/Sema/noescape.c +++ cfe/trunk/test/Sema/noescape.c @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +void escapefunc(int *); +void noescapefunc(__attribute__((noescape)) int *); +void (*escapefuncptr)(int *); +void (*noescapefuncptr)(__attribute__((noescape)) int *); + +void func_ne(__attribute__((noescape)) int *, int *); +void func_en(int *, __attribute__((noescape)) int *); + +void (*funcptr_ee)(int *, int *); +void (*funcptr_nn)(__attribute__((noescape)) int *, __attribute__((noescape)) int *); + +void test0(int c) { + escapefuncptr = &escapefunc; + escapefuncptr = &noescapefunc; + noescapefuncptr = &escapefunc; // expected-warning {{incompatible function pointer types assigning to 'void (*)(__attribute__((noescape)) int *)' from 'void (*)(int *)'}} + noescapefuncptr = &noescapefunc; + + escapefuncptr = c ? &escapefunc : &noescapefunc; + noescapefuncptr = c ? &escapefunc : &noescapefunc; // expected-warning {{incompatible function pointer types assigning to 'void (*)(__attribute__((noescape)) int *)' from 'void (*)(int *)'}} + + funcptr_ee = c ? &func_ne : &func_en; + funcptr_nn = c ? &func_ne : &func_en; // expected-warning {{incompatible function pointer types assigning to 'void (*)(__attribute__((noescape)) int *, __attribute__((noescape)) int *)' from 'void (*)(int *, int *)'}} +} Index: cfe/trunk/test/SemaObjCXX/noescape.mm =================================================================== --- cfe/trunk/test/SemaObjCXX/noescape.mm +++ cfe/trunk/test/SemaObjCXX/noescape.mm @@ -0,0 +1,90 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -fblocks -std=c++11 %s +// RUN: %clang_cc1 -fsyntax-only -verify -fblocks -std=c++1z %s + +typedef void (^BlockTy)(); + +struct S { + int i; + void m(); +}; + +void noescapeFunc0(id, __attribute__((noescape)) BlockTy); +void noescapeFunc1(id, [[clang::noescape]] BlockTy); +void noescapeFunc2(__attribute__((noescape)) int *); // expected-note {{previous declaration is here}} +void noescapeFunc3(__attribute__((noescape)) id); +void noescapeFunc4(__attribute__((noescape)) int &); +void noescapeFunc2(int *); // expected-error {{conflicting types for 'noescapeFunc2'}} + +void invalidFunc0(int __attribute__((noescape))); // expected-warning {{'noescape' attribute only applies to pointer arguments}} +void invalidFunc1(int __attribute__((noescape(0)))); // expected-error {{'noescape' attribute takes no arguments}} +void invalidFunc2(int0 *__attribute__((noescape))); // expected-error {{use of undeclared identifier 'int0'; did you mean 'int'?}} +void invalidFunc3(__attribute__((noescape)) int (S::*Ty)); // expected-warning {{'noescape' attribute only applies to pointer arguments}} +void invalidFunc4(__attribute__((noescape)) void (S::*Ty)()); // expected-warning {{'noescape' attribute only applies to pointer arguments}} +int __attribute__((noescape)) g; // expected-warning {{'noescape' attribute only applies to parameters}} + +struct S1 { + virtual void m0(int *__attribute__((noescape))); // expected-note {{parameter of overridden method is annotated with __attribute__((noescape))}} +}; + +struct S2 : S1 { + void m0(int *__attribute__((noescape))) override; +}; + +struct S3 : S1 { + void m0(int *) override; // expected-warning {{parameter of overriding method should be annotated with __attribute__((noescape))}} +}; + +__attribute__((objc_root_class)) +@interface C0 +-(void) m0:(int*)__attribute__((noescape)) p; // expected-note {{parameter of overridden method is annotated with __attribute__((noescape))}} +@end + +@implementation C0 +-(void) m0:(int*)__attribute__((noescape)) p {} +@end + +@interface C1 : C0 +-(void) m0:(int*)__attribute__((noescape)) p; +@end + +@implementation C1 : C0 +-(void) m0:(int*)__attribute__((noescape)) p {} +@end + +@interface C2 : C0 +-(void) m0:(int*) p; // expected-warning {{parameter of overriding method should be annotated with __attribute__((noescape))}} +@end + +@implementation C2 : C0 +-(void) m0:(int*) p {} +@end + +void func0(int *); +void (*fnptr0)(int *); +void (*fnptr1)(__attribute__((noescape)) int *); +template struct S4 {}; +template struct S5 {}; + +#if __cplusplus < 201406 + // expected-note@-4 {{template parameter is declared here}} + // expected-note@-4 {{template parameter is declared here}} +#endif + +void test0() { + fnptr0 = &func0; + fnptr0 = &noescapeFunc2; + fnptr1 = &func0; // expected-error {{assigning to 'void (*)(__attribute__((noescape)) int *)' from incompatible type 'void (*)(int *)'}} + fnptr1 = &noescapeFunc2; + S4<&func0> e0; + S4<&noescapeFunc2> e1; + S5<&func0> ne0; + +#if __cplusplus < 201406 + // expected-error@-4 {{non-type template argument of type 'void (*)(__attribute__((noescape)) int *)' cannot be converted to a value of type 'void (*)(int *)'}} + // expected-error@-4 {{non-type template argument of type 'void (*)(int *)' cannot be converted to a value of type 'void (*)(__attribute__((noescape)) int *)'}} +#else + // expected-error@-6 {{value of type 'void (*)(int *)' is not implicitly convertible to 'void (*)(__attribute__((noescape)) int *)'}} +#endif + + S5<&noescapeFunc2> ne1; +}