ahatanak updated this revision to Diff 212942.
ahatanak marked 2 inline comments as done.
ahatanak added a comment.
Herald added a reviewer: martong.
Herald added a reviewer: shafik.

- Emit member access, compound literal, and call expressions as subexpressions 
of `ExprWithCleanups` if the expressions are of C struct types that require 
non-trivial destruction. This fixes the bug in IRGen where it was destructing 
the function return at the end of the enclosing scope rather than at the end of 
the full expression (see the changes made in test/CodeGenObjC/arc.m).
- Add compound literal expressions with automatic storage duration to the list 
of cleanup objects of `ExprWithCleanups` if the expressions have C struct types 
requiring non-trivial destruction. This enables IRGen to destruct the compound 
literals at the end of their enclosing scopes.


Repository:
  rC Clang

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D64464/new/

https://reviews.llvm.org/D64464

Files:
  include/clang/AST/ASTImporter.h
  include/clang/AST/ExprCXX.h
  include/clang/AST/TextNodeDumper.h
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  include/clang/Serialization/ASTBitCodes.h
  lib/AST/ASTImporter.cpp
  lib/AST/JSONNodeDumper.cpp
  lib/AST/TextNodeDumper.cpp
  lib/CodeGen/CGBlocks.cpp
  lib/CodeGen/CGCleanup.cpp
  lib/CodeGen/CGDecl.cpp
  lib/CodeGen/CGExpr.cpp
  lib/CodeGen/CGExprAgg.cpp
  lib/CodeGen/CodeGenFunction.cpp
  lib/CodeGen/CodeGenFunction.h
  lib/Sema/JumpDiagnostics.cpp
  lib/Sema/SemaExpr.cpp
  lib/Serialization/ASTReaderStmt.cpp
  lib/Serialization/ASTWriterStmt.cpp
  test/AST/ast-dump-objc-arc-json.m
  test/AST/ast-dump-stmt.m
  test/CodeGenObjC/arc.m
  test/CodeGenObjC/strong-in-c-struct.m
  test/Import/objc-arc/Inputs/cleanup-objects.m
  test/Import/objc-arc/test-cleanup-object.m
  test/PCH/non-trivial-c-compound-literal.m
  test/SemaObjC/strong-in-c-struct.m
  tools/clang-import-test/clang-import-test.cpp

Index: tools/clang-import-test/clang-import-test.cpp
===================================================================
--- tools/clang-import-test/clang-import-test.cpp
+++ tools/clang-import-test/clang-import-test.cpp
@@ -64,6 +64,10 @@
           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"));
 
@@ -185,6 +189,8 @@
       Inv->getLangOpts()->ObjC = 1;
     }
   }
+  Inv->getLangOpts()->ObjCAutoRefCount = ObjCARC;
+
   Inv->getLangOpts()->Bool = true;
   Inv->getLangOpts()->WChar = true;
   Inv->getLangOpts()->Blocks = true;
Index: test/SemaObjC/strong-in-c-struct.m
===================================================================
--- test/SemaObjC/strong-in-c-struct.m
+++ test/SemaObjC/strong-in-c-struct.m
@@ -54,3 +54,21 @@
   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}}
+}
Index: test/PCH/non-trivial-c-compound-literal.m
===================================================================
--- /dev/null
+++ 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
Index: test/Import/objc-arc/test-cleanup-object.m
===================================================================
--- /dev/null
+++ 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);
+}
+
Index: test/Import/objc-arc/Inputs/cleanup-objects.m
===================================================================
--- /dev/null
+++ 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;
+}
Index: test/CodeGenObjC/strong-in-c-struct.m
===================================================================
--- test/CodeGenObjC/strong-in-c-struct.m
+++ test/CodeGenObjC/strong-in-c-struct.m
@@ -521,7 +521,9 @@
 
 // CHECK: define void @test_copy_constructor_StrongVolatile0(
 // CHECK: call void @__copy_constructor_8_8_t0w4_sv8(
+// CHECK-NOT: call
 // CHECK: call void @__destructor_8_sv8(
