Author: george.karpenkov Date: Fri Jan 11 10:02:08 2019 New Revision: 350942
URL: http://llvm.org/viewvc/llvm-project?rev=350942&view=rev Log: [attributes] Extend os_returns_(not_?)_retained attributes to parameters When applied to out-parameters, the attributes specify the expected lifetime of the written-into object. Additionally, introduce OSReturnsRetainedOn(Non)Zero attributes, which specify that an ownership transfer happens depending on a return code. Differential Revision: https://reviews.llvm.org/D56292 Modified: cfe/trunk/include/clang/Basic/Attr.td cfe/trunk/include/clang/Basic/AttrDocs.td cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td cfe/trunk/lib/Sema/SemaDeclAttr.cpp cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test cfe/trunk/test/Sema/attr-osobject.cpp cfe/trunk/test/Sema/attr-osobject.mm Modified: cfe/trunk/include/clang/Basic/Attr.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=350942&r1=350941&r2=350942&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/Attr.td (original) +++ cfe/trunk/include/clang/Basic/Attr.td Fri Jan 11 10:02:08 2019 @@ -841,13 +841,25 @@ def OSConsumed : InheritableParamAttr { def OSReturnsRetained : InheritableAttr { let Spellings = [Clang<"os_returns_retained">]; - let Subjects = SubjectList<[Function, ObjCMethod, ObjCProperty]>; + let Subjects = SubjectList<[Function, ObjCMethod, ObjCProperty, ParmVar]>; let Documentation = [RetainBehaviorDocs]; } def OSReturnsNotRetained : InheritableAttr { let Spellings = [Clang<"os_returns_not_retained">]; - let Subjects = SubjectList<[Function, ObjCMethod, ObjCProperty]>; + let Subjects = SubjectList<[Function, ObjCMethod, ObjCProperty, ParmVar]>; + let Documentation = [RetainBehaviorDocs]; +} + +def OSReturnsRetainedOnZero : InheritableAttr { + let Spellings = [Clang<"os_returns_retained_on_zero">]; + let Subjects = SubjectList<[ParmVar]>; + let Documentation = [RetainBehaviorDocs]; +} + +def OSReturnsRetainedOnNonZero : InheritableAttr { + let Spellings = [Clang<"os_returns_retained_on_non_zero">]; + let Subjects = SubjectList<[ParmVar]>; let Documentation = [RetainBehaviorDocs]; } Modified: cfe/trunk/include/clang/Basic/AttrDocs.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/AttrDocs.td?rev=350942&r1=350941&r2=350942&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/AttrDocs.td (original) +++ cfe/trunk/include/clang/Basic/AttrDocs.td Fri Jan 11 10:02:08 2019 @@ -888,6 +888,21 @@ Similar to ``__attribute__((ns_consumes_ ``__attribute__((os_consumes_this))`` specifies that the method call consumes the reference to "this" (e.g., when attaching it to a different object supplied as a parameter). +Out parameters (parameters the function is meant to write into, +either via pointers-to-pointers or references-to-pointers) +may be annotated with ``__attribute__((os_returns_retained))`` +or ``__attribute__((os_returns_not_retained))`` which specifies that the object +written into the out parameter should (or respectively should not) be released +after use. +Since often out parameters may or may not be written depending on the exit +code of the function, +annotations ``__attribute__((os_returns_retained_on_zero))`` +and ``__attribute__((os_returns_retained_on_non_zero))`` specify that +an out parameter at ``+1`` is written if and only if the function returns a zero +(respectively non-zero) error code. +Observe that return-code-dependent out parameter annotations are only +available for retained out parameters, as non-retained object do not have to be +released by the callee. These attributes are only used by the Clang Static Analyzer. The family of attributes ``X_returns_X_retained`` can be added to functions, Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=350942&r1=350941&r2=350942&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Jan 11 10:02:08 2019 @@ -3425,7 +3425,7 @@ def err_ns_attribute_wrong_parameter_typ "%select{Objective-C object|pointer|pointer-to-CF-pointer}1 parameters">; def warn_ns_attribute_wrong_parameter_type : Warning< "%0 attribute only applies to " - "%select{Objective-C object|pointer|pointer-to-CF-pointer}1 parameters">, + "%select{Objective-C object|pointer|pointer-to-CF-pointer|pointer/reference-to-OSObject-pointer}1 parameters">, InGroup<IgnoredAttributes>; def warn_objc_requires_super_protocol : Warning< "%0 attribute cannot be applied to %select{methods in protocols|dealloc}1">, Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=350942&r1=350941&r2=350942&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original) +++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Fri Jan 11 10:02:08 2019 @@ -425,18 +425,17 @@ appendDiagnostics(const Sema::SemaDiagno std::forward<DiagnosticArgs>(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}. +/// Add an attribute {@code AttrType} to declaration {@code D}, provided that +/// {@code PassesCheck} is true. +/// Otherwise, emit diagnostic {@code DiagID}, passing in all parameters +/// specified in {@code ExtraArgs}. template <typename AttrType, typename... DiagnosticArgs> static void -handleSimpleAttributeWithCheck(Sema &S, ValueDecl *D, SourceRange SR, +handleSimpleAttributeOrDiagnose(Sema &S, Decl *D, SourceRange SR, unsigned SpellingIndex, - llvm::function_ref<bool(QualType)> Check, - unsigned DiagID, DiagnosticArgs... ExtraArgs) { - if (!Check(D->getType())) { + bool PassesCheck, + unsigned DiagID, DiagnosticArgs&&... ExtraArgs) { + if (!PassesCheck) { Sema::SemaDiagnosticBuilder DB = S.Diag(D->getBeginLoc(), DiagID); appendDiagnostics(DB, std::forward<DiagnosticArgs>(ExtraArgs)...); return; @@ -444,6 +443,17 @@ handleSimpleAttributeWithCheck(Sema &S, handleSimpleAttribute<AttrType>(S, D, SR, SpellingIndex); } +template <typename AttrType, typename... DiagnosticArgs> +static void +handleSimpleAttributeOrDiagnose(Sema &S, Decl *D, const ParsedAttr &AL, + bool PassesCheck, + unsigned DiagID, + DiagnosticArgs&&... ExtraArgs) { + return handleSimpleAttributeOrDiagnose<AttrType>( + S, D, AL.getRange(), AL.getAttributeSpellingListIndex(), PassesCheck, + DiagID, std::forward<DiagnosticArgs>(ExtraArgs)...); +} + template <typename AttrType> static void handleSimpleAttributeWithExclusions(Sema &S, Decl *D, const ParsedAttr &AL) { @@ -4781,7 +4791,10 @@ static bool isValidSubjectOfCFAttribute( } static bool isValidSubjectOfOSAttribute(QualType QT) { - return QT->isDependentType() || QT->isPointerType(); + if (QT->isDependentType()) + return true; + QualType PT = QT->getPointeeType(); + return !PT.isNull() && PT->getAsCXXRecordDecl() != nullptr; } void Sema::AddXConsumedAttr(Decl *D, SourceRange SR, unsigned SpellingIndex, @@ -4790,14 +4803,14 @@ void Sema::AddXConsumedAttr(Decl *D, Sou ValueDecl *VD = cast<ValueDecl>(D); switch (K) { case RetainOwnershipKind::OS: - handleSimpleAttributeWithCheck<OSConsumedAttr>( - *this, VD, SR, SpellingIndex, &isValidSubjectOfOSAttribute, + handleSimpleAttributeOrDiagnose<OSConsumedAttr>( + *this, VD, SR, SpellingIndex, isValidSubjectOfOSAttribute(VD->getType()), diag::warn_ns_attribute_wrong_parameter_type, /*ExtraArgs=*/SR, "os_consumed", /*pointers*/ 1); return; case RetainOwnershipKind::NS: - handleSimpleAttributeWithCheck<NSConsumedAttr>( - *this, VD, SR, SpellingIndex, &isValidSubjectOfNSAttribute, + handleSimpleAttributeOrDiagnose<NSConsumedAttr>( + *this, VD, SR, SpellingIndex, isValidSubjectOfNSAttribute(VD->getType()), // These attributes are normally just advisory, but in ARC, ns_consumed // is significant. Allow non-dependent code to contain inappropriate @@ -4809,9 +4822,9 @@ void Sema::AddXConsumedAttr(Decl *D, Sou /*ExtraArgs=*/SR, "ns_consumed", /*objc pointers*/ 0); return; case RetainOwnershipKind::CF: - handleSimpleAttributeWithCheck<CFConsumedAttr>( + handleSimpleAttributeOrDiagnose<CFConsumedAttr>( *this, VD, SR, SpellingIndex, - &isValidSubjectOfCFAttribute, + isValidSubjectOfCFAttribute(VD->getType()), diag::warn_ns_attribute_wrong_parameter_type, /*ExtraArgs=*/SR, "cf_consumed", /*pointers*/1); return; @@ -4822,10 +4835,21 @@ static Sema::RetainOwnershipKind parsedAttrToRetainOwnershipKind(const ParsedAttr &AL) { switch (AL.getKind()) { case ParsedAttr::AT_CFConsumed: + case ParsedAttr::AT_CFReturnsRetained: + case ParsedAttr::AT_CFReturnsNotRetained: return Sema::RetainOwnershipKind::CF; + case ParsedAttr::AT_OSConsumesThis: case ParsedAttr::AT_OSConsumed: + case ParsedAttr::AT_OSReturnsRetained: + case ParsedAttr::AT_OSReturnsNotRetained: + case ParsedAttr::AT_OSReturnsRetainedOnZero: + case ParsedAttr::AT_OSReturnsRetainedOnNonZero: return Sema::RetainOwnershipKind::OS; + case ParsedAttr::AT_NSConsumesSelf: case ParsedAttr::AT_NSConsumed: + case ParsedAttr::AT_NSReturnsRetained: + case ParsedAttr::AT_NSReturnsNotRetained: + case ParsedAttr::AT_NSReturnsAutoreleased: return Sema::RetainOwnershipKind::NS; default: llvm_unreachable("Wrong argument supplied"); @@ -4841,9 +4865,20 @@ bool Sema::checkNSReturnsRetainedReturnT return true; } +/// \return whether the parameter is a pointer to OSObject pointer. +static bool isValidOSObjectOutParameter(const Decl *D) { + const auto *PVD = dyn_cast<ParmVarDecl>(D); + if (!PVD) + return false; + QualType QT = PVD->getType(); + QualType PT = QT->getPointeeType(); + return !PT.isNull() && isValidSubjectOfOSAttribute(PT); +} + static void handleXReturnsXRetainedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { QualType ReturnType; + Sema::RetainOwnershipKind K = parsedAttrToRetainOwnershipKind(AL); if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { ReturnType = MD->getReturnType(); @@ -4857,10 +4892,13 @@ static void handleXReturnsXRetainedAttr( } else if (const auto *Param = dyn_cast<ParmVarDecl>(D)) { // Attributes on parameters are used for out-parameters, // passed as pointers-to-pointers. + unsigned DiagID = K == Sema::RetainOwnershipKind::CF + ? /*pointer-to-CF-pointer*/2 + : /*pointer-to-OSObject-pointer*/3; ReturnType = Param->getType()->getPointeeType(); if (ReturnType.isNull()) { S.Diag(D->getBeginLoc(), diag::warn_ns_attribute_wrong_parameter_type) - << AL << /*pointer-to-CF-pointer*/ 2 << AL.getRange(); + << AL << DiagID << AL.getRange(); return; } } else if (AL.isUsedAsTypeAttr()) { @@ -4872,11 +4910,11 @@ static void handleXReturnsXRetainedAttr( case ParsedAttr::AT_NSReturnsRetained: case ParsedAttr::AT_NSReturnsAutoreleased: case ParsedAttr::AT_NSReturnsNotRetained: - case ParsedAttr::AT_OSReturnsRetained: - case ParsedAttr::AT_OSReturnsNotRetained: ExpectedDeclKind = ExpectedFunctionOrMethod; break; + case ParsedAttr::AT_OSReturnsRetained: + case ParsedAttr::AT_OSReturnsNotRetained: case ParsedAttr::AT_CFReturnsRetained: case ParsedAttr::AT_CFReturnsNotRetained: ExpectedDeclKind = ExpectedFunctionMethodOrParameter; @@ -4889,6 +4927,7 @@ static void handleXReturnsXRetainedAttr( bool TypeOK; bool Cf; + unsigned ParmDiagID = 2; // Pointer-to-CF-pointer switch (AL.getKind()) { default: llvm_unreachable("invalid ownership attribute"); case ParsedAttr::AT_NSReturnsRetained: @@ -4912,6 +4951,7 @@ static void handleXReturnsXRetainedAttr( case ParsedAttr::AT_OSReturnsNotRetained: TypeOK = isValidSubjectOfOSAttribute(ReturnType); Cf = true; + ParmDiagID = 3; // Pointer-to-OSObject-pointer break; } @@ -4921,7 +4961,7 @@ static void handleXReturnsXRetainedAttr( if (isa<ParmVarDecl>(D)) { S.Diag(D->getBeginLoc(), diag::warn_ns_attribute_wrong_parameter_type) - << AL << /*pointer-to-CF*/ 2 << AL.getRange(); + << AL << ParmDiagID << AL.getRange(); } else { // Needs to be kept in sync with warn_ns_attribute_wrong_return_type. enum : unsigned { @@ -6513,6 +6553,18 @@ static void ProcessDeclAttribute(Sema &S case ParsedAttr::AT_OSConsumesThis: handleSimpleAttribute<OSConsumesThisAttr>(S, D, AL); break; + case ParsedAttr::AT_OSReturnsRetainedOnZero: + handleSimpleAttributeOrDiagnose<OSReturnsRetainedOnZeroAttr>( + S, D, AL, isValidOSObjectOutParameter(D), + diag::warn_ns_attribute_wrong_parameter_type, + /*Extra Args=*/AL, /*pointer-to-OSObject-pointer*/ 3, AL.getRange()); + break; + case ParsedAttr::AT_OSReturnsRetainedOnNonZero: + handleSimpleAttributeOrDiagnose<OSReturnsRetainedOnNonZeroAttr>( + S, D, AL, isValidOSObjectOutParameter(D), + diag::warn_ns_attribute_wrong_parameter_type, + /*Extra Args=*/AL, /*pointer-to-OSObject-poointer*/ 3, AL.getRange()); + break; case ParsedAttr::AT_NSReturnsAutoreleased: case ParsedAttr::AT_NSReturnsNotRetained: case ParsedAttr::AT_NSReturnsRetained: Modified: cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test?rev=350942&r1=350941&r2=350942&view=diff ============================================================================== --- cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test (original) +++ cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test Fri Jan 11 10:02:08 2019 @@ -86,8 +86,10 @@ // 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, SubjectMatchRule_objc_property) -// CHECK-NEXT: OSReturnsRetained (SubjectMatchRule_function, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property) +// CHECK-NEXT: OSReturnsNotRetained (SubjectMatchRule_function, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_variable_is_parameter) +// CHECK-NEXT: OSReturnsRetained (SubjectMatchRule_function, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_variable_is_parameter) +// CHECK-NEXT: OSReturnsRetainedOnNonZero (SubjectMatchRule_variable_is_parameter) +// CHECK-NEXT: OSReturnsRetainedOnZero (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: ObjCBoxable (SubjectMatchRule_record) // CHECK-NEXT: ObjCBridge (SubjectMatchRule_record, SubjectMatchRule_type_alias) // CHECK-NEXT: ObjCBridgeMutable (SubjectMatchRule_record) Modified: cfe/trunk/test/Sema/attr-osobject.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/attr-osobject.cpp?rev=350942&r1=350941&r2=350942&view=diff ============================================================================== --- cfe/trunk/test/Sema/attr-osobject.cpp (original) +++ cfe/trunk/test/Sema/attr-osobject.cpp Fri Jan 11 10:02:08 2019 @@ -37,12 +37,38 @@ __attribute__((os_returns_retained(10))) return nullptr; } -struct __attribute__((os_returns_retained)) NoRetainAttrOnStruct {}; // expected-warning{{'os_returns_retained' attribute only applies to functions, Objective-C methods, and Objective-C properties}} +struct __attribute__((os_returns_retained)) NoRetainAttrOnStruct {}; // expected-warning{{'os_returns_retained' attribute only applies to functions, Objective-C methods, Objective-C properties, and parameters}} __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, Objective-C methods, and Objective-C properties}} +struct __attribute__((os_returns_not_retained)) NoNotRetainedAttrOnStruct {}; // expected-warning{{'os_returns_not_retained' attribute only applies to functions, Objective-C methods, Objective-C properties, and parameters}} __attribute__((os_consumes_this)) void no_consumes_this_on_function() {} // expected-warning{{'os_consumes_this' attribute only applies to non-static member functions}} + +void write_into_out_parameter(__attribute__((os_returns_retained)) S** out) {} + +bool write_into_out_parameter_on_nonzero(__attribute__((os_returns_retained_on_non_zero)) S** out) {} + +bool write_into_out_parameter_on_nonzero_invalid(__attribute__((os_returns_retained_on_non_zero)) S* out) {} // expected-warning{{'os_returns_retained_on_non_zero' attribute only applies to pointer/reference-to-OSObject-pointer parameters}} + +bool write_into_out_parameter_on_zero(__attribute__((os_returns_retained_on_zero)) S** out) {} + +bool write_into_out_parameter_on_zero_invalid(__attribute__((os_returns_retained_on_zero)) S* out) {} // expected-warning{{'os_returns_retained_on_zero' attribute only applies to pointer/reference-to-OSObject-pointer parameters}} + +void write_into_out_parameter_ref(__attribute__((os_returns_retained)) S*& out) {} + +typedef S* SPtr; + +void write_into_out_parameter_typedef(__attribute__((os_returns_retained)) SPtr* out) {} + +void write_into_out_parameter_invalid(__attribute__((os_returns_retained)) S* out) {} // expected-warning{{'os_returns_retained' attribute only applies to pointer/reference-to-OSObject-pointer parameters}} + +void write_into_out_parameter_not_retained(__attribute__((os_returns_not_retained)) S **out) {} + +void write_into_out_parameter_not_retained(__attribute__((os_returns_not_retained)) S volatile * const * const out) {} + +void write_into_not_retainedout_parameter_invalid(__attribute__((os_returns_not_retained)) S* out) {} // expected-warning{{'os_returns_not_retained' attribute only applies to pointer/reference-to-OSObject-pointer parameters}} + +void write_into_not_retainedout_parameter_too_many_pointers(__attribute__((os_returns_not_retained)) S*** out) {} // expected-warning{{'os_returns_not_retained' attribute only applies to pointer/reference-to-OSObject-pointer parameters}} Modified: cfe/trunk/test/Sema/attr-osobject.mm URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/attr-osobject.mm?rev=350942&r1=350941&r2=350942&view=diff ============================================================================== --- cfe/trunk/test/Sema/attr-osobject.mm (original) +++ cfe/trunk/test/Sema/attr-osobject.mm Fri Jan 11 10:02:08 2019 @@ -8,8 +8,8 @@ struct S {}; - (void) takeS:(S*) __attribute__((os_consumed)) s; @end -typedef __attribute__((os_returns_retained)) id (^blockType)(); // expected-warning{{'os_returns_retained' attribute only applies to functions, Objective-C methods, and Objective-C properties}} +typedef __attribute__((os_returns_retained)) id (^blockType)(); // expected-warning{{'os_returns_retained' attribute only applies to functions, Objective-C methods, Objective-C properties, and parameters}} -__auto_type b = ^ id (id filter) __attribute__((os_returns_retained)) { // expected-warning{{'os_returns_retained' attribute only applies to functions, Objective-C methods, and Objective-C properties}} +__auto_type b = ^ id (id filter) __attribute__((os_returns_retained)) { // expected-warning{{'os_returns_retained' attribute only applies to functions, Objective-C methods, Objective-C properties, and parameters}} return filter; }; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits