[clang] 40568fe - [CodeGen] Emit destructor calls to destruct compound literals

Akira Hatanaka via cfe-commits cfe-commits at lists.llvm.org
Tue Mar 10 14:08:47 PDT 2020


Author: Akira Hatanaka
Date: 2020-03-10T14:08:28-07:00
New Revision: 40568fec7e3eb51554cbdaf63a8044830be93fa4

URL: https://github.com/llvm/llvm-project/commit/40568fec7e3eb51554cbdaf63a8044830be93fa4
DIFF: https://github.com/llvm/llvm-project/commit/40568fec7e3eb51554cbdaf63a8044830be93fa4.diff

LOG: [CodeGen] Emit destructor calls to destruct compound literals

Fix a bug in IRGen where it wasn't destructing compound literals in C
that are ObjC pointer arrays or non-trivial structs. Also diagnose jumps
that enter or exit the lifetime of the compound literals.

rdar://problem/51867864

Differential Revision: https://reviews.llvm.org/D64464

Added: 
    clang/test/AST/ast-dump-objc-arc-json.m
    clang/test/Import/objc-arc/Inputs/cleanup-objects.m
    clang/test/Import/objc-arc/test-cleanup-object.m
    clang/test/PCH/non-trivial-c-compound-literal.m

Modified: 
    clang/include/clang/AST/ASTImporter.h
    clang/include/clang/AST/ExprCXX.h
    clang/include/clang/AST/TextNodeDumper.h
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/include/clang/Sema/Sema.h
    clang/include/clang/Serialization/ASTBitCodes.h
    clang/lib/AST/ASTImporter.cpp
    clang/lib/AST/JSONNodeDumper.cpp
    clang/lib/AST/TextNodeDumper.cpp
    clang/lib/CodeGen/CGBlocks.cpp
    clang/lib/CodeGen/CGExpr.cpp
    clang/lib/CodeGen/CGExprAgg.cpp
    clang/lib/CodeGen/CGExprScalar.cpp
    clang/lib/Sema/JumpDiagnostics.cpp
    clang/lib/Sema/SemaExpr.cpp
    clang/lib/Serialization/ASTReaderStmt.cpp
    clang/lib/Serialization/ASTWriterStmt.cpp
    clang/test/AST/ast-dump-stmt.m
    clang/test/CodeGenObjC/arc-ternary-op.m
    clang/test/CodeGenObjC/arc.m
    clang/test/CodeGenObjC/strong-in-c-struct.m
    clang/test/SemaObjC/strong-in-c-struct.m
    clang/tools/clang-import-test/clang-import-test.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/AST/ASTImporter.h b/clang/include/clang/AST/ASTImporter.h
index 490b34bf95e8..205d7ec67754 100644
--- a/clang/include/clang/AST/ASTImporter.h
+++ b/clang/include/clang/AST/ASTImporter.h
@@ -16,6 +16,7 @@
 
 #include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclarationName.h"
+#include "clang/AST/ExprCXX.h"
 #include "clang/AST/NestedNameSpecifier.h"
 #include "clang/AST/TemplateName.h"
 #include "clang/AST/Type.h"
@@ -349,6 +350,10 @@ class TypeSourceInfo;
       return ToOrErr.takeError();
     }
 
+    /// Import cleanup objects owned by ExprWithCleanup.
+    llvm::Expected<ExprWithCleanups::CleanupObject>
+    Import(ExprWithCleanups::CleanupObject From);
+
     /// Import the given type from the "from" context into the "to"
     /// context. A null type is imported as a null type (no error).
     ///

diff  --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 2bd68eec175a..694575790f48 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -3321,13 +3321,15 @@ class DependentScopeDeclRefExpr final
 /// literal is the extent of the enclosing scope.
 class ExprWithCleanups final
     : public FullExpr,
-      private llvm::TrailingObjects<ExprWithCleanups, BlockDecl *> {
+      private llvm::TrailingObjects<
+          ExprWithCleanups,
+          llvm::PointerUnion<BlockDecl *, CompoundLiteralExpr *>> {
 public:
   /// The type of objects that are kept in the cleanup.
-  /// It's useful to remember the set of blocks;  we could also
-  /// remember the set of temporaries, but there's currently
-  /// no need.
-  using CleanupObject = BlockDecl *;
+  /// It's useful to remember the set of blocks and block-scoped compound
+  /// literals; we could also remember the set of temporaries, but there's
+  /// currently no need.
+  using CleanupObject = llvm::PointerUnion<BlockDecl *, CompoundLiteralExpr *>;
 
 private:
   friend class ASTStmtReader;

diff  --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h
index 5dc5022ed697..b08d1e482be8 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -184,6 +184,7 @@ class TextNodeDumper
   void dumpBareDeclRef(const Decl *D);
   void dumpName(const NamedDecl *ND);
   void dumpAccessSpecifier(AccessSpecifier AS);
+  void dumpCleanupObject(const ExprWithCleanups::CleanupObject &C);
 
   void dumpDeclRef(const Decl *D, StringRef Label = {});
 

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d8b43519dd3e..4193b0390ce0 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5543,6 +5543,8 @@ def note_enters_block_captures_weak : Note<
 def note_enters_block_captures_non_trivial_c_struct : Note<
   "jump enters lifetime of block which captures a C struct that is non-trivial "
   "to destroy">;
+def note_enters_compound_literal_scope : Note<
+  "jump enters lifetime of a compound literal that is non-trivial to destruct">;
 
 def note_exits_cleanup : Note<
   "jump exits scope of variable with __attribute__((cleanup))">;
@@ -5586,6 +5588,8 @@ def note_exits_block_captures_weak : Note<
 def note_exits_block_captures_non_trivial_c_struct : Note<
   "jump exits lifetime of block which captures a C struct that is non-trivial "
   "to destroy">;
+def note_exits_compound_literal_scope : Note<
+  "jump exits lifetime of a compound literal that is non-trivial to destruct">;
 
 def err_func_returning_qualified_void : ExtWarn<
   "function cannot return qualified void type %0">,

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 60a4f2ef7048..d24dfe563bd2 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -613,9 +613,8 @@ class Sema final {
   CleanupInfo Cleanup;
 
   /// ExprCleanupObjects - This is the stack of objects requiring
-  /// cleanup that are created by the current full expression.  The
-  /// element type here is ExprWithCleanups::Object.
-  SmallVector<BlockDecl*, 8> ExprCleanupObjects;
+  /// cleanup that are created by the current full expression.
+  SmallVector<ExprWithCleanups::CleanupObject, 8> ExprCleanupObjects;
 
   /// Store a set of either DeclRefExprs or MemberExprs that contain a reference
   /// to a variable (constant) that may or may not be odr-used in this Expr, and

diff  --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index 0767fb8e22c1..31226fb0516c 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1900,6 +1900,9 @@ namespace serialization {
       CTOR_INITIALIZER_INDIRECT_MEMBER
     };
 
+    /// Kinds of cleanup objects owned by ExprWithCleanups.
+    enum CleanupObjectKind { COK_Block, COK_CompoundLiteral };
+
     /// Describes the redeclarations of a declaration.
     struct LocalRedeclarationsInfo {
       // The ID of the first declaration

diff  --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index ffe867cbee22..86be6fc582a7 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -7910,6 +7910,18 @@ void ASTImporter::RegisterImportedDecl(Decl *FromD, Decl *ToD) {
   MapImported(FromD, ToD);
 }
 
+llvm::Expected<ExprWithCleanups::CleanupObject>
+ASTImporter::Import(ExprWithCleanups::CleanupObject From) {
+  if (auto *CLE = From.dyn_cast<CompoundLiteralExpr *>()) {
+    if (Expected<Expr *> R = Import(CLE))
+      return ExprWithCleanups::CleanupObject(cast<CompoundLiteralExpr>(*R));
+  }
+
+  // FIXME: Handle BlockDecl when we implement importing BlockExpr in
+  //        ASTNodeImporter.
+  return make_error<ImportError>(ImportError::UnsupportedConstruct);
+}
+
 Expected<QualType> ASTImporter::Import(QualType FromT) {
   if (FromT.isNull())
     return QualType{};

diff  --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp
index bae5566057b6..1f7dc5e9190f 100644
--- a/clang/lib/AST/JSONNodeDumper.cpp
+++ b/clang/lib/AST/JSONNodeDumper.cpp
@@ -1334,7 +1334,16 @@ void JSONNodeDumper::VisitExprWithCleanups(const ExprWithCleanups *EWC) {
   if (EWC->getNumObjects()) {
     JOS.attributeArray("cleanups", [this, EWC] {
       for (const ExprWithCleanups::CleanupObject &CO : EWC->getObjects())
-        JOS.value(createBareDeclRef(CO));
+        if (auto *BD = CO.dyn_cast<BlockDecl *>()) {
+          JOS.value(createBareDeclRef(BD));
+        } else if (auto *CLE = CO.dyn_cast<CompoundLiteralExpr *>()) {
+          llvm::json::Object Obj;
+          Obj["id"] = createPointerRepresentation(CLE);
+          Obj["kind"] = CLE->getStmtClassName();
+          JOS.value(std::move(Obj));
+        } else {
+          llvm_unreachable("unexpected cleanup object type");
+        }
     });
   }
 }

diff  --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 9ed2829acbb5..a49b022629dc 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -448,6 +448,23 @@ void TextNodeDumper::dumpAccessSpecifier(AccessSpecifier AS) {
   }
 }
 
+void TextNodeDumper::dumpCleanupObject(
+    const ExprWithCleanups::CleanupObject &C) {
+  if (auto *BD = C.dyn_cast<BlockDecl *>())
+    dumpDeclRef(BD, "cleanup");
+  else if (auto *CLE = C.dyn_cast<CompoundLiteralExpr *>())
+    AddChild([=] {
+      OS << "cleanup ";
+      {
+        ColorScope Color(OS, ShowColors, StmtColor);
+        OS << CLE->getStmtClassName();
+      }
+      dumpPointer(CLE);
+    });
+  else
+    llvm_unreachable("unexpected cleanup type");
+}
+
 void TextNodeDumper::dumpDeclRef(const Decl *D, StringRef Label) {
   if (!D)
     return;
@@ -950,7 +967,7 @@ void TextNodeDumper::VisitMaterializeTemporaryExpr(
 
 void TextNodeDumper::VisitExprWithCleanups(const ExprWithCleanups *Node) {
   for (unsigned i = 0, e = Node->getNumObjects(); i != e; ++i)
-    dumpDeclRef(Node->getObject(i), "cleanup");
+    dumpCleanupObject(Node->getObject(i));
 }
 
 void TextNodeDumper::VisitSizeOfPackExpr(const SizeOfPackExpr *Node) {

diff  --git a/clang/lib/CodeGen/CGBlocks.cpp b/clang/lib/CodeGen/CGBlocks.cpp
index 9ec37385c4a7..9097e51e9c00 100644
--- a/clang/lib/CodeGen/CGBlocks.cpp
+++ b/clang/lib/CodeGen/CGBlocks.cpp
@@ -860,13 +860,13 @@ static void enterBlockScope(CodeGenFunction &CGF, BlockDecl *block) {
 }
 
 /// Enter a full-expression with a non-trivial number of objects to
-/// clean up.  This is in this file because, at the moment, the only
-/// kind of cleanup object is a BlockDecl*.
+/// clean up.
 void CodeGenFunction::enterNonTrivialFullExpression(const FullExpr *E) {
   if (const auto EWC = dyn_cast<ExprWithCleanups>(E)) {
     assert(EWC->getNumObjects() != 0);
     for (const ExprWithCleanups::CleanupObject &C : EWC->getObjects())
-      enterBlockScope(*this, C);
+      if (auto *BD = C.dyn_cast<BlockDecl *>())
+        enterBlockScope(*this, BD);
   }
 }
 

diff  --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 356d76893364..a3399af183b5 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -4268,6 +4268,14 @@ LValue CodeGenFunction::EmitCompoundLiteralLValue(const CompoundLiteralExpr *E){
   EmitAnyExprToMem(InitExpr, DeclPtr, E->getType().getQualifiers(),
                    /*Init*/ true);
 
+  // Block-scope compound literals are destroyed at the end of the enclosing
+  // scope in C.
+  if (!getLangOpts().CPlusPlus)
+    if (QualType::DestructionKind DtorKind = E->getType().isDestructedType())
+      pushLifetimeExtendedDestroy(getCleanupKind(DtorKind), DeclPtr,
+                                  E->getType(), getDestroyer(DtorKind),
+                                  DtorKind & EHCleanup);
+
   return Result;
 }
 

diff  --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index 8de609a2ccd9..9881d28fe25c 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -659,7 +659,21 @@ AggExprEmitter::VisitCompoundLiteralExpr(CompoundLiteralExpr *E) {
   }
 
   AggValueSlot Slot = EnsureSlot(E->getType());
+
+  // Block-scope compound literals are destroyed at the end of the enclosing
+  // scope in C.
+  bool Destruct =
+      !CGF.getLangOpts().CPlusPlus && !Slot.isExternallyDestructed();
+  if (Destruct)
+    Slot.setExternallyDestructed();
+
   CGF.EmitAggExpr(E->getInitializer(), Slot);
+
+  if (Destruct)
+    if (QualType::DestructionKind DtorKind = E->getType().isDestructedType())
+      CGF.pushLifetimeExtendedDestroy(
+          CGF.getCleanupKind(DtorKind), Slot.getAddress(), E->getType(),
+          CGF.getDestroyer(DtorKind), DtorKind & EHCleanup);
 }
 
 /// Attempt to look through various unimportant expressions to find a

diff  --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 8d9bf17969a3..a0d1fe24da92 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -556,6 +556,11 @@ class ScalarExprEmitter
   Value *VisitMemberExpr(MemberExpr *E);
   Value *VisitExtVectorElementExpr(Expr *E) { return EmitLoadOfLValue(E); }
   Value *VisitCompoundLiteralExpr(CompoundLiteralExpr *E) {
+    // Strictly speaking, we shouldn't be calling EmitLoadOfLValue, which
+    // transitively calls EmitCompoundLiteralLValue, here in C++ since compound
+    // literals aren't l-values in C++. We do so simply because that's the
+    // cleanest way to handle compound literals in C++.
+    // See the discussion here: https://reviews.llvm.org/D64464
     return EmitLoadOfLValue(E);
   }
 

diff  --git a/clang/lib/Sema/JumpDiagnostics.cpp b/clang/lib/Sema/JumpDiagnostics.cpp
index 960e62d4a2db..b34243edea35 100644
--- a/clang/lib/Sema/JumpDiagnostics.cpp
+++ b/clang/lib/Sema/JumpDiagnostics.cpp
@@ -75,6 +75,7 @@ class JumpScopeChecker {
   void BuildScopeInformation(Decl *D, unsigned &ParentScope);
   void BuildScopeInformation(VarDecl *D, const BlockDecl *BDecl,
                              unsigned &ParentScope);
+  void BuildScopeInformation(CompoundLiteralExpr *CLE, unsigned &ParentScope);
   void BuildScopeInformation(Stmt *S, unsigned &origParentScope);
 
   void VerifyJumps();
@@ -276,6 +277,16 @@ void JumpScopeChecker::BuildScopeInformation(VarDecl *D,
   }
 }
 
+/// Build scope information for compound literals of C struct types that are
+/// non-trivial to destruct.
+void JumpScopeChecker::BuildScopeInformation(CompoundLiteralExpr *CLE,
+                                             unsigned &ParentScope) {
+  unsigned InDiag = diag::note_enters_compound_literal_scope;
+  unsigned OutDiag = diag::note_exits_compound_literal_scope;
+  Scopes.push_back(GotoScope(ParentScope, InDiag, OutDiag, CLE->getExprLoc()));
+  ParentScope = Scopes.size() - 1;
+}
+
 /// BuildScopeInformation - The statements from CI to CE are known to form a
 /// coherent VLA scope with a specified parent node.  Walk through the
 /// statements, adding any labels or gotos to LabelAndGotoScopes and recursively
@@ -529,11 +540,15 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S,
     // implementable but a lot of work which we haven't felt up to doing.
     ExprWithCleanups *EWC = cast<ExprWithCleanups>(S);
     for (unsigned i = 0, e = EWC->getNumObjects(); i != e; ++i) {
-      const BlockDecl *BDecl = EWC->getObject(i);
-      for (const auto &CI : BDecl->captures()) {
-        VarDecl *variable = CI.getVariable();
-        BuildScopeInformation(variable, BDecl, origParentScope);
-      }
+      if (auto *BDecl = EWC->getObject(i).dyn_cast<BlockDecl *>())
+        for (const auto &CI : BDecl->captures()) {
+          VarDecl *variable = CI.getVariable();
+          BuildScopeInformation(variable, BDecl, origParentScope);
+        }
+      else if (auto *CLE = EWC->getObject(i).dyn_cast<CompoundLiteralExpr *>())
+        BuildScopeInformation(CLE, origParentScope);
+      else
+        llvm_unreachable("unexpected cleanup object type");
     }
     break;
   }

diff  --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index f50a77a40510..dc69b198a289 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6251,14 +6251,24 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo,
     return ExprError();
   }
 
-  // Compound literals that have automatic storage duration are destroyed at
-  // the end of the scope. Emit diagnostics if it is or contains a C union type
-  // that is non-trivial to destruct.
-  if (!isFileScope)
+  if (!isFileScope && !getLangOpts().CPlusPlus) {
+    // Compound literals that have automatic storage duration are destroyed at
+    // the end of the scope in C; in C++, they're just temporaries.
+
+    // Emit diagnostics if it is or contains a C union type that is non-trivial
+    // to destruct.
     if (E->getType().hasNonTrivialToPrimitiveDestructCUnion())
       checkNonTrivialCUnion(E->getType(), E->getExprLoc(),
                             NTCUC_CompoundLiteral, NTCUK_Destruct);
 
+    // Diagnose jumps that enter or exit the lifetime of the compound literal.
+    if (literalType.isDestructedType()) {
+      Cleanup.setExprNeedsCleanups(true);
+      ExprCleanupObjects.push_back(E);
+      getCurFunction()->setHasBranchProtectedScope();
+    }
+  }
+
   if (E->getType().hasNonTrivialToPrimitiveDefaultInitializeCUnion() ||
       E->getType().hasNonTrivialToPrimitiveCopyCUnion())
     checkNonTrivialCUnionInInitializer(E->getInitializer(),

diff  --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 975b7580d861..4c1abde29a20 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1819,9 +1819,17 @@ void ASTStmtReader::VisitExprWithCleanups(ExprWithCleanups *E) {
 
   unsigned NumObjects = Record.readInt();
   assert(NumObjects == E->getNumObjects());
-  for (unsigned i = 0; i != NumObjects; ++i)
-    E->getTrailingObjects<BlockDecl *>()[i] =
-        readDeclAs<BlockDecl>();
+  for (unsigned i = 0; i != NumObjects; ++i) {
+    unsigned CleanupKind = Record.readInt();
+    ExprWithCleanups::CleanupObject Obj;
+    if (CleanupKind == COK_Block)
+      Obj = readDeclAs<BlockDecl>();
+    else if (CleanupKind == COK_CompoundLiteral)
+      Obj = cast<CompoundLiteralExpr>(Record.readSubExpr());
+    else
+      llvm_unreachable("unexpected cleanup object type");
+    E->getTrailingObjects<ExprWithCleanups::CleanupObject>()[i] = Obj;
+  }
 
   E->ExprWithCleanupsBits.CleanupsHaveSideEffects = Record.readInt();
   E->SubExpr = Record.readSubExpr();

diff  --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 0aadeaa9a615..bf63ac300c6d 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1721,8 +1721,15 @@ void ASTStmtWriter::VisitCXXPseudoDestructorExpr(CXXPseudoDestructorExpr *E) {
 void ASTStmtWriter::VisitExprWithCleanups(ExprWithCleanups *E) {
   VisitExpr(E);
   Record.push_back(E->getNumObjects());
-  for (unsigned i = 0, e = E->getNumObjects(); i != e; ++i)
-    Record.AddDeclRef(E->getObject(i));
+  for (auto &Obj : E->getObjects()) {
+    if (auto *BD = Obj.dyn_cast<BlockDecl *>()) {
+      Record.push_back(serialization::COK_Block);
+      Record.AddDeclRef(BD);
+    } else if (auto *CLE = Obj.dyn_cast<CompoundLiteralExpr *>()) {
+      Record.push_back(serialization::COK_CompoundLiteral);
+      Record.AddStmt(CLE);
+    }
+  }
 
   Record.push_back(E->cleanupsHaveSideEffects());
   Record.AddStmt(E->getSubExpr());

diff  --git a/clang/test/AST/ast-dump-objc-arc-json.m b/clang/test/AST/ast-dump-objc-arc-json.m
new file mode 100644
index 000000000000..bb9268b28ef6
--- /dev/null
+++ b/clang/test/AST/ast-dump-objc-arc-json.m
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -x objective-c -fobjc-arc -ast-dump=json -ast-dump-filter Test %s | FileCheck %s
+
+typedef struct {
+  id f;
+} S;
+
+id TestCompoundLiteral(id a) {
+  return ((S){ .f = a }).f;
+}
+
+// CHECK:  "kind": "ExprWithCleanups",
+// CHECK-NEXT:  "range": {
+// CHECK-NEXT:   "begin": {
+// CHECK-NEXT:    "offset": 202,
+// CHECK-NEXT:    "col": 10,
+// CHECK-NEXT:    "tokLen": 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   "end": {
+// CHECK-NEXT:    "offset": 218,
+// CHECK-NEXT:    "col": 26,
+// CHECK-NEXT:    "tokLen": 1
+// CHECK-NEXT:   }
+// CHECK-NEXT:  },
+// CHECK-NEXT:  "type": {
+// CHECK-NEXT:   "desugaredQualType": "id",
+// CHECK-NEXT:   "qualType": "id",
+// CHECK-NEXT:   "typeAliasDeclId": "0x{{.*}}"
+// CHECK-NEXT:  },
+// CHECK-NEXT:  "valueCategory": "rvalue",
+// CHECK-NEXT:  "cleanupsHaveSideEffects": true,
+// CHECK-NEXT:  "cleanups": [
+// CHECK-NEXT:   {
+// CHECK-NEXT:    "id": "0x{{.*}}",
+// CHECK-NEXT:    "kind": "CompoundLiteralExpr"
+// CHECK-NEXT:   }
+// CHECK-NEXT:  ],

diff  --git a/clang/test/AST/ast-dump-stmt.m b/clang/test/AST/ast-dump-stmt.m
index 8c0ca897e511..9744e7a43c3e 100644
--- a/clang/test/AST/ast-dump-stmt.m
+++ b/clang/test/AST/ast-dump-stmt.m
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -Wno-unused -fblocks -fobjc-exceptions -ast-dump -ast-dump-filter Test %s | FileCheck -strict-whitespace %s
+// RUN: %clang_cc1 -Wno-unused -fobjc-arc -fblocks -fobjc-exceptions -ast-dump -ast-dump-filter Test %s | FileCheck -strict-whitespace %s
 
 void TestBlockExpr(int x) {
   ^{ x; };
@@ -34,3 +34,16 @@ void TestObjCAtCatchStmt() {
 // CHECK-NEXT:   ObjCAtCatchStmt{{.*}} catch all
 // CHECK-NEXT:     CompoundStmt
 // CHECK-NEXT:   ObjCAtFinallyStmt
+
+typedef struct {
+  id f;
+} S;
+
+id TestCompoundLiteral(id a) {
+  return ((S){ .f = a }).f;
+}
+
+// CHECK:     FunctionDecl{{.*}}TestCompoundLiteral
+// CHECK:       ExprWithCleanups
+// CHECK-NEXT:    cleanup CompoundLiteralExpr
+// CHECK:           CompoundLiteralExpr{{.*}}'S':'S' lvalue

diff  --git a/clang/test/CodeGenObjC/arc-ternary-op.m b/clang/test/CodeGenObjC/arc-ternary-op.m
index 4883143791a2..28567fcbe22e 100644
--- a/clang/test/CodeGenObjC/arc-ternary-op.m
+++ b/clang/test/CodeGenObjC/arc-ternary-op.m
@@ -1,5 +1,7 @@
 // RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fblocks -fobjc-arc -fobjc-runtime-has-weak -O2 -disable-llvm-passes -o - %s | FileCheck %s
 
+id g0, g1;
+
 void test0(_Bool cond) {
   id test0_helper(void) __attribute__((ns_returns_retained));
 
@@ -147,4 +149,58 @@ void test2(int cond) {
   // CHECK:      call void @llvm.objc.release(i8* [[RESULT]])
 }
 
+void test3(int cond) {
+  __strong id *p = cond ? (__strong id[]){g0, g1} : (__strong id[]){g1, g0};
+  test2(cond);
+
+  // CHECK: define void @test3(
+  // CHECK: %[[P:.*]] = alloca i8**, align 8
+  // CHECK: %[[_COMPOUNDLITERAL:.*]] = alloca [2 x i8*], align 8
+  // CHECK: %[[CLEANUP_COND:.*]] = alloca i1, align 1
+  // CHECK: %[[_COMPOUNDLITERAL1:.*]] = alloca [2 x i8*], align 8
+  // CHECK: %[[CLEANUP_COND4:.*]] = alloca i1, align 1
+
+  // CHECK: %[[ARRAYINIT_BEGIN:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[_COMPOUNDLITERAL]], i64 0, i64 0
+  // CHECK: %[[V2:.*]] = load i8*, i8** @g0, align 8
+  // CHECK: %[[V3:.*]] = call i8* @llvm.objc.retain(i8* %[[V2]])
+  // CHECK: store i8* %[[V3]], i8** %[[ARRAYINIT_BEGIN]], align 8
+  // CHECK: %[[ARRAYINIT_ELEMENT:.*]] = getelementptr inbounds i8*, i8** %[[ARRAYINIT_BEGIN]], i64 1
+  // CHECK: %[[V4:.*]] = load i8*, i8** @g1, align 8
+  // CHECK: %[[V5:.*]] = call i8* @llvm.objc.retain(i8* %[[V4]])
+  // CHECK: store i8* %[[V5]], i8** %[[ARRAYINIT_ELEMENT]], align 8
+  // CHECK: store i1 true, i1* %[[CLEANUP_COND]], align 1
+  // CHECK: %[[ARRAYDECAY:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[_COMPOUNDLITERAL]], i64 0, i64 0
+
+  // CHECK: %[[ARRAYINIT_BEGIN2:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[_COMPOUNDLITERAL1]], i64 0, i64 0
+  // CHECK: %[[V6:.*]] = load i8*, i8** @g1, align 8
+  // CHECK: %[[V7:.*]] = call i8* @llvm.objc.retain(i8* %[[V6]])
+  // CHECK: store i8* %[[V7]], i8** %[[ARRAYINIT_BEGIN2]], align 8
+  // CHECK: %[[ARRAYINIT_ELEMENT3:.*]] = getelementptr inbounds i8*, i8** %[[ARRAYINIT_BEGIN2]], i64 1
+  // CHECK: %[[V8:.*]] = load i8*, i8** @g0, align 8
+  // CHECK: %[[V9:.*]] = call i8* @llvm.objc.retain(i8* %[[V8]])
+  // CHECK: store i8* %[[V9]], i8** %[[ARRAYINIT_ELEMENT3]], align 8
+  // CHECK: store i1 true, i1* %[[CLEANUP_COND4]], align 1
+  // CHECK: %[[ARRAYDECAY5:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[_COMPOUNDLITERAL1]], i64 0, i64 0
+
+  // CHECK: %[[COND6:.*]] = phi i8** [ %[[ARRAYDECAY]], %{{.*}} ], [ %[[ARRAYDECAY5]], %{{.*}} ]
+  // CHECK: store i8** %[[COND6]], i8*** %[[P]], align 8
+  // CHECK: call void @test2(
+
+  // CHECK: %[[ARRAY_BEGIN:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[_COMPOUNDLITERAL1]], i32 0, i32 0
+  // CHECK: %[[V11:.*]] = getelementptr inbounds i8*, i8** %[[ARRAY_BEGIN]], i64 2
+
+  // CHECK: %[[ARRAYDESTROY_ELEMENTPAST:.*]] = phi i8** [ %[[V11]], %{{.*}} ], [ %[[ARRAYDESTROY_ELEMENT:.*]], %{{.*}} ]
+  // CHECK: %[[ARRAYDESTROY_ELEMENT]] = getelementptr inbounds i8*, i8** %[[ARRAYDESTROY_ELEMENTPAST]], i64 -1
+  // CHECK: %[[V12:.*]] = load i8*, i8** %[[ARRAYDESTROY_ELEMENT]], align 8
+  // CHECK: call void @llvm.objc.release(i8* %[[V12]])
+
+  // CHECK: %[[ARRAY_BEGIN10:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[_COMPOUNDLITERAL]], i32 0, i32 0
+  // CHECK: %[[V13:.*]] = getelementptr inbounds i8*, i8** %[[ARRAY_BEGIN10]], i64 2
+
+  // CHECK: %[[ARRAYDESTROY_ELEMENTPAST12:.*]] = phi i8** [ %[[V13]], %{{.*}} ], [ %[[ARRAYDESTROY_ELEMENT13:.*]], %{{.*}} ]
+  // CHECK: %[[ARRAYDESTROY_ELEMENT13]] = getelementptr inbounds i8*, i8** %[[ARRAYDESTROY_ELEMENTPAST12]], i64 -1
+  // CHECK: %[[V14:.*]] = load i8*, i8** %[[ARRAYDESTROY_ELEMENT13]], align 8
+  // CHECK: call void @llvm.objc.release(i8* %[[V14]])
+}
+
 // CHECK: attributes [[NUW]] = { nounwind }

diff  --git a/clang/test/CodeGenObjC/arc.m b/clang/test/CodeGenObjC/arc.m
index 31ecb53713be..560a08449578 100644
--- a/clang/test/CodeGenObjC/arc.m
+++ b/clang/test/CodeGenObjC/arc.m
@@ -1557,6 +1557,43 @@ void test71(void) {
   getAggDtor();
 }
 
+// Check that no extra release calls are emitted to detruct the compond literal.
+
+// CHECK: define void @test72(i8* %[[A:.*]], i8* %[[B:.*]])
+// CHECK: %[[A_ADDR:.*]] = alloca i8*, align 8
+// CHECK: %[[B_ADDR:.*]] = alloca i8*, align 8
+// CHECK: %[[T:.*]] = alloca [2 x i8*], align 16
+// CHECK: %[[V0:.*]] = call i8* @llvm.objc.retain(i8* %[[A]])
+// CHECK: %[[V1:.*]] = call i8* @llvm.objc.retain(i8* %[[B]]) #2
+// CHECK: %[[ARRAYINIT_BEGIN:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[T]], i64 0, i64 0
+// CHECK: %[[V3:.*]] = load i8*, i8** %[[A_ADDR]], align 8, !tbaa !7
+// CHECK: %[[V4:.*]] = call i8* @llvm.objc.retain(i8* %[[V3]]) #2
+// CHECK: store i8* %[[V4]], i8** %[[ARRAYINIT_BEGIN]], align 8, !tbaa !7
+// CHECK: %[[ARRAYINIT_ELEMENT:.*]] = getelementptr inbounds i8*, i8** %[[ARRAYINIT_BEGIN]], i64 1
+// CHECK: %[[V5:.*]] = load i8*, i8** %[[B_ADDR]], align 8, !tbaa !7
+// CHECK: %[[V6:.*]] = call i8* @llvm.objc.retain(i8* %[[V5]]) #2
+// CHECK: store i8* %[[V6]], i8** %[[ARRAYINIT_ELEMENT]], align 8, !tbaa !7
+// CHECK: %[[ARRAY_BEGIN:.*]] = getelementptr inbounds [2 x i8*], [2 x i8*]* %[[T]], i32 0, i32 0
+// CHECK: %[[V7:.*]] = getelementptr inbounds i8*, i8** %[[ARRAY_BEGIN]], i64 2
+
+// CHECK-NOT: call void @llvm.objc.release
+
+// CHECK: %[[ARRAYDESTROY_ELEMENTPAST:.*]] = phi i8** [ %[[V7]], %{{.*}} ], [ %[[ARRAYDESTROY_ELEMENT:.*]], %{{.*}} ]
+// CHECK: %[[ARRAYDESTROY_ELEMENT]] = getelementptr inbounds i8*, i8** %[[ARRAYDESTROY_ELEMENTPAST]], i64 -1
+// CHECK: %[[V8:.*]] = load i8*, i8** %[[ARRAYDESTROY_ELEMENT]], align 8
+// CHECK: call void @llvm.objc.release(i8* %[[V8]]) #2, !clang.imprecise_release !10
+
+// CHECK-NOT: call void @llvm.objc.release
+
+// CHECK: %[[V10:.*]] = load i8*, i8** %[[B_ADDR]], align 8
+// CHECK: call void @llvm.objc.release(i8* %[[V10]]) #2, !clang.imprecise_release !10
+// CHECK: %[[V11:.*]] = load i8*, i8** %[[A_ADDR]], align 8
+// CHECK: call void @llvm.objc.release(i8* %[[V11]]) #2, !clang.imprecise_release !10
+
+void test72(id a, id b) {
+  __strong id t[] = (__strong id[]){a, b};
+}
+
 // ARC-ALIEN: attributes [[NLB]] = { nonlazybind }
 // ARC-NATIVE: attributes [[NLB]] = { nonlazybind }
 // CHECK: attributes [[NUW]] = { nounwind }

diff  --git a/clang/test/CodeGenObjC/strong-in-c-struct.m b/clang/test/CodeGenObjC/strong-in-c-struct.m
index eae5013dd3fc..34bf0323695a 100644
--- a/clang/test/CodeGenObjC/strong-in-c-struct.m
+++ b/clang/test/CodeGenObjC/strong-in-c-struct.m
@@ -709,4 +709,103 @@ void test_copy_constructor_VolatileArray(VolatileArray *a) {
   VolatileArray t = *a;
 }
 
+// CHECK: define void @test_compound_literal0(
+// CHECK: %[[P:.*]] = alloca %[[STRUCT_STRONGSMALL]]*, align 8
+// CHECK: %[[_COMPOUNDLITERAL:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8
+// CHECK: %[[CLEANUP_COND:.*]] = alloca i1, align 1
+// CHECK: %[[_COMPOUNDLITERAL1:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8
+// CHECK: %[[CLEANUP_COND4:.*]] = alloca i1, align 1
+
+// CHECK: %[[I:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL]], i32 0, i32 0
+// CHECK: store i32 1, i32* %[[I]], align 8
+// CHECK: %[[F1:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL]], i32 0, i32 1
+// CHECK: store i8* null, i8** %[[F1]], align 8
+// CHECK: store i1 true, i1* %[[CLEANUP_COND]], align 1
+
+// CHECK: %[[I2:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL1]], i32 0, i32 0
+// CHECK: store i32 2, i32* %[[I2]], align 8
+// CHECK: %[[F13:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL1]], i32 0, i32 1
+// CHECK: store i8* null, i8** %[[F13]], align 8
+// CHECK: store i1 true, i1* %[[CLEANUP_COND4]], align 1
+
+// CHECK: %[[COND:.*]] = phi %[[STRUCT_STRONGSMALL]]* [ %[[_COMPOUNDLITERAL]], %{{.*}} ], [ %[[_COMPOUNDLITERAL1]], %{{.*}} ]
+// CHECK: store %[[STRUCT_STRONGSMALL]]* %[[COND]], %[[STRUCT_STRONGSMALL]]** %[[P]], align 8
+// CHECK: call void @func(
+
+// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL1]] to i8**
+// CHECK: call void @__destructor_8_s8(i8** %[[V1]])
+
+// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL]] to i8**
+// CHECK: call void @__destructor_8_s8(i8** %[[V2]])
+
+void test_compound_literal0(int c) {
+  StrongSmall *p = c ? &(StrongSmall){ 1, 0 } : &(StrongSmall){ 2, 0 };
+  func(0);
+}
+
+// Check that there is only one destructor call, which destructs 't'.
+
+// CHECK: define void @test_compound_literal1(
+// CHECK: %[[T:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8
+
+// CHECK: %[[I:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[T]], i32 0, i32 0
+// CHECK: store i32 1, i32* %[[I]], align 8
+// CHECK: %[[F1:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[T]], i32 0, i32 1
+// CHECK: store i8* null, i8** %[[F1]], align 8
+
+// CHECK: %[[I1:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[T]], i32 0, i32 0
+// CHECK: store i32 2, i32* %[[I1]], align 8
+// CHECK: %[[F12:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[T]], i32 0, i32 1
+// CHECK: store i8* null, i8** %[[F12]], align 8
+
+// CHECK: call void @func(
+// CHECK-NOT: call void
+// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[T]] to i8**
+// CHECK: call void @__destructor_8_s8(i8** %[[V1]])
+// CHECK-NOT: call void
+
+void test_compound_literal1(int c) {
+  StrongSmall t = c ? (StrongSmall){ 1, 0 } : (StrongSmall){ 2, 0 };
+  func(0);
+}
+
+// CHECK: define void @test_compound_literal2(
+// CHECK: %[[P_ADDR:.*]] = alloca %[[STRUCT_STRONGSMALL]]*, align 8
+// CHECK: %[[_COMPOUNDLITERAL:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8
+// CHECK: %[[CLEANUP_COND:.*]] = alloca i1, align 1
+// CHECK: %[[_COMPOUNDLITERAL1:.*]] = alloca %[[STRUCT_STRONGSMALL]], align 8
+// CHECK: %[[CLEANUP_COND4:.*]] = alloca i1, align 1
+// CHECK: %[[V0:.*]] = load %[[STRUCT_STRONGSMALL]]*, %[[STRUCT_STRONGSMALL]]** %[[P_ADDR]], align 8
+
+// CHECK: %[[I:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL]], i32 0, i32 0
+// CHECK: store i32 1, i32* %[[I]], align 8
+// CHECK: %[[F1:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL]], i32 0, i32 1
+// CHECK: store i8* null, i8** %[[F1]], align 8
+// CHECK: store i1 true, i1* %[[CLEANUP_COND]], align 1
+// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[V0]] to i8**
+// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL]] to i8**
+// CHECK: call void @__copy_assignment_8_8_t0w4_s8(i8** %[[V2]], i8** %[[V3]])
+
+// CHECK: %[[I2:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL1]], i32 0, i32 0
+// CHECK: store i32 2, i32* %[[I2]], align 8
+// CHECK: %[[F13:.*]] = getelementptr inbounds %[[STRUCT_STRONGSMALL]], %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL1]], i32 0, i32 1
+// CHECK: store i8* null, i8** %[[F13]], align 8
+// CHECK: store i1 true, i1* %[[CLEANUP_COND4]], align 1
+// CHECK: %[[V4:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[V0]] to i8**
+// CHECK: %[[V5:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL1]] to i8**
+// CHECK: call void @__copy_assignment_8_8_t0w4_s8(i8** %[[V4]], i8** %[[V5]])
+
+// CHECK: call void @func(
+
+// CHECK: %[[V6:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL1]] to i8**
+// CHECK: call void @__destructor_8_s8(i8** %[[V6]])
+
+// CHECK: %[[V7:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL]] to i8**
+// CHECK: call void @__destructor_8_s8(i8** %[[V7]])
+
+void test_compound_literal2(int c, StrongSmall *p) {
+  *p = c ? (StrongSmall){ 1, 0 } : (StrongSmall){ 2, 0 };
+  func(0);
+}
+
 #endif /* USESTRUCT */

diff  --git a/clang/test/Import/objc-arc/Inputs/cleanup-objects.m b/clang/test/Import/objc-arc/Inputs/cleanup-objects.m
new file mode 100644
index 000000000000..9c18399259f9
--- /dev/null
+++ b/clang/test/Import/objc-arc/Inputs/cleanup-objects.m
@@ -0,0 +1,10 @@
+typedef struct {
+  id x;
+} S;
+
+id getObj(int c, id a) {
+  // Commenting out the following line because AST importer crashes when trying
+  // to import a BlockExpr.
+  // return c ? ^{ return a; }() : ((S){ .x = a }).x;
+  return ((S){ .x = a }).x;
+}

diff  --git a/clang/test/Import/objc-arc/test-cleanup-object.m b/clang/test/Import/objc-arc/test-cleanup-object.m
new file mode 100644
index 000000000000..aab1cd377a2e
--- /dev/null
+++ b/clang/test/Import/objc-arc/test-cleanup-object.m
@@ -0,0 +1,10 @@
+// RUN: clang-import-test -x objective-c -objc-arc -import %S/Inputs/cleanup-objects.m -dump-ast -expression %s | FileCheck %s
+
+// CHECK: FunctionDecl {{.*}} getObj '
+// CHECK: ExprWithCleanups
+// CHECK-NEXT: cleanup CompoundLiteralExpr
+
+void test(int c, id a) {
+  (void)getObj(c, a);
+}
+

diff  --git a/clang/test/PCH/non-trivial-c-compound-literal.m b/clang/test/PCH/non-trivial-c-compound-literal.m
new file mode 100644
index 000000000000..d4e3c1f12df2
--- /dev/null
+++ b/clang/test/PCH/non-trivial-c-compound-literal.m
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -x objective-c -fobjc-arc -emit-pch -o %t %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -x objective-c -fobjc-arc -include-pch %t -emit-llvm -o - %s | FileCheck %s
+
+#ifndef HEADER
+#define HEADER
+
+typedef struct {
+  id f;
+} S;
+
+static inline id getObj(id a) {
+  S *p = &(S){ .f = a };
+  return p->f;
+}
+
+#else
+
+// CHECK: %[[STRUCT_S:.*]] = type { i8* }
+
+// CHECK: define internal i8* @getObj(
+// CHECK: %[[_COMPOUNDLITERAL:.*]] = alloca %[[STRUCT_S]],
+// CHECK: %[[V5:.*]] = bitcast %[[STRUCT_S]]* %[[_COMPOUNDLITERAL]] to i8**
+// CHECK: call void @__destructor_8_s0(i8** %[[V5]])
+
+id test(id a) {
+  return getObj(a);
+}
+
+#endif

diff  --git a/clang/test/SemaObjC/strong-in-c-struct.m b/clang/test/SemaObjC/strong-in-c-struct.m
index 5dd5f94af830..5d2f4e496c2e 100644
--- a/clang/test/SemaObjC/strong-in-c-struct.m
+++ b/clang/test/SemaObjC/strong-in-c-struct.m
@@ -54,3 +54,21 @@ void test_block_scope1(void) {
   func(^{ func2(x); });
   goto *ips; // expected-error {{cannot jump}}
 }
+
+void test_compound_literal0(int cond, id x) {
+  switch (cond) {
+  case 0:
+    (void)(Strong){ .a = x }; // expected-note {{jump enters lifetime of a compound literal that is non-trivial to destruct}}
+    break;
+  default: // expected-error {{cannot jump from switch statement to this case label}}
+    break;
+  }
+}
+
+void test_compound_literal1(id x) {
+  static void *ips[] = { &&L0 };
+L0:  // expected-note {{possible target of indirect goto}}
+  ;
+  (void)(Strong){ .a = x }; // expected-note {{jump exits lifetime of a compound literal that is non-trivial to destruct}}
+  goto *ips; // expected-error {{cannot jump}}
+}

diff  --git a/clang/tools/clang-import-test/clang-import-test.cpp b/clang/tools/clang-import-test/clang-import-test.cpp
index b42c93e8d381..f76f850ff41f 100644
--- a/clang/tools/clang-import-test/clang-import-test.cpp
+++ b/clang/tools/clang-import-test/clang-import-test.cpp
@@ -64,6 +64,9 @@ static llvm::cl::opt<std::string>
           llvm::cl::desc("The language to parse (default: c++)"),
           llvm::cl::init("c++"));
 
+static llvm::cl::opt<bool> ObjCARC("objc-arc", llvm::cl::init(false),
+                                   llvm::cl::desc("Emable ObjC ARC"));
+
 static llvm::cl::opt<bool> DumpAST("dump-ast", llvm::cl::init(false),
                                    llvm::cl::desc("Dump combined AST"));
 
@@ -183,6 +186,8 @@ std::unique_ptr<CompilerInstance> BuildCompilerInstance() {
       Inv->getLangOpts()->ObjC = 1;
     }
   }
+  Inv->getLangOpts()->ObjCAutoRefCount = ObjCARC;
+
   Inv->getLangOpts()->Bool = true;
   Inv->getLangOpts()->WChar = true;
   Inv->getLangOpts()->Blocks = true;


        


More information about the cfe-commits mailing list