From 00fc11fc8b3f0bb5c8f1de337df149b0733d986e Mon Sep 17 00:00:00 2001 From: Douglas Gregor <dgregor@apple.com> Date: Fri, 19 Jun 2015 23:17:51 +0000 Subject: [PATCH] CF_RETURNS_[NOT_]RETAINED on a param makes the inner pointer __nullable. That is, void cf2(CFTypeRef * __nullable p CF_RETURNS_NOT_RETAINED); is equivalent to void cf2(CFTypeRef __nullable * __nullable p CF_RETURNS_NOT_RETAINED); More rdar://problem/18742441 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@240186 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaType.cpp | 42 +++++++++++++++++-- test/Analysis/retain-release.m | 7 ++++ .../Inputs/nullability-consistency-8.h | 16 +++++++ 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index d3b773e5350..d950d329b4e 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -2614,6 +2614,8 @@ namespace { SingleLevelPointer, // Multi-level pointer (of any pointer kind). MultiLevelPointer, + // CFFooRef* + MaybePointerToCFRef, // CFErrorRef* CFErrorRefPointer, // NSError** @@ -2754,6 +2756,9 @@ static PointerDeclaratorKind classifyPointerDeclarator(Sema &S, case 1: return PointerDeclaratorKind::SingleLevelPointer; + case 2: + return PointerDeclaratorKind::MaybePointerToCFRef; + default: return PointerDeclaratorKind::MultiLevelPointer; } @@ -2894,6 +2899,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, // Determine whether we should infer __nonnull on pointer types. Optional<NullabilityKind> inferNullability; bool inferNullabilityCS = false; + bool inferNullabilityInnerOnly = false; + bool inferNullabilityInnerOnlyComplete = false; // Are we in an assume-nonnull region? bool inAssumeNonNullRegion = false; @@ -3007,6 +3014,31 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, if (isFunctionOrMethod && inAssumeNonNullRegion) inferNullability = NullabilityKind::Nullable; break; + + case PointerDeclaratorKind::MaybePointerToCFRef: + if (isFunctionOrMethod) { + // On pointer-to-pointer parameters marked cf_returns_retained or + // cf_returns_not_retained, if the outer pointer is explicit then + // infer the inner pointer as __nullable. + auto hasCFReturnsAttr = [](const AttributeList *NextAttr) -> bool { + while (NextAttr) { + if (NextAttr->getKind() == AttributeList::AT_CFReturnsRetained || + NextAttr->getKind() == AttributeList::AT_CFReturnsNotRetained) + return true; + NextAttr = NextAttr->getNext(); + } + return false; + }; + if (const auto *InnermostChunk = D.getInnermostNonParenChunk()) { + if (hasCFReturnsAttr(D.getAttributes()) || + hasCFReturnsAttr(InnermostChunk->getAttrs()) || + hasCFReturnsAttr(D.getDeclSpec().getAttributes().getList())) { + inferNullability = NullabilityKind::Nullable; + inferNullabilityInnerOnly = true; + } + } + } + break; } break; @@ -3047,9 +3079,10 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, return nullptr; // If we're supposed to infer nullability, do so now. - if (inferNullability) { - auto syntax = inferNullabilityCS ? AttributeList::AS_ContextSensitiveKeyword - : AttributeList::AS_Keyword; + if (inferNullability && !inferNullabilityInnerOnlyComplete) { + AttributeList::Syntax syntax + = inferNullabilityCS ? AttributeList::AS_ContextSensitiveKeyword + : AttributeList::AS_Keyword; AttributeList *nullabilityAttr = state.getDeclarator().getAttributePool() .create( S.getNullabilityKeyword( @@ -3059,6 +3092,9 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, nullptr, 0, syntax); spliceAttrIntoList(*nullabilityAttr, attrs); + + if (inferNullabilityInnerOnly) + inferNullabilityInnerOnlyComplete = true; return nullabilityAttr; } diff --git a/test/Analysis/retain-release.m b/test/Analysis/retain-release.m index e3ad4090373..1dbcda507c1 100644 --- a/test/Analysis/retain-release.m +++ b/test/Analysis/retain-release.m @@ -2164,6 +2164,13 @@ void testCFReturnsNotRetained() { CFRelease(obj); // // expected-warning {{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}} } +void testCFReturnsNotRetainedAnnotated() { + extern void getViaParam2(CFTypeRef * __nonnull CF_RETURNS_NOT_RETAINED outObj); + CFTypeRef obj; + getViaParam2(&obj); + CFRelease(obj); // // expected-warning {{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}} +} + void testCFReturnsRetained() { extern int copyViaParam(CFTypeRef * CF_RETURNS_RETAINED outObj); CFTypeRef obj; diff --git a/test/SemaObjCXX/Inputs/nullability-consistency-8.h b/test/SemaObjCXX/Inputs/nullability-consistency-8.h index e7cf4b3ee8a..890bb4db546 100644 --- a/test/SemaObjCXX/Inputs/nullability-consistency-8.h +++ b/test/SemaObjCXX/Inputs/nullability-consistency-8.h @@ -9,3 +9,19 @@ void func2(mynonnull i); void func3(int *); // expected-warning{{pointer is missing a nullability type specifier}} +#define CF_RETURNS_NOT_RETAINED __attribute__((cf_returns_not_retained)) +typedef void *CFTypeRef; +void cf1(CFTypeRef * p CF_RETURNS_NOT_RETAINED); // expected-warning {{pointer is missing a nullability type specifier}} + +void cf2(CFTypeRef * __nullable p CF_RETURNS_NOT_RETAINED); +void cf3(CFTypeRef * __nonnull p CF_RETURNS_NOT_RETAINED); + +void cf4(CFTypeRef __nullable * __nullable p CF_RETURNS_NOT_RETAINED); +void cf5(CFTypeRef __nonnull * __nullable p CF_RETURNS_NOT_RETAINED); + +void cf6(CFTypeRef * __nullable CF_RETURNS_NOT_RETAINED p); +void cf7(CF_RETURNS_NOT_RETAINED CFTypeRef * __nonnull p); + +typedef CFTypeRef __nullable *CFTypeRefPtr; +void cfp1(CFTypeRefPtr p CF_RETURNS_NOT_RETAINED); // expected-warning {{pointer is missing a nullability type specifier}} +void cfp2(CFTypeRefPtr __nonnull p CF_RETURNS_NOT_RETAINED); -- GitLab