+// CHECK-NOT: call
 
 // CHECK: define linkonce_odr hidden void @__copy_constructor_8_8_t0w4_sv8(
 // CHECK: %[[V8:.*]] = load volatile i8*, i8** %{{.*}}, align 8
@@ -718,4 +720,89 @@
   VolatileArray t = *a;
 }
 
+// CHECK: define void @test_member_access(
+// CHECK: %[[TMP:.*]] = alloca %[[STRUCT_STRONGSMALL]],
+// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[TMP]] to i8**
+// CHECK: call void @__destructor_8_s8(i8** %[[V3]])
+// CHECK: call void @func(
+
+void test_member_access(void) {
+  id t = getStrongSmall().f1;
+  func(0);
+}
+
+// CHECK: define void @test_compound_literal(
+// CHECK: %[[_COMPOUNDLITERAL:.*]] = alloca %[[STRUCT_STRONGSMALL]],
+// CHECK: %[[CLEANUP_ISACTIVE:.*]] = alloca i1,
+// CHECK: %[[_COMPOUNDLITERAL1:.*]] = alloca %[[STRUCT_STRONGSMALL]],
+// CHECK: %[[CLEANUP_ISACTIVE4:.*]] = alloca i1,
+// CHECK: store i1 false, i1* %[[CLEANUP_ISACTIVE]],
+// CHECK: store i1 false, i1* %[[CLEANUP_ISACTIVE4]],
+
+// CHECK: store i1 true, i1* %[[CLEANUP_ISACTIVE]],
+
+// CHECK: store i1 true, i1* %[[CLEANUP_ISACTIVE4]],
+
+// CHECK: call void @func(
+// CHECK: %[[IS_ACTIVE4:.*]] = load i1, i1* %[[CLEANUP_ISACTIVE4]],
+// CHECK: br i1 %[[IS_ACTIVE4]],
+
+// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL1]] to i8**
+// CHECK: call void @__destructor_8_s8(i8** %[[V1]])
+
+// CHECK: %[[IS_ACTIVE:.*]] = load i1, i1* %[[CLEANUP_ISACTIVE]],
+// CHECK: br i1 %[[IS_ACTIVE]],
+
+// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[_COMPOUNDLITERAL]] to i8**
+// CHECK: call void @__destructor_8_s8(i8** %[[V2]])
+
+void test_compound_literal(int c) {
+  StrongSmall *p = c ? &(StrongSmall){ 1, 0 } : &(StrongSmall){ 2, 0 };
+  func(0);
+}
+
+// CHECK: define void @test_compound_literal2(
+// CHECK: %[[AGG_TMP_ENSURED:.*]] = alloca %[[STRUCT_STRONGSMALL]],
+// CHECK: %[[CLEANUP_ISACTIVE:.*]] = alloca i1,
+// CHECK: %[[AGG_TMP_ENSURED1:.*]] = alloca %[[STRUCT_STRONGSMALL]],
+// CHECK: %[[CLEANUP_ISACTIVE4:.*]] = alloca i1,
+// CHECK: store i1 false, i1* %[[CLEANUP_ISACTIVE]],
+// CHECK: store i1 false, i1* %[[CLEANUP_ISACTIVE4]],
+
+// CHECK: store i1 true, i1* %[[CLEANUP_ISACTIVE]],
+
+// CHECK: store i1 true, i1* %[[CLEANUP_ISACTIVE4]],
+
+// CHECK: call void @func(
+// CHECK: %[[IS_ACTIVE4:.*]] = load i1, i1* %[[CLEANUP_ISACTIVE4]],
+// CHECK: br i1 %[[IS_ACTIVE4]],
+
+// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[AGG_TMP_ENSURED1]] to i8**
+// CHECK: call void @__destructor_8_s8(i8** %[[V1]])
+
+// CHECK: %[[IS_ACTIVE:.*]] = load i1, i1* %[[CLEANUP_ISACTIVE]],
+// CHECK: br i1 %[[IS_ACTIVE]],
+
+// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[AGG_TMP_ENSURED]] to i8**
+// CHECK: call void @__destructor_8_s8(i8** %[[V2]])
+
+void test_compound_literal2(int c) {
+  c ? (StrongSmall){ 1, 0 } : (StrongSmall){ 2, 0 };
+  func(0);
+}
+
+// CHECK: define void @test_volatile_variable_reference(
+// CHECK: %[[AGG_TMP_ENSURED:.*]] = alloca %[[STRUCT_STRONGSMALL]],
+// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[AGG_TMP_ENSURED]] to i8**
+// CHECK: %[[V2:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %{{.*}} to i8**
+// CHECK: call void @__copy_constructor_8_8_tv0w32_sv8(i8** %[[V1]], i8** %[[V2]])
+// CHECK: %[[V3:.*]] = bitcast %[[STRUCT_STRONGSMALL]]* %[[AGG_TMP_ENSURED]] to i8**
+// CHECK: call void @__destructor_8_s8(i8** %[[V3]])
+// CHECK: call void @func(
+
+void test_volatile_variable_reference(volatile StrongSmall *a) {
+  (void)*a;
+  func(0);
+}
+
 #endif /* USESTRUCT */
