Index: include/clang/AST/Type.h =================================================================== --- include/clang/AST/Type.h +++ include/clang/AST/Type.h @@ -3152,6 +3152,7 @@ ABIMask = 0x0F, IsConsumed = 0x10, HasPassObjSize = 0x20, + IsNoEscape = 0x40, }; unsigned char Data; @@ -3192,6 +3193,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: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -1395,6 +1395,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: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -112,6 +112,46 @@ }]; } +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, for block pointers, the same restriction apply to copies of +blocks. 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: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -1650,6 +1650,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))">; +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: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -1461,6 +1461,27 @@ /// in an Objective-C message declaration. Return the appropriate type. ParsedType ActOnObjCInstanceType(SourceLocation Loc); + /// This function removes the noescape attribute from FromType and returns a + /// new function prototype that can be converted to ToType. + /// + /// A function that takes a noescape parameter can be implicitly converted to + /// a function that doesn't take one. For example, the following assignment is + /// legal: + /// + /// void from1(__attribute__((noescape)) int *); + /// void (*to1)(int *) = &from1; + /// + /// If the implicit conversion described above is invalid or not needed, this + /// function returns FromType. The following code is an example of an + /// assignment that is invalid: + /// + /// void from2(int *); + /// void (*to2)(__attribute__((noescape)) int *) = &from2; + /// + const FunctionProtoType * + removeNoEscapeFromFunctionProto(const FunctionProtoType *ToType, + const FunctionProtoType *FromType); + /// \brief Abstract class used to diagnose incomplete types. struct TypeDiagnoser { TypeDiagnoser() {} Index: lib/AST/ItaniumMangle.cpp =================================================================== --- lib/AST/ItaniumMangle.cpp +++ lib/AST/ItaniumMangle.cpp @@ -2696,6 +2696,8 @@ assert(Attr->getType() <= 9 && Attr->getType() >= 0); Out << "U17pass_object_size" << Attr->getType(); } + if (FD->getParamDecl(I)->hasAttr()) + Out << "U8noescape"; } } Index: lib/AST/TypePrinter.cpp =================================================================== --- lib/AST/TypePrinter.cpp +++ 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: lib/CodeGen/CGCall.cpp =================================================================== --- lib/CodeGen/CGCall.cpp +++ 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 & @@ -2092,6 +2096,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: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -1517,6 +1517,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), @@ -6149,6 +6165,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: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -14018,8 +14018,20 @@ 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: lib/Sema/SemaDeclObjC.cpp =================================================================== --- lib/Sema/SemaDeclObjC.cpp +++ lib/Sema/SemaDeclObjC.cpp @@ -171,20 +171,28 @@ Diag(Overridden->getLocation(), diag::note_previous_decl) << "method"; } - ObjCMethodDecl::param_const_iterator oi = Overridden->param_begin(), - oe = Overridden->param_end(); - for (ObjCMethodDecl::param_iterator - ni = NewMethod->param_begin(), ne = NewMethod->param_end(); - ni != ne && oi != oe; ++ni, ++oi) { - const ParmVarDecl *oldDecl = (*oi); - ParmVarDecl *newDecl = (*ni); - if (newDecl->hasAttr() != - oldDecl->hasAttr()) { - Diag(newDecl->getLocation(), - diag::err_nsconsumed_attribute_mismatch); - Diag(oldDecl->getLocation(), diag::note_previous_decl) - << "parameter"; - } + } + + ObjCMethodDecl::param_const_iterator oi = Overridden->param_begin(), + oe = Overridden->param_end(); + for (ObjCMethodDecl::param_iterator ni = NewMethod->param_begin(), + ne = NewMethod->param_end(); + ni != ne && oi != oe; ++ni, ++oi) { + const ParmVarDecl *oldDecl = (*oi); + ParmVarDecl *newDecl = (*ni); + if (getLangOpts().ObjCAutoRefCount && + newDecl->hasAttr() != + oldDecl->hasAttr()) { + Diag(newDecl->getLocation(), diag::err_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: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -7168,6 +7168,52 @@ ColonLoc, result, VK, OK); } +const FunctionProtoType * +Sema::removeNoEscapeFromFunctionProto(const FunctionProtoType *ToType, + const FunctionProtoType *FromType) { + if (ToType->getNumParams() != FromType->getNumParams()) + return FromType; + + // FromType doesn't have noescape parameters if it doesn't + // have ExtParameterInfo. + if (!FromType->hasExtParameterInfos()) + return FromType; + + SmallVector NewParamInfos; + bool NeedNewProtoType = false, NeedExtInfo = false; + + // Create a new ExtParameterInfo list for FromType. + for (unsigned I = 0, E = FromType->getNumParams(); I != E; ++I) { + const FunctionProtoType::ExtParameterInfo ToInfo = + ToType->getExtParameterInfo(I); + const FunctionProtoType::ExtParameterInfo FromInfo = + FromType->getExtParameterInfo(I); + bool ToNoEscape = ToInfo.isNoEscape(), FromNoEscape = FromInfo.isNoEscape(); + + // We don't want to allow converting a function that doesn't take a noescape + // parameter to a function that takes one. + if (ToNoEscape && !FromNoEscape) + return FromType; + + // The IsNoEscape flag is set only when both sides are noescape. + NewParamInfos.push_back(FromInfo.withIsNoEscape(ToNoEscape && FromNoEscape)); + if (NewParamInfos.back().getOpaqueValue()) + NeedExtInfo = true; + + if (!ToNoEscape && FromNoEscape) + NeedNewProtoType = true; + } + + if (!NeedNewProtoType) + return FromType; + + FunctionProtoType::ExtProtoInfo ExtInfo = FromType->getExtProtoInfo(); + ExtInfo.ExtParameterInfos = NeedExtInfo ? NewParamInfos.data() : nullptr; + QualType FnTy = Context.getFunctionType(FromType->getReturnType(), + FromType->getParamTypes(), ExtInfo); + return cast(FnTy.getTypePtr()); +} + // checkPointerTypesForAssignment - This is a very tricky routine (despite // being closely modeled after the C99 spec:-). The odd characteristic of this // routine is it effectively iqnores the qualifiers on the top level pointee. @@ -7200,6 +7246,10 @@ rhq.removeObjCLifetime(); } + if (const auto *lhproto = dyn_cast(lhptee)) + if (const auto *rhproto = dyn_cast(rhptee)) + rhptee = S.removeNoEscapeFromFunctionProto(lhproto, rhproto); + if (!lhq.compatiblyIncludes(rhq)) { // Treat address-space mismatches as fatal. TODO: address subspaces if (!lhq.isAddressSpaceSupersetOf(rhq)) Index: lib/Sema/SemaOverload.cpp =================================================================== --- lib/Sema/SemaOverload.cpp +++ lib/Sema/SemaOverload.cpp @@ -1481,6 +1481,11 @@ .getTypePtr()); Changed = true; } + + FromFPT = cast(FromFn); + FromFn = removeNoEscapeFromFunctionProto(ToFPT, FromFPT); + if (FromFn != FromFPT) + Changed = true; } if (!Changed) Index: lib/Sema/SemaTemplate.cpp =================================================================== --- lib/Sema/SemaTemplate.cpp +++ lib/Sema/SemaTemplate.cpp @@ -5451,6 +5451,31 @@ // At this point, the template argument refers to an object or // function with external linkage. We now need to check whether the // argument and parameter types are compatible. + + // If ParamType and ArgType are pointers to function prototypes, remove + // noescape from ArgType's parameters if doing so enables implicitly + // converting ArgType to ParamType. This enables using a function taking + // noescape parameters as a template argument to instantiate a template that + // expects a function that doesn't take noescape parameters. For example: + // + // void noescapefunc(__attribute__((noescape)) int *); + // template struct S {}; + // S<&noescapefunc> s; + // + if (ParamType->isPointerType() && ArgType->isPointerType()) { + QualType ParamPointeeType = + ParamType->getAs()->getPointeeType().IgnoreParens(); + QualType ArgPointeeType = + ArgType->getAs()->getPointeeType(); + const auto *ParamProto = dyn_cast(ParamPointeeType); + const auto *ArgProto = dyn_cast(ArgPointeeType); + if (ParamProto && ArgProto) { + ArgProto = S.removeNoEscapeFromFunctionProto(ParamProto, ArgProto); + ArgType = S.Context.getPointerType( + S.Context.getQualifiedType(ArgProto, ArgType.getQualifiers())); + } + } + if (!S.Context.hasSameUnqualifiedType(ArgType, ParamType.getNonReferenceType())) { // We can't perform this conversion or binding. Index: lib/Sema/SemaType.cpp =================================================================== --- lib/Sema/SemaType.cpp +++ lib/Sema/SemaType.cpp @@ -4478,6 +4478,11 @@ HasAnyInterestingExtParameterInfos = true; } + if (Param->hasAttr()) { + ExtParameterInfos[i] = ExtParameterInfos[i].withIsNoEscape(true); + HasAnyInterestingExtParameterInfos = true; + } + ParamTys.push_back(ParamTy); } Index: test/CodeGenCXX/noescape.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/noescape.cpp @@ -0,0 +1,59 @@ +// 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_U8noescape(%struct.S* {{.*}}, {{.*}}, {{.*}} nocapture) +// CHECK: define void @_ZN1SC1EPiS0_U8noescape(%struct.S* {{.*}}, {{.*}}, {{.*}} nocapture) {{.*}} { +// CHECK: call void @_ZN1SC2EPiS0_U8noescape(%struct.S* {{.*}}, {{.*}}, {{.*}} nocapture {{.*}}) + +S::S(int *, int * __attribute__((noescape))) {} + +// CHECK: define {{.*}} %struct.S* @_ZN1SaSEPiU8noescape(%struct.S* {{.*}}, {{.*}} nocapture) +S &S::operator=(int * __attribute__((noescape))) { return *this; } + +// CHECK: define void @_ZN1S2m0EPiS0_U8noescape(%struct.S* {{.*}}, {{.*}} nocapture) +void S::m0(int *, int * __attribute__((noescape))) {} + +// CHECK: define void @_ZN1S3vm1EPiS0_U8noescape(%struct.S* {{.*}}, {{.*}} nocapture) +void S::vm1(int *, int * __attribute__((noescape))) {} + +// CHECK-LABEL: define void @_Z5test0P1SPiS1_( +// CHECK: call void @_ZN1SC1EPiS0_U8noescape(%struct.S* {{.*}}, {{.*}}, {{.*}} nocapture {{.*}}) +// CHECK: call {{.*}} %struct.S* @_ZN1SaSEPiU8noescape(%struct.S* {{.*}}, {{.*}} nocapture {{.*}}) +// CHECK: call void @_ZN1S2m0EPiS0_U8noescape(%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 {{.*}} @_ZnwmPvU8noescape({{.*}}, {{.*}} 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_U8noescape"({{.*}}, {{.*}}, {{.*}} nocapture {{.*}}) +// CHECK: define internal void @"_ZZ5test2PiS_ENK3$_0clES_S_U8noescape"({{.*}}, {{.*}}, {{.*}} nocapture) +void test2(int *p0, int *p1) { + auto t = [](int *, int * __attribute__((noescape))){}; + t(p0, p1); +} Index: test/CodeGenObjC/noescape.m =================================================================== --- /dev/null +++ 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: test/Misc/ast-dump-attr.cpp =================================================================== --- test/Misc/ast-dump-attr.cpp +++ 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: test/Misc/pragma-attribute-supported-attributes-list.test =================================================================== --- test/Misc/pragma-attribute-supported-attributes-list.test +++ 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: test/Misc/warning-flags.c =================================================================== --- test/Misc/warning-flags.c +++ test/Misc/warning-flags.c @@ -18,7 +18,7 @@ The list of warnings below should NEVER grow. It should gradually shrink to 0. -CHECK: Warnings without flags (78): +CHECK: Warnings without flags (79): CHECK-NEXT: ext_excess_initializers CHECK-NEXT: ext_excess_initializers_in_char_array_initializer CHECK-NEXT: ext_expected_semi_decl_list @@ -78,6 +78,7 @@ CHECK-NEXT: warn_objc_property_copy_missing_on_block CHECK-NEXT: warn_objc_protocol_qualifier_missing_id CHECK-NEXT: warn_on_superclass_use +CHECK-NEXT: warn_overriding_method_missing_noescape CHECK-NEXT: warn_pp_convert_to_positive CHECK-NEXT: warn_pp_expr_overflow CHECK-NEXT: warn_pp_line_decimal Index: test/Sema/noescape.c =================================================================== --- /dev/null +++ test/Sema/noescape.c @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +void escapefunc(int *); +void noescapefunc(__attribute__((noescape)) int *); // expected-note {{previous declaration is here}} +void (*fnptr0)(int *); +void (*fnptr1)(__attribute__((noescape)) int *); +void noescapefunc(int *); // expected-error {{conflicting types for 'noescapefunc'}} + +void test0() { + fnptr0 = &escapefunc; + fnptr0 = &noescapefunc; + fnptr1 = &escapefunc; // expected-warning {{incompatible function pointer types assigning to 'void (*)(__attribute__((noescape)) int *)' from 'void (*)(int *)'}} + fnptr1 = &noescapefunc; +} Index: test/SemaObjCXX/noescape.mm =================================================================== --- /dev/null +++ test/SemaObjCXX/noescape.mm @@ -0,0 +1,76 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -fblocks -std=c++11 %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 {}; // expected-note {{template parameter is declared here}} + +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; // expected-error {{non-type template argument of type 'void (*)(int *)' cannot be converted to a value of type 'void (*)(__attribute__((noescape)) int *)'}} + S5<&noescapeFunc2> ne1; +}