modern objective-c rewriter: further improvement in
writing @synchronized statement; do not call locking
expression more than once and support early exits in
@synchronized's statement block (such as return).


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@152993 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Rewrite/RewriteModernObjC.cpp b/lib/Rewrite/RewriteModernObjC.cpp
index b10d641..78e0dbc 100644
--- a/lib/Rewrite/RewriteModernObjC.cpp
+++ b/lib/Rewrite/RewriteModernObjC.cpp
@@ -1703,18 +1703,30 @@
   assert((*startBuf == '@') && "bogus @synchronized location");
 
   std::string buf;
-  buf = "{ id volatile _rethrow = 0; objc_sync_enter((id)";
+  buf = "{ id _rethrow = 0; id _sync_obj = ";
   
   const char *lparenBuf = startBuf;
   while (*lparenBuf != '(') lparenBuf++;
   ReplaceText(startLoc, lparenBuf-startBuf+1, buf);
-  SourceLocation endLoc = S->getSynchBody()->getLocStart();
-  const char *endBuf = SM->getCharacterData(endLoc);
-  while (*endBuf != ')') endBuf--;
-  SourceLocation rparenLoc = startLoc.getLocWithOffset(endBuf-startBuf);
-  buf = ");\n";
-  buf += "try ";
-  ReplaceText(rparenLoc, 1, buf);
+  
+  buf = "; objc_sync_enter(_sync_obj);\n";
+  buf += "try {\n\tstruct _SYNC_EXIT { _SYNC_EXIT(id arg) : sync_exit(arg) {}";
+  buf += "\n\t~_SYNC_EXIT() {objc_sync_exit(sync_exit);}";
+  buf += "\n\tid sync_exit;";
+  buf += "\n\t} _sync_exit(_sync_obj);\n";
+
+  // We can't use S->getSynchExpr()->getLocEnd() to find the end location, since
+  // the sync expression is typically a message expression that's already
+  // been rewritten! (which implies the SourceLocation's are invalid).
+  SourceLocation RParenExprLoc = S->getSynchBody()->getLocStart();
+  const char *RParenExprLocBuf = SM->getCharacterData(RParenExprLoc);
+  while (*RParenExprLocBuf != ')') RParenExprLocBuf--;
+  RParenExprLoc = startLoc.getLocWithOffset(RParenExprLocBuf-startBuf);
+  
+  SourceLocation LBranceLoc = S->getSynchBody()->getLocStart();
+  const char *LBraceLocBuf = SM->getCharacterData(LBranceLoc);
+  assert (*LBraceLocBuf == '{');
+  ReplaceText(RParenExprLoc, (LBraceLocBuf - SM->getCharacterData(RParenExprLoc) + 1), buf);
   
   SourceLocation startRBraceLoc = S->getSynchBody()->getLocEnd();
   assert((*SM->getCharacterData(startRBraceLoc) == '}') &&
@@ -1722,27 +1734,6 @@
   
   buf = "} catch (id e) {_rethrow = e;}\n";
   Write_RethrowObject(buf);
-  
-  // produce objc_sync_exit(expr);
-  std::string syncBuf;
-  syncBuf += "\n\tobjc_sync_exit(";
-
-  Expr *syncExpr = S->getSynchExpr();
-  CastKind CK = syncExpr->getType()->isObjCObjectPointerType()
-                  ? CK_BitCast :
-                syncExpr->getType()->isBlockPointerType()
-                  ? CK_BlockPointerToObjCPointerCast
-                  : CK_CPointerToObjCPointerCast;
-  syncExpr = NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(),
-                                      CK, syncExpr);
-  std::string syncExprBufS;
-  llvm::raw_string_ostream syncExprBuf(syncExprBufS);
-  syncExpr->printPretty(syncExprBuf, *Context, 0,
-                        PrintingPolicy(LangOpts));
-  syncBuf += syncExprBuf.str();
-  syncBuf += ");";
-  
-  buf += syncBuf;
   buf += "}\n";
   buf += "}\n";
 
@@ -1908,6 +1899,8 @@
     
     SourceLocation endFinalBodyLoc = body->getLocEnd();
     ReplaceText(endFinalBodyLoc, 1, "}\n}");
+    // Now check for any return/continue/go statements within the @try.
+    WarnAboutReturnGotoStmts(S->getTryBody());
   }
 
   return 0;
diff --git a/test/Rewriter/rewrite-modern-synchronized.m b/test/Rewriter/rewrite-modern-synchronized.m
index 789f313..6dbba9a 100644
--- a/test/Rewriter/rewrite-modern-synchronized.m
+++ b/test/Rewriter/rewrite-modern-synchronized.m
@@ -21,3 +21,10 @@
     return;
  }
 }
+
+void test_sync_with_implicit_finally() {
+    id foo;
+    @synchronized (foo) {
+        return; // The rewriter knows how to generate code for implicit finally
+    }
+}