Index: test/CodeGenObjC/arc.m
===================================================================
--- test/CodeGenObjC/arc.m
+++ test/CodeGenObjC/arc.m
@@ -1536,12 +1536,13 @@
 
 // CHECK-LABEL: define void @test71
 void test71(void) {
-  // FIXME: It would be nice if the __destructor_8_s40 for the first call (and
-  // the following lifetime.end) came before the second call.
-  //
   // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP1:[^ ]+]] to i8*
   // CHECK: call void @llvm.lifetime.start.p0i8({{[^,]+}}, i8* %[[T]])
   // CHECK: call void @getAggDtor(%struct.AggDtor* sret %[[TMP1]])
+  // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP1]] to i8**
+  // CHECK: call void @__destructor_8_s40(i8** %[[T]])
+  // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP1:[^ ]+]] to i8*
+  // CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* %[[T]])
   // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP2:[^ ]+]] to i8*
   // CHECK: call void @llvm.lifetime.start.p0i8({{[^,]+}}, i8* %[[T]])
   // CHECK: call void @getAggDtor(%struct.AggDtor* sret %[[TMP2]])
@@ -1549,10 +1550,6 @@
   // CHECK: call void @__destructor_8_s40(i8** %[[T]])
   // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP2:[^ ]+]] to i8*
   // CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* %[[T]])
-  // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP1]] to i8**
-  // CHECK: call void @__destructor_8_s40(i8** %[[T]])
-  // CHECK: %[[T:[^ ]+]] = bitcast %struct.AggDtor* %[[TMP1:[^ ]+]] to i8*
-  // CHECK: call void @llvm.lifetime.end.p0i8({{[^,]+}}, i8* %[[T]])
   getAggDtor();
   getAggDtor();
 }
Index: test/AST/ast-dump-stmt.m
===================================================================
--- test/AST/ast-dump-stmt.m
+++ 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 @@
 // 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
Index: test/AST/ast-dump-objc-arc-json.m
===================================================================
--- /dev/null
+++ test/AST/ast-dump-objc-arc-json.m
@@ -0,0 +1,34 @@
+// 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:    "col": 10,
+// CHECK-NEXT:    "tokLen": 1
+// CHECK-NEXT:   },
+// CHECK-NEXT:   "end": {
+// 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":
+// 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:  ],
Index: lib/Serialization/ASTWriterStmt.cpp
===================================================================
--- lib/Serialization/ASTWriterStmt.cpp
+++ lib/Serialization/ASTWriterStmt.cpp
@@ -1598,8 +1598,15 @@
 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());
Index: lib/Serialization/ASTReaderStmt.cpp
===================================================================
--- lib/Serialization/ASTReaderStmt.cpp
+++ lib/Serialization/ASTReaderStmt.cpp
@@ -1655,9 +1655,17 @@
 
   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();
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -635,6 +635,10 @@
   if (E->getType().getObjCLifetime() == Qualifiers::OCL_Weak)
     Cleanup.setExprNeedsCleanups(true);
 
