[clang] 550fa4e - [clang] Do not discard cleanups while processing immediate invocation

Mariya Podchishchaeva via cfe-commits cfe-commits at lists.llvm.org
Fri Jun 30 01:52:12 PDT 2023


Author: Mariya Podchishchaeva
Date: 2023-06-30T04:50:08-04:00
New Revision: 550fa4eabd83d133595c7a5a07d54fc029b73733

URL: https://github.com/llvm/llvm-project/commit/550fa4eabd83d133595c7a5a07d54fc029b73733
DIFF: https://github.com/llvm/llvm-project/commit/550fa4eabd83d133595c7a5a07d54fc029b73733.diff

LOG: [clang] Do not discard cleanups while processing immediate invocation

Since an immediate invocation is a full expression itself - it requires
an additional ExprWithCleanups node, but it can participate to a bigger
full expression which actually requires cleanups to be run after.

Thanks @ilya-biryukov for helping reducing the reproducer and confirming
that the analysis is correct.

Fixes https://github.com/llvm/llvm-project/issues/60709

Reviewed By: ilya-biryukov

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

Added: 
    clang/test/CodeGenCXX/consteval-cleanup.cpp
    clang/test/SemaCXX/consteval-cleanup.cpp

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/lib/Sema/SemaExpr.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index da4ddff93c1f95..4a9a0305b7e140 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -557,6 +557,10 @@ Bug Fixes in This Version
   attempting to see if it could be type template for class template argument
   deduction. This fixes
   (`Issue 57495 <https://github.com/llvm/llvm-project/issues/57495>`_)
+- Fix missing destructor calls and therefore memory leaks in generated code
+  when an immediate invocation appears as a part of an expression that produces
+  temporaries.
+  (`#60709 <https://github.com/llvm/llvm-project/issues/60709>`_).
 
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

diff  --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 528b76518ff3a7..4d6414eed99cd2 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18183,7 +18183,25 @@ ExprResult Sema::CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl) {
     return E;
   }
 
-  E = MaybeCreateExprWithCleanups(E);
+  if (Cleanup.exprNeedsCleanups()) {
+    // Since an immediate invocation is a full expression itself - it requires
+    // an additional ExprWithCleanups node, but it can participate to a bigger
+    // full expression which actually requires cleanups to be run after so
+    // create ExprWithCleanups without using MaybeCreateExprWithCleanups as it
+    // may discard cleanups for outer expression too early.
+
+    // Note that ExprWithCleanups created here must always have empty cleanup
+    // objects:
+    // - compound literals do not create cleanup objects in C++ and immediate
+    // invocations are C++-only.
+    // - blocks are not allowed inside constant expressions and compiler will
+    // issue an error if they appear there.
+    //
+    // Hence, in correct code any cleanup objects created inside current
+    // evaluation context must be outside the immediate invocation.
+    E = ExprWithCleanups::Create(getASTContext(), E.get(),
+                                 Cleanup.cleanupsHaveSideEffects(), {});
+  }
 
   ConstantExpr *Res = ConstantExpr::Create(
       getASTContext(), E.get(),

diff  --git a/clang/test/CodeGenCXX/consteval-cleanup.cpp b/clang/test/CodeGenCXX/consteval-cleanup.cpp
new file mode 100644
index 00000000000000..e6fdd50df6cb04
--- /dev/null
+++ b/clang/test/CodeGenCXX/consteval-cleanup.cpp
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -std=c++20 -Wno-unused-value -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
+
+struct P {
+  consteval P() {}
+};
+
+struct A {
+  A(int v) { this->data = new int(v); }
+  ~A() { delete data; }
+private:
+  int *data;
+};
+
+void foo() {
+  for (;A(1), P(), false;);
+  // CHECK: foo
+  // CHECK: for.cond:
+  // CHECK: call void @_ZN1AC1Ei
+  // CHECK: call void @_ZN1AD1Ev
+  // CHECK: for.body
+}

diff  --git a/clang/test/SemaCXX/consteval-cleanup.cpp b/clang/test/SemaCXX/consteval-cleanup.cpp
new file mode 100644
index 00000000000000..ddc56174b484f4
--- /dev/null
+++ b/clang/test/SemaCXX/consteval-cleanup.cpp
@@ -0,0 +1,61 @@
+// RUN: %clang_cc1 -fblocks -Wno-unused-value -std=c++20 -ast-dump -verify %s -ast-dump | FileCheck %s
+
+// expected-no-diagnostics
+
+struct P {
+  consteval P() {}
+};
+
+struct A {
+  A(int v) { this->data = new int(v); }
+  const int& get() const {
+    return *this->data;
+  }
+  ~A() { delete data; }
+private:
+  int *data;
+};
+
+void foo() {
+  for (;A(1), P(), false;);
+  // CHECK: foo
+  // CHECK: ExprWithCleanups
+  // CHECK-NEXT: BinaryOperator {{.*}} 'bool' ','
+  // CHECK-NEXT: BinaryOperator {{.*}} 'P':'P' ','
+  // CHECK-NEXT: CXXFunctionalCastExpr {{.*}} 'A':'A'
+  // CHECK-NEXT: CXXBindTemporaryExpr {{.*}} 'A':'A'
+  // CHECK-NEXT: CXXConstructExpr {{.*}} 'A':'A'
+  // CHECK: ConstantExpr {{.*}} 'P':'P'
+  // CHECK-NEXT: value:
+  // CHECK-NEXT: ExprWithCleanups
+}
+
+void foobar() {
+  A a(1);
+  for (; ^{ auto ptr = &a.get(); }(), P(), false;);
+  // CHECK: ExprWithCleanups
+  // CHECK-NEXT: cleanup Block
+  // CHECK-NEXT: BinaryOperator {{.*}} 'bool' ','
+  // CHECK-NEXT: BinaryOperator {{.*}} 'P':'P' ','
+  // CHECK-NEXT: CallExpr
+  // CHECK-NEXT: BlockExpr
+  // CHECK: ConstantExpr {{.*}} 'P':'P'
+  // CHECK-NEXT: value:
+  // CHECK-NEXT: ExprWithCleanups
+  // CHECK-NOT:  cleanup Block
+}
+
+struct B {
+  int *p = new int(38);
+  consteval int get() { return *p; }
+  constexpr ~B() { delete p; }
+};
+
+void bar() {
+  // CHECK: bar
+  // CHECK: ExprWithCleanups
+  // CHECK: ConstantExpr
+  // CHECK-NEXT: value:
+  // CHECK-NEXT: ExprWithCleanups
+  int k = B().get();
+}


        


More information about the cfe-commits mailing list