ahatanak created this revision.
ahatanak added reviewers: rjmccall, erik.pilkington, arphaman.
ahatanak added a project: clang.
Herald added subscribers: dexonsmith, jkorous.

If the block implicitly referencing `self` doesn't escape, there is no risk of 
creating retain cycles, so clang shouldn't diagnose it and force users to add 
`self->` to silence the diagnostic.

Also, fix a bug where clang was failing to diagnose `self` referenced inside a 
block that was nested inside a c++ lambda.

rdar://problem/25059955


Repository:
  rC Clang

https://reviews.llvm.org/D60736

Files:
  include/clang/Sema/ScopeInfo.h
  lib/Sema/Sema.cpp
  lib/Sema/SemaDeclObjC.cpp
  lib/Sema/SemaExpr.cpp
  test/SemaObjC/warn-implicit-self-in-block.m
  test/SemaObjCXX/warn-implicit-self-in-block.mm

Index: test/SemaObjCXX/warn-implicit-self-in-block.mm
===================================================================
--- /dev/null
+++ test/SemaObjCXX/warn-implicit-self-in-block.mm
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -x objective-c++ -std=c++11 -fobjc-arc -fblocks -Wimplicit-retain-self -verify %s
+// rdar://11194874
+
+typedef void (^BlockTy)();
+
+void noescapeFunc(__attribute__((noescape)) BlockTy);
+void escapeFunc(BlockTy);
+
+@interface Root @end
+
+@interface I : Root
+{
+  int _bar;
+}
+@end
+
+@implementation I
+  - (void)foo{
+      ^{
+           _bar = 3; // expected-warning {{block implicitly retains 'self'; explicitly mention 'self' to indicate this is intended behavior}}
+       }();
+  }
+
+  - (void)testNested{
+    noescapeFunc(^{
+      (void)_bar;
+      escapeFunc(^{
+        (void)_bar; // expected-warning {{block implicitly retains 'self'; explicitly mention 'self' to indicate this is intended behavior}}
+        noescapeFunc(^{
+          (void)_bar; // expected-warning {{block implicitly retains 'self'; explicitly mention 'self' to indicate this is intended behavior}}
+        });
+        (void)_bar; // expected-warning {{block implicitly retains 'self'; explicitly mention 'self' to indicate this is intended behavior}}
+      });
+      (void)_bar;
+    });
+  }
+
+  - (void)testLambdaInBlock{
+    noescapeFunc(^{ [&](){ (void)_bar; }(); });
+    escapeFunc(^{ [&](){ (void)_bar; }(); }); // expected-warning {{block implicitly retains 'self'; explicitly mention 'self' to indicate this is intended behavior}}
+  }
+@end
Index: test/SemaObjC/warn-implicit-self-in-block.m
===================================================================
--- test/SemaObjC/warn-implicit-self-in-block.m
+++ /dev/null
@@ -1,18 +0,0 @@
-// RUN: %clang_cc1 -x objective-c -fobjc-arc -fblocks -Wimplicit-retain-self -verify %s
-// rdar://11194874
-
-@interface Root @end
-
-@interface I : Root
-{
-  int _bar;
-}
-@end
-
-@implementation I
-  - (void)foo{
-      ^{
-           _bar = 3; // expected-warning {{block implicitly retains 'self'; explicitly mention 'self' to indicate this is intended behavior}}
-       }();
-  }
-@end
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -2575,11 +2575,6 @@
             !Diags.isIgnored(diag::warn_arc_repeated_use_of_weak, Loc))
           getCurFunction()->recordUseOfWeak(Result);
       }
-      if (getLangOpts().ObjCAutoRefCount) {
-        if (CurContext->isClosure())
-          Diag(Loc, diag::warn_implicitly_retains_self)
-            << FixItHint::CreateInsertion(Loc, "self->");
-      }
 
       return Result;
     }
@@ -13652,6 +13647,9 @@
     }
   }
 
