Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -812,19 +812,38 @@ def CFReturnsRetained : InheritableAttr { let Spellings = [Clang<"cf_returns_retained">]; // let Subjects = SubjectList<[ObjCMethod, ObjCProperty, Function]>; - let Documentation = [Undocumented]; + let Documentation = [RetainBehaviorDocs]; } def CFReturnsNotRetained : InheritableAttr { let Spellings = [Clang<"cf_returns_not_retained">]; // let Subjects = SubjectList<[ObjCMethod, ObjCProperty, Function]>; - let Documentation = [Undocumented]; + let Documentation = [RetainBehaviorDocs]; } def CFConsumed : InheritableParamAttr { let Spellings = [Clang<"cf_consumed">]; let Subjects = SubjectList<[ParmVar]>; - let Documentation = [Undocumented]; + let Documentation = [RetainBehaviorDocs]; +} + +// OSObject-based attributes. +def OSConsumed : InheritableParamAttr { + let Spellings = [Clang<"os_consumed">]; + let Subjects = SubjectList<[ParmVar]>; + let Documentation = [RetainBehaviorDocs]; +} + +def OSReturnsRetained : InheritableAttr { + let Spellings = [Clang<"os_returns_retained">]; + let Subjects = SubjectList<[Function, ObjCMethod]>; + let Documentation = [RetainBehaviorDocs]; +} + +def OSReturnsNotRetained : InheritableAttr { + let Spellings = [Clang<"os_returns_not_retained">]; + let Subjects = SubjectList<[Function, ObjCMethod]>; + let Documentation = [RetainBehaviorDocs]; } def Cleanup : InheritableAttr { @@ -1612,19 +1631,19 @@ def NSReturnsRetained : DeclOrTypeAttr { let Spellings = [Clang<"ns_returns_retained">]; // let Subjects = SubjectList<[ObjCMethod, ObjCProperty, Function]>; - let Documentation = [Undocumented]; + let Documentation = [RetainBehaviorDocs]; } def NSReturnsNotRetained : InheritableAttr { let Spellings = [Clang<"ns_returns_not_retained">]; // let Subjects = SubjectList<[ObjCMethod, ObjCProperty, Function]>; - let Documentation = [Undocumented]; + let Documentation = [RetainBehaviorDocs]; } def NSReturnsAutoreleased : InheritableAttr { let Spellings = [Clang<"ns_returns_autoreleased">]; // let Subjects = SubjectList<[ObjCMethod, ObjCProperty, Function]>; - let Documentation = [Undocumented]; + let Documentation = [RetainBehaviorDocs]; } def NSConsumesSelf : InheritableAttr { Index: clang/include/clang/Basic/AttrDocs.td =================================================================== --- clang/include/clang/Basic/AttrDocs.td +++ clang/include/clang/Basic/AttrDocs.td @@ -847,6 +847,46 @@ }]; } +def RetainBehaviorDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The behavior of a function with respect to reference counting for Foundation +(Objective-C), CoreFoundation (C) and OSObject (C++) is determined by a naming +convention (e.g. functions starting with "get" are assumed to return at +``+0``). + +It can be overriden using a family of the following attributes. In +Objective-C, the annotation ``__attribute__((ns_returns_retained))`` applied to +a function communicates that the object is returned at ``+1``, and the caller +is responsible for freeing it. +Similiarly, the annotation ``__attribute__((ns_returns_not_retained))`` +specifies that the object is returned at ``+0`` and the ownership remains with +the callee. +Additionally, parameters can have an annotation +``__attribute__((ns_consumed))``, which specifies that passing an owned object +as that parameter effectively transfers the ownership, and the caller is no +longer responsible for it. +These attributes affect code generation when interacting with ARC code, and +they are used by the Clang Static Analyzer. + +In C programs using CoreFoundation, a similar set of attributes: +``__attribute__((cf_returns_not_retained))``, +``__attribute__((cf_returns_retained))`` and ``__attribute__((cf_consumed))`` +have the same respective semantics when applied to CoreFoundation objects. +They are used by the Clang Static Analyzer to check the correct reference +counting throughout the program. + +Finally, in C++ interacting with XNU kernel (objects inheriting from OSObject), +the same attribute family is present: +``__attribute__((os_returns_not_retained))``, +``__attribute__((os_returns_retained))`` and ``__attribute__((os_consumed))``, +with the same respective semantics. +These attributes are also used by the Clang Static Analyzer. + }]; +} + + + def NoDebugDocs : Documentation { let Category = DocCatVariable; let Content = [{ Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -8544,9 +8544,9 @@ void AddParameterABIAttr(SourceRange AttrRange, Decl *D, ParameterABI ABI, unsigned SpellingListIndex); - void AddNSConsumedAttr(SourceRange AttrRange, Decl *D, - unsigned SpellingListIndex, bool isNSConsumed, - bool isTemplateInstantiation); + enum class RetainOwnershipKind {NS, CF, OS}; + void AddXConsumedAttr(Decl *D, SourceRange SR, unsigned SpellingIndex, + RetainOwnershipKind K, bool IsTemplateInstantiation); bool checkNSReturnsRetainedReturnType(SourceLocation loc, QualType type); Index: clang/lib/Sema/SemaDeclAttr.cpp =================================================================== --- clang/lib/Sema/SemaDeclAttr.cpp +++ clang/lib/Sema/SemaDeclAttr.cpp @@ -391,10 +391,50 @@ /// Applies the given attribute to the Decl without performing any /// additional semantic checking. +template +static void handleSimpleAttribute(Sema &S, Decl *D, SourceRange SR, + unsigned SpellingIndex) { + D->addAttr(::new (S.Context) AttrType(SR, S.Context, SpellingIndex)); +} + template static void handleSimpleAttribute(Sema &S, Decl *D, const ParsedAttr &AL) { - D->addAttr(::new (S.Context) AttrType(AL.getRange(), S.Context, - AL.getAttributeSpellingListIndex())); + handleSimpleAttribute(S, D, AL.getRange(), + AL.getAttributeSpellingListIndex()); +} + + +template +static const Sema::SemaDiagnosticBuilder& +appendDiagnostics(const Sema::SemaDiagnosticBuilder &Bldr) { + return Bldr; +} + +template +static const Sema::SemaDiagnosticBuilder& +appendDiagnostics(const Sema::SemaDiagnosticBuilder &Bldr, T &&ExtraArg, + DiagnosticArgs &&... ExtraArgs) { + return appendDiagnostics(Bldr << std::forward(ExtraArg), + std::forward(ExtraArgs)...); +} + +/// Add an attribute {@code AttrType} to declaration {@code D}, +/// provided the given {@code Check} function returns {@code true} +/// on type of {@code D}. +/// If check does not pass, emit diagnostic {@code DiagID}, +/// passing in all parameters specified in {@code ExtraArgs}. +template +static void +handleSimpleAttributeWithCheck(Sema &S, ValueDecl *D, SourceRange SR, + unsigned SpellingIndex, + llvm::function_ref Check, + unsigned DiagID, DiagnosticArgs... ExtraArgs) { + if (!Check(D->getType())) { + Sema::SemaDiagnosticBuilder DB = S.Diag(D->getBeginLoc(), DiagID); + appendDiagnostics(DB, std::forward(ExtraArgs)...); + return; + } + handleSimpleAttribute(S, D, SR, SpellingIndex); } template @@ -4711,58 +4751,70 @@ //===----------------------------------------------------------------------===// // Checker-specific attribute handlers. //===----------------------------------------------------------------------===// - static bool isValidSubjectOfNSReturnsRetainedAttribute(QualType QT) { return QT->isDependentType() || QT->isObjCRetainableType(); } -static bool isValidSubjectOfNSAttribute(Sema &S, QualType QT) { +static bool isValidSubjectOfNSAttribute(QualType QT) { return QT->isDependentType() || QT->isObjCObjectPointerType() || - S.Context.isObjCNSObjectType(QT); + QT->isObjCNSObjectType(); } -static bool isValidSubjectOfCFAttribute(Sema &S, QualType QT) { +static bool isValidSubjectOfCFAttribute(QualType QT) { return QT->isDependentType() || QT->isPointerType() || - isValidSubjectOfNSAttribute(S, QT); + isValidSubjectOfNSAttribute(QT); } -static void handleNSConsumedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { - S.AddNSConsumedAttr(AL.getRange(), D, AL.getAttributeSpellingListIndex(), - AL.getKind() == ParsedAttr::AT_NSConsumed, - /*template instantiation*/ false); +static bool isValidSubjectOfOSAttribute(QualType QT) { + return QT->isDependentType() || QT->isPointerType(); } -void Sema::AddNSConsumedAttr(SourceRange AttrRange, Decl *D, - unsigned SpellingIndex, bool IsNSConsumed, - bool IsTemplateInstantiation) { - const auto *Param = cast(D); - bool TypeOK; - - if (IsNSConsumed) - TypeOK = isValidSubjectOfNSAttribute(*this, Param->getType()); - else - TypeOK = isValidSubjectOfCFAttribute(*this, Param->getType()); +void Sema::AddXConsumedAttr(Decl *D, SourceRange SR, unsigned SpellingIndex, + RetainOwnershipKind K, + bool IsTemplateInstantiation) { + ValueDecl *VD = cast(D); + switch (K) { + case RetainOwnershipKind::OS: + handleSimpleAttributeWithCheck( + *this, VD, SR, SpellingIndex, &isValidSubjectOfOSAttribute, + diag::warn_ns_attribute_wrong_parameter_type, + /*ExtraArgs=*/SR, "os_consumed", /*pointers*/ 1); + return; + case RetainOwnershipKind::NS: + handleSimpleAttributeWithCheck( + *this, VD, SR, SpellingIndex, &isValidSubjectOfNSAttribute, - if (!TypeOK) { - // These attributes are normally just advisory, but in ARC, ns_consumed - // is significant. Allow non-dependent code to contain inappropriate - // attributes even in ARC, but require template instantiations to be - // set up correctly. - Diag(D->getBeginLoc(), (IsTemplateInstantiation && IsNSConsumed && - getLangOpts().ObjCAutoRefCount - ? diag::err_ns_attribute_wrong_parameter_type - : diag::warn_ns_attribute_wrong_parameter_type)) - << AttrRange << (IsNSConsumed ? "ns_consumed" : "cf_consumed") - << (IsNSConsumed ? /*objc pointers*/ 0 : /*cf pointers*/ 1); + // These attributes are normally just advisory, but in ARC, ns_consumed + // is significant. Allow non-dependent code to contain inappropriate + // attributes even in ARC, but require template instantiations to be + // set up correctly. + ((IsTemplateInstantiation && getLangOpts().ObjCAutoRefCount) + ? diag::err_ns_attribute_wrong_parameter_type + : diag::warn_ns_attribute_wrong_parameter_type), + /*ExtraArgs=*/SR, "ns_consumed", /*objc pointers*/ 0); + return; + case RetainOwnershipKind::CF: + handleSimpleAttributeWithCheck( + *this, VD, SR, SpellingIndex, + &isValidSubjectOfCFAttribute, + diag::warn_ns_attribute_wrong_parameter_type, + /*ExtraArgs=*/SR, "cf_consumed", /*pointers*/1); return; } +} - if (IsNSConsumed) - D->addAttr(::new (Context) - NSConsumedAttr(AttrRange, Context, SpellingIndex)); - else - D->addAttr(::new (Context) - CFConsumedAttr(AttrRange, Context, SpellingIndex)); +static Sema::RetainOwnershipKind +parsedAttrToRetainOwnershipKind(const ParsedAttr &AL) { + switch (AL.getKind()) { + case ParsedAttr::AT_CFConsumed: + return Sema::RetainOwnershipKind::CF; + case ParsedAttr::AT_OSConsumed: + return Sema::RetainOwnershipKind::OS; + case ParsedAttr::AT_NSConsumed: + return Sema::RetainOwnershipKind::NS; + default: + llvm_unreachable("Wrong argument supplied"); + } } bool Sema::checkNSReturnsRetainedReturnType(SourceLocation Loc, QualType QT) { @@ -4774,24 +4826,26 @@ return true; } -static void handleNSReturnsRetainedAttr(Sema &S, Decl *D, +static void handleXReturnsXRetainedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { QualType ReturnType; - if (const auto *MD = dyn_cast(D)) + if (const auto *MD = dyn_cast(D)) { ReturnType = MD->getReturnType(); - else if (S.getLangOpts().ObjCAutoRefCount && hasDeclarator(D) && - (AL.getKind() == ParsedAttr::AT_NSReturnsRetained)) + } else if (S.getLangOpts().ObjCAutoRefCount && hasDeclarator(D) && + (AL.getKind() == ParsedAttr::AT_NSReturnsRetained)) { return; // ignore: was handled as a type attribute - else if (const auto *PD = dyn_cast(D)) + } else if (const auto *PD = dyn_cast(D)) { ReturnType = PD->getType(); - else if (const auto *FD = dyn_cast(D)) + } else if (const auto *FD = dyn_cast(D)) { ReturnType = FD->getReturnType(); - else if (const auto *Param = dyn_cast(D)) { + } else if (const auto *Param = dyn_cast(D)) { + // Attributes on parameters are used for out-parameters, + // passed as pointers-to-pointers. ReturnType = Param->getType()->getPointeeType(); if (ReturnType.isNull()) { S.Diag(D->getBeginLoc(), diag::warn_ns_attribute_wrong_parameter_type) - << AL << /*pointer-to-CF*/ 2 << AL.getRange(); + << AL << /*pointer-to-CF-pointer*/ 2 << AL.getRange(); return; } } else if (AL.isUsedAsTypeAttr()) { @@ -4803,6 +4857,8 @@ case ParsedAttr::AT_NSReturnsRetained: case ParsedAttr::AT_NSReturnsAutoreleased: case ParsedAttr::AT_NSReturnsNotRetained: + case ParsedAttr::AT_OSReturnsRetained: + case ParsedAttr::AT_OSReturnsNotRetained: ExpectedDeclKind = ExpectedFunctionOrMethod; break; @@ -4827,13 +4883,19 @@ case ParsedAttr::AT_NSReturnsAutoreleased: case ParsedAttr::AT_NSReturnsNotRetained: - TypeOK = isValidSubjectOfNSAttribute(S, ReturnType); + TypeOK = isValidSubjectOfNSAttribute(ReturnType); Cf = false; break; case ParsedAttr::AT_CFReturnsRetained: case ParsedAttr::AT_CFReturnsNotRetained: - TypeOK = isValidSubjectOfCFAttribute(S, ReturnType); + TypeOK = isValidSubjectOfCFAttribute(ReturnType); + Cf = true; + break; + + case ParsedAttr::AT_OSReturnsRetained: + case ParsedAttr::AT_OSReturnsNotRetained: + TypeOK = isValidSubjectOfOSAttribute(ReturnType); Cf = true; break; } @@ -4866,24 +4928,25 @@ default: llvm_unreachable("invalid ownership attribute"); case ParsedAttr::AT_NSReturnsAutoreleased: - D->addAttr(::new (S.Context) NSReturnsAutoreleasedAttr( - AL.getRange(), S.Context, AL.getAttributeSpellingListIndex())); + handleSimpleAttribute(S, D, AL); return; case ParsedAttr::AT_CFReturnsNotRetained: - D->addAttr(::new (S.Context) CFReturnsNotRetainedAttr( - AL.getRange(), S.Context, AL.getAttributeSpellingListIndex())); + handleSimpleAttribute(S, D, AL); return; case ParsedAttr::AT_NSReturnsNotRetained: - D->addAttr(::new (S.Context) NSReturnsNotRetainedAttr( - AL.getRange(), S.Context, AL.getAttributeSpellingListIndex())); + handleSimpleAttribute(S, D, AL); return; case ParsedAttr::AT_CFReturnsRetained: - D->addAttr(::new (S.Context) CFReturnsRetainedAttr( - AL.getRange(), S.Context, AL.getAttributeSpellingListIndex())); + handleSimpleAttribute(S, D, AL); return; case ParsedAttr::AT_NSReturnsRetained: - D->addAttr(::new (S.Context) NSReturnsRetainedAttr( - AL.getRange(), S.Context, AL.getAttributeSpellingListIndex())); + handleSimpleAttribute(S, D, AL); + return; + case ParsedAttr::AT_OSReturnsRetained: + handleSimpleAttribute(S, D, AL); + return; + case ParsedAttr::AT_OSReturnsNotRetained: + handleSimpleAttribute(S, D, AL); return; }; } @@ -6337,17 +6400,22 @@ break; case ParsedAttr::AT_CFConsumed: case ParsedAttr::AT_NSConsumed: - handleNSConsumedAttr(S, D, AL); + case ParsedAttr::AT_OSConsumed: + S.AddXConsumedAttr(D, AL.getRange(), AL.getAttributeSpellingListIndex(), + parsedAttrToRetainOwnershipKind(AL), + /*IsTemplateInstantiation=*/false); break; case ParsedAttr::AT_NSConsumesSelf: handleSimpleAttribute(S, D, AL); break; case ParsedAttr::AT_NSReturnsAutoreleased: case ParsedAttr::AT_NSReturnsNotRetained: - case ParsedAttr::AT_CFReturnsNotRetained: case ParsedAttr::AT_NSReturnsRetained: + case ParsedAttr::AT_CFReturnsNotRetained: case ParsedAttr::AT_CFReturnsRetained: - handleNSReturnsRetainedAttr(S, D, AL); + case ParsedAttr::AT_OSReturnsNotRetained: + case ParsedAttr::AT_OSReturnsRetained: + handleXReturnsXRetainedAttr(S, D, AL); break; case ParsedAttr::AT_WorkGroupSizeHint: handleWorkGroupSize(S, D, AL); Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -365,6 +365,20 @@ } } +static Sema::RetainOwnershipKind +attrToRetainOwnershipKind(const Attr *A) { + switch (A->getKind()) { + case clang::attr::CFConsumed: + return Sema::RetainOwnershipKind::CF; + case clang::attr::OSConsumed: + return Sema::RetainOwnershipKind::OS; + case clang::attr::NSConsumed: + return Sema::RetainOwnershipKind::NS; + default: + llvm_unreachable("Wrong argument supplied"); + } +} + void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, const Decl *Tmpl, Decl *New, LateInstantiatedAttrVec *LateAttrs, @@ -438,11 +452,12 @@ continue; } - if (isa(TmplAttr) || isa(TmplAttr)) { - AddNSConsumedAttr(TmplAttr->getRange(), New, - TmplAttr->getSpellingListIndex(), - isa(TmplAttr), - /*template instantiation*/ true); + if (isa(TmplAttr) || isa(TmplAttr) || + isa(TmplAttr)) { + AddXConsumedAttr(New, TmplAttr->getRange(), + TmplAttr->getSpellingListIndex(), + attrToRetainOwnershipKind(TmplAttr), + /*template instantiation=*/true); continue; } Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test =================================================================== --- clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ clang/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 129 attributes: +// CHECK: #pragma clang attribute supports 132 attributes: // CHECK-NEXT: AMDGPUFlatWorkGroupSize (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumSGPR (SubjectMatchRule_function) // CHECK-NEXT: AMDGPUNumVGPR (SubjectMatchRule_function) @@ -85,6 +85,9 @@ // CHECK-NEXT: NoThreadSafetyAnalysis (SubjectMatchRule_function) // CHECK-NEXT: NoThrow (SubjectMatchRule_function) // CHECK-NEXT: NotTailCalled (SubjectMatchRule_function) +// CHECK-NEXT: OSConsumed (SubjectMatchRule_variable_is_parameter) +// CHECK-NEXT: OSReturnsNotRetained (SubjectMatchRule_function, SubjectMatchRule_objc_method) +// CHECK-NEXT: OSReturnsRetained (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: ObjCBoxable (SubjectMatchRule_record) // CHECK-NEXT: ObjCBridge (SubjectMatchRule_record, SubjectMatchRule_type_alias) // CHECK-NEXT: ObjCBridgeMutable (SubjectMatchRule_record) Index: clang/test/Sema/attr-osobject.cpp =================================================================== --- /dev/null +++ clang/test/Sema/attr-osobject.cpp @@ -0,0 +1,42 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +struct S { + __attribute__((os_returns_retained)) S* method_returns_retained() { + return nullptr; + } +}; +__attribute__((os_returns_retained)) S *ret_retained() { + return nullptr; +} + +__attribute__((os_returns_retained)) S ret_retained_value() { // expected-warning{{'os_returns_retained' attribute only applies to functions that return a pointer}} + return {}; +} + +__attribute__((os_returns_not_retained)) S *ret_not_retained() { + return nullptr; +} + +__attribute__((os_returns_not_retained)) S ret_not_retained_value() { // expected-warning{{'os_returns_not_retained' attribute only applies to functions that return a pointer}} + return {}; +} + +void accept_consumed_arg(__attribute__((os_consumed)) S *arg) {} + +void accept_consumed_arg_by_value(__attribute__((os_consumed)) S arg) {} // expected-warning{{os_consumed attribute only applies to pointer parameters}} + +void accept_consumed_arg_no_extra_arg(__attribute__((os_consumed(10))) S *arg) {} // expected-error{{'os_consumed' attribute takes no arguments}} + +struct __attribute__((os_consumed)) NoAttrOnStruct {}; // expected-warning{{'os_consumed' attribute only applies to parameters}} + +__attribute__((os_returns_retained(10))) S* returns_retained_no_extra_arg() { // expected-error{{'os_returns_retained' attribute takes no arguments}} + return nullptr; +} + +struct __attribute__((os_returns_retained)) NoRetainAttrOnStruct {}; // expected-warning{{'os_returns_retained' attribute only applies to functions and Objective-C methods}} + +__attribute__((os_returns_not_retained(10))) S* os_returns_no_retained_no_extra_args( S *arg) { // expected-error{{'os_returns_not_retained' attribute takes no arguments}} + return nullptr; +} + +struct __attribute__((os_returns_not_retained)) NoNotRetainedAttrOnStruct {}; // expected-warning{{'os_returns_not_retained' attribute only applies to functions and Objective-C methods}} Index: clang/test/Sema/attr-osobject.mm =================================================================== --- /dev/null +++ clang/test/Sema/attr-osobject.mm @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +struct S {}; + +@interface I + @property (readonly) S* prop __attribute__((os_returns_retained)); // expected-warning{{'os_returns_retained' attribute only applies to functions and Objective-C methods}} + - (S*) generateS __attribute__((os_returns_retained)); + - (void) takeS:(S*) __attribute__((os_consumed)) s; +@end