+  if (E->getType().isVolatileQualified() &&
+      E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct)
+    Cleanup.setExprNeedsCleanups(true);
+
   // C++ [conv.lval]p3:
   //   If T is cv std::nullptr_t, the result is a null pointer constant.
   CastKind CK = T->isNullPtrType() ? CK_NullToPointer : CK_LValueToRValue;
@@ -5569,6 +5573,10 @@
     }
   }
 
+  if (Call.get()->getType().isDestructedType() ==
+      QualType::DK_nontrivial_c_struct)
+    Cleanup.setExprNeedsCleanups(true);
+
   return Call;
 }
 
@@ -6076,7 +6084,7 @@
         ILE->setInit(i, ConstantExpr::Create(Context, Init));
       }
 
-  Expr *E = new (Context) CompoundLiteralExpr(LParenLoc, TInfo, literalType,
+  auto *E = new (Context) CompoundLiteralExpr(LParenLoc, TInfo, literalType,
                                               VK, LiteralExpr, isFileScope);
   if (isFileScope) {
     if (!LiteralExpr->isTypeDependent() &&
@@ -6094,6 +6102,13 @@
     return ExprError();
   }
 
+  if (!isFileScope &&
+      literalType.isDestructedType() == QualType::DK_nontrivial_c_struct) {
+    Cleanup.setExprNeedsCleanups(true);
+    ExprCleanupObjects.push_back(E);
+    getCurFunction()->setHasBranchProtectedScope();
+  }
+
   return MaybeBindToTemporary(E);
 }
 
Index: lib/Sema/JumpDiagnostics.cpp
===================================================================
--- lib/Sema/JumpDiagnostics.cpp
+++ lib/Sema/JumpDiagnostics.cpp
@@ -75,6 +75,7 @@
   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 @@
   }
 }
 
+/// 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 @@
     // 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;
   }
Index: lib/CodeGen/CodeGenFunction.h
===================================================================
--- lib/CodeGen/CodeGenFunction.h
+++ lib/CodeGen/CodeGenFunction.h
@@ -342,6 +342,10 @@
   /// we prefer to insert allocas.
   llvm::AssertingVH<llvm::Instruction> AllocaInsertPt;
 
+  /// List of dominating IPs. These instructions are deleted after IR generation
+  /// of a function is completed.
+  SmallVector<llvm::Instruction *, 1> DominatingIPs;
+
   /// API for captured statement code generation.
   class CGCapturedStmtInfo {
   public:
@@ -559,6 +563,31 @@
 
   llvm::BasicBlock *getInvokeDestImpl();
 
+  /// This struct has the information that is needed to destruct a compound
+  /// literal with automatic storage duration that has a C struct type that is
+  /// non-trivial to destruct.
+  struct CompoundLiteralCleanupInfo {
+    EHScopeStack::stable_iterator Cleanup;
+    llvm::Instruction *DominatingIP;
+    llvm::Value *AddrPtr;
+    CharUnits AddrAlignment;
+
+    void setCleanup(EHScopeStack::stable_iterator C, llvm::Instruction *D) {
+      Cleanup = C;
+      DominatingIP = D;
+    }
+    void setAddr(Address Addr) {
+      AddrPtr = Addr.getPointer();
+      AddrAlignment = Addr.getAlignment();
+    }
+    Address getAddr() const {
+      return Address(AddrPtr, AddrAlignment);
+    }
+  };
+
+  llvm::DenseMap<const CompoundLiteralExpr *, CompoundLiteralCleanupInfo>
+      CompoundLiteralCleanupInfoMap;
+
   template <class T>
   typename DominatingValue<T>::saved_type saveValueInCond(T value) {
     return DominatingValue<T>::save(*this, value);
@@ -1685,6 +1714,10 @@
                      Address addr, QualType type);
   void pushDestroy(CleanupKind kind, Address addr, QualType type,
                    Destroyer *destroyer, bool useEHCleanupForArray);
+  void pushCompoundLiteralCleanup(CleanupKind kind,
+                                  const CompoundLiteralExpr *CLE,
+                                  Destroyer *destroyer,
+                                  bool useEHCleanupForArray);
   void pushLifetimeExtendedDestroy(CleanupKind kind, Address addr,
                                    QualType type, Destroyer *destroyer,
                                    bool useEHCleanupForArray);
@@ -3997,6 +4030,9 @@
   }
   void enterNonTrivialFullExpression(const FullExpr *E);
 
+  void enterCompoundLiteralScope(const CompoundLiteralExpr *CLE,
+                                 llvm::Instruction *DomInst);
+
   void EmitCXXThrowExpr(const CXXThrowExpr *E, bool KeepInsertionPoint = true);
 
   RValue EmitAtomicExpr(AtomicExpr *E);
Index: lib/CodeGen/CodeGenFunction.cpp
===================================================================
--- lib/CodeGen/CodeGenFunction.cpp
+++ lib/CodeGen/CodeGenFunction.cpp
@@ -389,6 +389,9 @@
   AllocaInsertPt = nullptr;
   Ptr->eraseFromParent();
 
+  for (auto I : DominatingIPs)
+    I->eraseFromParent();
+
   // If someone took the address of a label but never did an indirect goto, we
   // made a zero entry PHI node, which is illegal, zap it now.
   if (IndirectBranch) {
Index: lib/CodeGen/CGExprAgg.cpp
===================================================================
--- lib/CodeGen/CGExprAgg.cpp
+++ lib/CodeGen/CGExprAgg.cpp
@@ -245,7 +245,7 @@
     const Expr *E, llvm::function_ref<RValue(ReturnValueSlot)> EmitCall) {
   QualType RetTy = E->getType();
   bool RequiresDestruction =
-      Dest.isIgnored() &&
+      !Dest.isExternallyDestructed() &&
       RetTy.isDestructedType() == QualType::DK_nontrivial_c_struct;
 
   // If it makes no observable difference, save a memcpy + temporary.
@@ -657,6 +657,18 @@
 
   AggValueSlot Slot = EnsureSlot(E->getType());
   CGF.EmitAggExpr(E->getInitializer(), Slot);
+
+  // If the compound literal has a non-trivial C struct type, activate the
+  // cleanup that was pushed at the entry of the full expression and record the
+  // address of the aggregate.
+  if (!Slot.isExternallyDestructed() &&
+      E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct) {
+    CodeGenFunction::CompoundLiteralCleanupInfo &Info =
+        CGF.CompoundLiteralCleanupInfoMap[E];
+    CGF.ActivateCleanupBlock(Info.Cleanup, Info.DominatingIP);
+    Info.setAddr(Slot.getAddress());
+    Slot.setExternallyDestructed();
+  }
 }
 
 /// Attempt to look through various unimportant expressions to find a
@@ -811,7 +823,16 @@
     // into existence.
     if (E->getSubExpr()->getType().isVolatileQualified()) {
       EnsureDest(E->getType());
-      return Visit(E->getSubExpr());
+      Visit(E->getSubExpr());
+
+      if (!Dest.isExternallyDestructed() &&
+          E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct) {
+        CGF.pushDestroy(QualType::DK_nontrivial_c_struct, Dest.getAddress(),
+                        E->getType());
+        Dest.setExternallyDestructed();
+      }
+
+      return;
     }
 
     LLVM_FALLTHROUGH;
Index: lib/CodeGen/CGExpr.cpp
===================================================================
--- lib/CodeGen/CGExpr.cpp
+++ lib/CodeGen/CGExpr.cpp
@@ -4151,6 +4151,15 @@
   EmitAnyExprToMem(InitExpr, DeclPtr, E->getType().getQualifiers(),
                    /*Init*/ true);
 
+  // If the compound literal has a non-trivial C struct type, activate the
+  // cleanup that was pushed at the entry of the full expression and record the
+  // address of the aggregate.
+  if (E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct) {
+    CompoundLiteralCleanupInfo &Info = CompoundLiteralCleanupInfoMap[E];
+    ActivateCleanupBlock(Info.Cleanup, Info.DominatingIP);
+    Info.setAddr(DeclPtr);
+  }
+
   return Result;
 }
 