+  if (getCurFunction())
+    getCurFunction()->addBlock(Block, /*IsIntroducedInCurrentScope*/true);
+
   PushBlockScope(CurScope, Block);
   CurContext->addDecl(Block);
   if (CurScope)
@@ -13913,9 +13911,6 @@
     }
   }
 
-  if (getCurFunction())
-    getCurFunction()->addBlock(BD);
-
   return Result;
 }
 
Index: lib/Sema/SemaDeclObjC.cpp
===================================================================
--- lib/Sema/SemaDeclObjC.cpp
+++ lib/Sema/SemaDeclObjC.cpp
@@ -378,6 +378,7 @@
   // Allow all of Sema to see that we are entering a method definition.
   PushDeclContext(FnBodyScope, MDecl);
   PushFunctionScope();
+  getCurFunction()->setIsObjCMethod();
 
   // Create Decl objects for each parameter, entrring them in the scope for
   // binding to their use.
Index: lib/Sema/Sema.cpp
===================================================================
--- lib/Sema/Sema.cpp
+++ lib/Sema/Sema.cpp
@@ -20,6 +20,7 @@
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/PrettyDeclStackTrace.h"
 #include "clang/AST/StmtCXX.h"
+#include "clang/AST/StmtVisitor.h"
 #include "clang/Basic/DiagnosticOptions.h"
 #include "clang/Basic/PartialDiagnostic.h"
 #include "clang/Basic/TargetInfo.h"
@@ -1636,10 +1637,16 @@
 static void markEscapingByrefs(const FunctionScopeInfo &FSI, Sema &S) {
   // Set the EscapingByref flag of __block variables captured by
   // escaping blocks.
-  for (const BlockDecl *BD : FSI.Blocks) {
-    if (BD->doesNotEscape())
+  for (const std::pair<const BlockDecl *, bool> &P : FSI.Blocks) {
+    // Skip blocks that aren't directly introduced in this function. We've
+    // already seen them.
+    if (!P.second)
       continue;
-    for (const BlockDecl::Capture &BC : BD->captures()) {
+
+    if (P.first->doesNotEscape())
+      continue;
+
+    for (const BlockDecl::Capture &BC : P.first->captures()) {
       VarDecl *VD = BC.getVariable();
       if (VD->hasAttr<BlocksAttr>())
         VD->setEscapingByref();
@@ -1659,6 +1666,54 @@
   }
 }
 
+namespace {
+
+class DiagImplicitRetainedSelf
+    : public ConstStmtVisitor<DiagImplicitRetainedSelf> {
+  Sema &S;
+
+  // A flag indicating whether we are visiting a Stmt that is inside a block
+  // that can possibly escape.
+  bool VisitingEscapingBlock = false;
+
+public:
+  DiagImplicitRetainedSelf(Sema &S) : S(S) {}
+
+  void VisitBlockDecl(const BlockDecl *BD) {
+    bool OldVisitingEscapingBlock = VisitingEscapingBlock;
+    VisitingEscapingBlock = VisitingEscapingBlock || !BD->doesNotEscape();
+    Visit(BD->getBody());
+    VisitingEscapingBlock = OldVisitingEscapingBlock;
+  }
+
+  void VisitBlockExpr(const BlockExpr *BE) {
+    VisitBlockDecl(BE->getBlockDecl());
+  }
+
+  void VisitObjCIvarRefExpr(const ObjCIvarRefExpr *ORE) {
+    if (!VisitingEscapingBlock || !ORE->isFreeIvar())
+      return;
+    SourceLocation Loc = ORE->getExprLoc();
+    S.Diag(Loc, diag::warn_implicitly_retains_self)
+        << FixItHint::CreateInsertion(Loc, "self->");
+  }
+
+  void VisitStmt(const Stmt *S) {
+    for (const Stmt *SubStmt : S->children())
+      if (SubStmt)
+        Visit(SubStmt);
+  }
+};
+
+} // namespace
+
+static void diagnoseImplicitlyRetainedSelf(const FunctionScopeInfo &FSI,
+                                           Sema &S) {
+  DiagImplicitRetainedSelf D(S);
+  for (const std::pair<const BlockDecl *, bool> &P : FSI.Blocks)
+    D.VisitBlockDecl(P.first);
+}
+
 void Sema::PopFunctionScopeInfo(const AnalysisBasedWarnings::Policy *WP,
                                 const Decl *D, const BlockExpr *blkExpr) {
   assert(!FunctionScopes.empty() && "mismatched push/pop!");
@@ -1671,6 +1726,16 @@
 
   FunctionScopeInfo *Scope = FunctionScopes.pop_back_val();
 
+  if (getLangOpts().ObjCAutoRefCount && Scope->isObjCMethod())
+    diagnoseImplicitlyRetainedSelf(*Scope, *this);
+
+  // If the current scope isn't a block, add the blocks in the list to the
+  // enclosing function's block list.
+  if (!Scope->isBlockScope() && !FunctionScopes.empty())
+    for (std::pair<const BlockDecl *, bool> &p : Scope->Blocks)
+      FunctionScopes.back()->addBlock(
+          p.first, /*IsIntroducedInCurrentScope*/false);
+
   if (LangOpts.OpenMP)
     popOpenMPFunctionRegion(Scope);
 
Index: include/clang/Sema/ScopeInfo.h
===================================================================
--- include/clang/Sema/ScopeInfo.h
+++ include/clang/Sema/ScopeInfo.h
@@ -151,6 +151,9 @@
   /// false if there is an invocation of an initializer on 'self'.
   bool ObjCWarnForNoInitDelegation : 1;
 
+  /// A flag indicating the scope is an ObjC method definition.
+  bool IsObjCMethod : 1;
+
   /// True only when this function has not already built, or attempted
   /// to build, the initial and final coroutine suspend points
   bool NeedsCoroutineSuspends : 1;
@@ -202,8 +205,23 @@
   /// function.
   SmallVector<CompoundScopeInfo, 4> CompoundScopes;
 
-  /// The set of blocks that are introduced in this function.
-  llvm::SmallPtrSet<const BlockDecl *, 1> Blocks;
+  /// The list of blocks that are directly or indirectly introduced in this
+  /// function and aren't enclosed in another block. The boolean flag indicates
+  /// whether the block is introduced in the current function scope. For
+  /// example, when function 'foo1' is parsed in the code below, the flag is
+  /// false for the first block and is true for the second block. The block that
+  /// calls 'foo2(2)' isn't added to the list since it is nested inside another
+  /// block.
+  ///
+  /// void foo1() {
+  ///   [](){ ^{ foo2(0); }; }();
+  ///   ^{ foo2(1); }();
+  ///   ^{ ^{ foo2(2); }(); }();
+  /// }
+  ///
+  /// The blocks are stored to the list in the order they appear in the
+  /// function.
+  llvm::SmallVector<std::pair<const BlockDecl *, bool>, 1> Blocks;
 
   /// The set of __block variables that are introduced in this function.
   llvm::TinyPtrVector<VarDecl *> ByrefBlockVars;
@@ -432,9 +450,17 @@
           (HasBranchProtectedScope && HasBranchIntoScope));
   }
 
-  // Add a block introduced in this function.
-  void addBlock(const BlockDecl *BD) {
-    Blocks.insert(BD);
+  bool isObjCMethod() const {
+    return IsObjCMethod;
+  }
+
+  void setIsObjCMethod() {
+    IsObjCMethod = true;
+  }
+
+  // Add a block to the list.
+  void addBlock(const BlockDecl *BD, bool IsIntroducedInCurrentScope) {
+    Blocks.push_back({BD, IsIntroducedInCurrentScope});
   }
 
   // Add a __block variable introduced in this function.
@@ -442,6 +468,8 @@
     ByrefBlockVars.push_back(VD);
   }
 
+  bool isBlockScope() const { return Kind == SK_Block; }
+
   bool isCoroutine() const { return !FirstCoroutineStmtLoc.isInvalid(); }
 
   void setFirstCoroutineStmt(SourceLocation Loc, StringRef Keyword) {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to