]> granicus.if.org Git - clang/commitdiff
Fix cleanup registration for lambda captures.
authorRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 17 Jan 2019 22:05:50 +0000 (22:05 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 17 Jan 2019 22:05:50 +0000 (22:05 +0000)
Lambda captures should be destroyed if an exception is thrown only if
the construction of the complete lambda-expression has not completed.
(If the lambda-expression has been fully constructed, any exception will
invoke its destructor, which will destroy the captures.)

This is directly modeled after how we handle the equivalent situation in
InitListExprs.

Note that EmitLambdaLValue was unreachable because in C++11 onwards the
frontend never creates the awkward situation where a prvalue expression
(such as a lambda) is used in an lvalue context (such as the left-hand
side of a class member access).

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@351487 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/CGExpr.cpp
lib/CodeGen/CGExprAgg.cpp
lib/CodeGen/CGExprCXX.cpp
lib/CodeGen/CodeGenFunction.h
test/CodeGenCXX/cxx1y-init-captures-eh.cpp [new file with mode: 0644]

index 34a921e2dc002d4efa85b8ee6b146bb9cd7e44ae..f986bc4c4cf81720e16656ed346a3eaf931eb5a1 100644 (file)
@@ -1287,8 +1287,6 @@ LValue CodeGenFunction::EmitLValue(const Expr *E) {
     return EmitCXXBindTemporaryLValue(cast<CXXBindTemporaryExpr>(E));
   case Expr::CXXUuidofExprClass:
     return EmitCXXUuidofLValue(cast<CXXUuidofExpr>(E));
-  case Expr::LambdaExprClass:
-    return EmitLambdaLValue(cast<LambdaExpr>(E));
 
   case Expr::ExprWithCleanupsClass: {
     const auto *cleanups = cast<ExprWithCleanups>(E);
@@ -4548,13 +4546,6 @@ CodeGenFunction::EmitCXXBindTemporaryLValue(const CXXBindTemporaryExpr *E) {
   return MakeAddrLValue(Slot.getAddress(), E->getType(), AlignmentSource::Decl);
 }
 
-LValue
-CodeGenFunction::EmitLambdaLValue(const LambdaExpr *E) {
-  AggValueSlot Slot = CreateAggTemp(E->getType(), "temp.lvalue");
-  EmitLambdaExpr(E, Slot);
-  return MakeAddrLValue(Slot.getAddress(), E->getType(), AlignmentSource::Decl);
-}
-
 LValue CodeGenFunction::EmitObjCMessageExprLValue(const ObjCMessageExpr *E) {
   RValue RV = EmitObjCMessageExpr(E);
 
index db49b3f28a59127a9a531e5d7d28a90fa57cb9d2..be6e938c308199763bc3b7ec0b1e8868c9c965e6 100644 (file)
@@ -1264,7 +1264,52 @@ void AggExprEmitter::VisitCXXInheritedCtorInitExpr(
 void
 AggExprEmitter::VisitLambdaExpr(LambdaExpr *E) {
   AggValueSlot Slot = EnsureSlot(E->getType());
-  CGF.EmitLambdaExpr(E, Slot);
+  LValue SlotLV = CGF.MakeAddrLValue(Slot.getAddress(), E->getType());
+
+  // We'll need to enter cleanup scopes in case any of the element
+  // initializers throws an exception.
+  SmallVector<EHScopeStack::stable_iterator, 16> Cleanups;
+  llvm::Instruction *CleanupDominator = nullptr;
+
+  CXXRecordDecl::field_iterator CurField = E->getLambdaClass()->field_begin();
+  for (LambdaExpr::const_capture_init_iterator i = E->capture_init_begin(),
+                                               e = E->capture_init_end();
+       i != e; ++i, ++CurField) {
+    // Emit initialization
+    LValue LV = CGF.EmitLValueForFieldInitialization(SlotLV, *CurField);
+    if (CurField->hasCapturedVLAType()) {
+      CGF.EmitLambdaVLACapture(CurField->getCapturedVLAType(), LV);
+      continue;
+    }
+
+    EmitInitializationToLValue(*i, LV);
+
+    // Push a destructor if necessary.
+    if (QualType::DestructionKind DtorKind =
+            CurField->getType().isDestructedType()) {
+      assert(LV.isSimple());
+      if (CGF.needsEHCleanup(DtorKind)) {
+        if (!CleanupDominator)
+          CleanupDominator = CGF.Builder.CreateAlignedLoad(
+              CGF.Int8Ty,
+              llvm::Constant::getNullValue(CGF.Int8PtrTy),
+              CharUnits::One()); // placeholder
+
+        CGF.pushDestroy(EHCleanup, LV.getAddress(), CurField->getType(),
+                        CGF.getDestroyer(DtorKind), false);
+        Cleanups.push_back(CGF.EHStack.stable_begin());
+      }
+    }
+  }
+
+  // Deactivate all the partial cleanups in reverse order, which
+  // generally means popping them.
+  for (unsigned i = Cleanups.size(); i != 0; --i)
+    CGF.DeactivateCleanupBlock(Cleanups[i-1], CleanupDominator);
+
+  // Destroy the placeholder if we made one.
+  if (CleanupDominator)
+    CleanupDominator->eraseFromParent();
 }
 
 void AggExprEmitter::VisitExprWithCleanups(ExprWithCleanups *E) {
index 884ce96859c5aee6fa85815f0b04888b62bb6455..98ef25bb1923029d6ca5884aa2e7b14774cdc93f 100644 (file)
@@ -2253,21 +2253,3 @@ llvm::Value *CodeGenFunction::EmitDynamicCast(Address ThisAddr,
 
   return Value;
 }
-
-void CodeGenFunction::EmitLambdaExpr(const LambdaExpr *E, AggValueSlot Slot) {
-  LValue SlotLV = MakeAddrLValue(Slot.getAddress(), E->getType());
-
-  CXXRecordDecl::field_iterator CurField = E->getLambdaClass()->field_begin();
-  for (LambdaExpr::const_capture_init_iterator i = E->capture_init_begin(),
-                                               e = E->capture_init_end();
-       i != e; ++i, ++CurField) {
-    // Emit initialization
-    LValue LV = EmitLValueForFieldInitialization(SlotLV, *CurField);
-    if (CurField->hasCapturedVLAType()) {
-      auto VAT = CurField->getCapturedVLAType();
-      EmitStoreThroughLValue(RValue::get(VLASizeMap[VAT->getSizeExpr()]), LV);
-    } else {
-      EmitInitializerForField(*CurField, LV, *i);
-    }
-  }
-}
index 89cb850ab1b1020550ec48e8b71c97553a633afc..005337a56b95df0ce1be069b5858f2f32ac3b5ce 100644 (file)
@@ -1836,6 +1836,9 @@ public:
   void EmitLambdaBlockInvokeBody();
   void EmitLambdaDelegatingInvokeBody(const CXXMethodDecl *MD);
   void EmitLambdaStaticInvokeBody(const CXXMethodDecl *MD);
+  void EmitLambdaVLACapture(const VariableArrayType *VAT, LValue LV) {
+    EmitStoreThroughLValue(RValue::get(VLASizeMap[VAT->getSizeExpr()]), LV);
+  }
   void EmitAsanPrologueOrEpilogue(bool Prologue);
 
   /// Emit the unified return block, trying to avoid its emission when
@@ -3560,7 +3563,6 @@ public:
 
   LValue EmitCXXConstructLValue(const CXXConstructExpr *E);
   LValue EmitCXXBindTemporaryLValue(const CXXBindTemporaryExpr *E);
-  LValue EmitLambdaLValue(const LambdaExpr *E);
   LValue EmitCXXTypeidLValue(const CXXTypeidExpr *E);
   LValue EmitCXXUuidofLValue(const CXXUuidofExpr *E);
 
@@ -3982,8 +3984,6 @@ public:
 
   void EmitCXXThrowExpr(const CXXThrowExpr *E, bool KeepInsertionPoint = true);
 
-  void EmitLambdaExpr(const LambdaExpr *E, AggValueSlot Dest);
-
   RValue EmitAtomicExpr(AtomicExpr *E);
 
   //===--------------------------------------------------------------------===//
diff --git a/test/CodeGenCXX/cxx1y-init-captures-eh.cpp b/test/CodeGenCXX/cxx1y-init-captures-eh.cpp
new file mode 100644 (file)
index 0000000..70103dc
--- /dev/null
@@ -0,0 +1,104 @@
+// RUN: %clang_cc1 -std=c++1y -triple x86_64-linux-gnu -fexceptions -fcxx-exceptions -emit-llvm %s -o - | FileCheck %s
+
+struct S {
+  S();
+  ~S();
+};
+
+struct T {
+  T() noexcept;
+  ~T();
+  int n;
+};
+
+// CHECK-LABEL: define void @_Z1fv(
+void f() {
+  // CHECK: call void @_ZN1SC1Ev(
+  // CHECK: invoke void @__cxa_throw
+  //
+  // Ensure we call the lambda destructor here, and do not call the destructor
+  // for the capture.
+  // CHECK: landingpad
+  // CHECK-NOT: _ZN1SD
+  // CHECK: call void @"_ZZ1fvEN3$_0D1Ev"(
+  // CHECK-NOT: _ZN1SD
+  // CHECK: resume
+  [s = S()] {}, throw 0;
+
+  // CHECK: }
+}
+
+// CHECK-LABEL: define void @_Z1gv(
+void g() {
+  // CHECK: call void @_ZN1SC1Ev(
+  // CHECK: invoke void @__cxa_throw
+  //
+  // Ensure we call the lambda destructor here, and do not call the destructor
+  // for the capture.
+  // CHECK: landingpad
+  // CHECK-NOT: @"_ZZ1gvEN3$_0D1Ev"(
+  // CHECK: call void @_ZN1SD1Ev(
+  // CHECK-NOT: @"_ZZ1gvEN3$_0D1Ev"(
+  // CHECK: resume
+  [s = S(), t = (throw 0, 1)] {};
+
+  // CHECK: }
+}
+
+void x() noexcept;
+void y() noexcept;
+
+// CHECK-LABEL: define void @_Z1hbb(
+void h(bool b1, bool b2) {
+  // CHECK: {{.*}} = alloca i1,
+  // CHECK: %[[S_ISACTIVE:.*]] = alloca i1,
+  // CHECK: {{.*}} = alloca i1,
+
+  // lambda init: s and t, branch on b1
+  // CHECK: call void @_ZN1SC1Ev(
+  // CHECK: store i1 true, i1* %[[S_ISACTIVE]], align 1
+  // CHECK: call void @_ZN1TC1Ev(
+  // CHECK: br i1
+
+  // throw 1
+  // CHECK: invoke void @__cxa_throw
+
+  // completion of lambda init, branch on b2
+  // CHECK: store i32 42,
+  // CHECK: store i1 false, i1* %[[S_ISACTIVE]], align 1
+  // CHECK: br i1
+
+  // throw 2
+  // CHECK: invoke void @__cxa_throw
+
+  // end of full-expression
+  // CHECK: call void @_Z1xv(
+  // CHECK: call void @"_ZZ1hbbEN3$_2D1Ev"(
+  // CHECK: call void @_ZN1TD1Ev(
+  // CHECK: call void @_Z1yv(
+  // CHECK: ret void
+
+  // cleanups for throw 1
+  // CHECK: landingpad
+  // CHECK-NOT: @"_ZZ1hbbEN3$_2D1Ev"(
+  // CHECK: br
+
+  // cleanups for throw 2
+  // CHECK: landingpad
+  // CHECK: call void @"_ZZ1hbbEN3$_2D1Ev"(
+  // CHECK: br
+
+  // common cleanup code
+  // CHECK: call void @_ZN1TD1Ev(
+  // CHECK: load i1, i1* %[[S_ISACTIVE]],
+  // CHECK: br i1
+
+  // CHECK: call void @_ZN1SD1Ev(
+  // CHECK: br
+
+  // CHECK: resume
+  [s = S(), t = T().n, u = (b1 ? throw 1 : 42)] {}, (b2 ? throw 2 : 0), x();
+  y();
+
+  // CHECK: }
+}