@@ -4633,6 +4642,10 @@
 LValue CodeGenFunction::EmitCallExprLValue(const CallExpr *E) {
   RValue RV = EmitCallExpr(E);
 
+  if (E->getType().isDestructedType() == QualType::DK_nontrivial_c_struct)
+    pushDestroy(QualType::DK_nontrivial_c_struct, RV.getAggregateAddress(),
+                E->getType());
+
   if (!RV.isScalar())
     return MakeAddrLValue(RV.getAggregateAddress(), E->getType(),
                           AlignmentSource::Decl);
Index: lib/CodeGen/CGDecl.cpp
===================================================================
--- lib/CodeGen/CGDecl.cpp
+++ lib/CodeGen/CGDecl.cpp
@@ -536,6 +536,28 @@
     }
   };
 
+  /// A cleanup for destructing compound literals that have automatic storage
+  /// duration and are of C struct types that are non-trivial to destruct.
+  struct DestroyCompoundLiteral final : EHScopeStack::Cleanup {
+    DestroyCompoundLiteral(const CompoundLiteralExpr *CLE,
+                           CodeGenFunction::Destroyer *destroyer,
+                           bool useEHCleanupForArray)
+      : CLE(CLE), destroyer(destroyer),
+        useEHCleanupForArray(useEHCleanupForArray) {}
+
+    const CompoundLiteralExpr *CLE;
+    CodeGenFunction::Destroyer *destroyer;
+    bool useEHCleanupForArray;
+
+    void Emit(CodeGenFunction &CGF, Flags flags) override {
+      // Don't use an EH cleanup recursively from an EH cleanup.
+      bool useEHCleanupForArray =
+          flags.isForNormalCleanup() && this->useEHCleanupForArray;
+      CGF.emitDestroy(CGF.CompoundLiteralCleanupInfoMap[CLE].getAddr(),
+                      CLE->getType(), destroyer, useEHCleanupForArray);
+    }
+  };
+
   struct CallStackRestore final : EHScopeStack::Cleanup {
     Address Stack;
     CallStackRestore(Address Stack) : Stack(Stack) {}
@@ -2072,6 +2094,14 @@
                                      destroyer, useEHCleanupForArray);
 }
 
+void CodeGenFunction::pushCompoundLiteralCleanup(CleanupKind cleanupKind,
+                                                 const CompoundLiteralExpr *CLE,
+                                                 Destroyer *destroyer,
+                                                 bool useEHCleanupForArray) {
+  pushFullExprCleanup<DestroyCompoundLiteral>(cleanupKind, CLE, destroyer,
+                                              useEHCleanupForArray);
+}
+
 void CodeGenFunction::pushStackRestore(CleanupKind Kind, Address SPMem) {
   EHStack.pushCleanup<CallStackRestore>(Kind, SPMem);
 }
Index: lib/CodeGen/CGCleanup.cpp
===================================================================
--- lib/CodeGen/CGCleanup.cpp
+++ lib/CodeGen/CGCleanup.cpp
@@ -1275,3 +1275,21 @@
   pushDestroy(NormalAndEHCleanup, Ptr, TempType, destroyCXXObject,
               /*useEHCleanup*/ true);
 }
+
+void CodeGenFunction::enterCompoundLiteralScope(const CompoundLiteralExpr *CLE,
+                                                llvm::Instruction *DomInst) {
+  // The lifetime of a compound literal with automatic storage duration ends at
+  // the end of its enclosing block. Push an inactive cleanup here so that its
+  // destructor is called when the lifetime ends.
+  QualType Ty = CLE->getType();
+  QualType::DestructionKind DtorKind = Ty.isDestructedType();
+  assert(DtorKind == QualType::DK_nontrivial_c_struct &&
+         "non-trivial C struct type expected here");
+  bool UseArrayEHCleanup = needsEHCleanup(DtorKind);
+  CleanupKind CK = UseArrayEHCleanup ?
+      InactiveNormalAndEHCleanup : InactiveNormalCleanup;
+  pushCompoundLiteralCleanup(CK, CLE, getDestroyer(DtorKind),
+                             UseArrayEHCleanup);
+  CompoundLiteralCleanupInfoMap[CLE].setCleanup(
+      EHStack.stable_begin(), DomInst);
+}
Index: lib/CodeGen/CGBlocks.cpp
===================================================================
--- lib/CodeGen/CGBlocks.cpp
+++ lib/CodeGen/CGBlocks.cpp
@@ -863,9 +863,19 @@
 /// kind of cleanup object is a BlockDecl*.
 void CodeGenFunction::enterNonTrivialFullExpression(const FullExpr *E) {
   if (const auto EWC = dyn_cast<ExprWithCleanups>(E)) {
+    auto *DomInst = Builder.CreateAlignedLoad(
+        Int8Ty, llvm::Constant::getNullValue(Int8PtrTy),
+        CharUnits::One());
+    DominatingIPs.push_back(DomInst);
+
     assert(EWC->getNumObjects() != 0);
     for (const ExprWithCleanups::CleanupObject &C : EWC->getObjects())
-      enterBlockScope(*this, C);
+      if (auto *BD = C.dyn_cast<BlockDecl *>())
+        enterBlockScope(*this, BD);
+      else if (auto *CLE = C.dyn_cast<CompoundLiteralExpr *>())
+        enterCompoundLiteralScope(CLE, DomInst);
+      else
+        llvm_unreachable("unexpected cleanup object type");
   }
 }
 
Index: lib/AST/TextNodeDumper.cpp
===================================================================
--- lib/AST/TextNodeDumper.cpp
+++ lib/AST/TextNodeDumper.cpp
@@ -448,6 +448,23 @@
   }
 }
 
+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;
@@ -947,7 +964,7 @@
 
 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) {
Index: lib/AST/JSONNodeDumper.cpp
===================================================================
--- lib/AST/JSONNodeDumper.cpp
+++ lib/AST/JSONNodeDumper.cpp
@@ -1288,7 +1288,16 @@
   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");
+        }
     });
   }
 }
Index: lib/AST/ASTImporter.cpp
===================================================================
--- lib/AST/ASTImporter.cpp
+++ lib/AST/ASTImporter.cpp
@@ -7852,6 +7852,19 @@
   MapImported(FromD, ToD);
 }
 
+llvm::Expected<ExprWithCleanups::CleanupObject>
+ASTImporter::Import(ExprWithCleanups::CleanupObject From) {
+  if (auto *BD = From.dyn_cast<BlockDecl *>()) {
+    if (Expected<Decl *> R = Import(BD))
+      return ExprWithCleanups::CleanupObject(cast<BlockDecl>(*R));
+  } else if (auto *CLE = From.dyn_cast<CompoundLiteralExpr *>()) {
+    if (Expected<Expr *> R = Import(CLE))
+      return ExprWithCleanups::CleanupObject(cast<CompoundLiteralExpr>(*R));
+  }
+
+  return nullptr;
+}
+
 Expected<QualType> ASTImporter::Import(QualType FromT) {
   if (FromT.isNull())
     return QualType{};
Index: include/clang/Serialization/ASTBitCodes.h
===================================================================
--- include/clang/Serialization/ASTBitCodes.h
+++ include/clang/Serialization/ASTBitCodes.h
@@ -2014,6 +2014,12 @@
       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
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -585,7 +585,7 @@
   /// 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;
+  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
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -5167,6 +5167,8 @@
 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))">;
@@ -5210,6 +5212,8 @@
 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">,
Index: include/clang/AST/TextNodeDumper.h
===================================================================
--- include/clang/AST/TextNodeDumper.h
+++ include/clang/AST/TextNodeDumper.h
@@ -186,6 +186,7 @@
   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 = {});
 
Index: include/clang/AST/ExprCXX.h
===================================================================
--- include/clang/AST/ExprCXX.h
+++ include/clang/AST/ExprCXX.h
@@ -3211,13 +3211,14 @@
 /// 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 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;
Index: include/clang/AST/ASTImporter.h
===================================================================
--- include/clang/AST/ASTImporter.h
+++ 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"
@@ -339,6 +340,10 @@
       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).
     ///
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to