Izaron created this revision.
Izaron added reviewers: aaron.ballman, cor3ntin.
Izaron requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

When removing nested ConstantExprs, the tree transformer
doesn't remove redundant CXXFunctionalCasts that were created
"above" consteval constructors. After a while it generates a call
to a constructor, therefore violating the C++17 mandatory copy
elision rule.

Fixes https://github.com/llvm/llvm-project/issues/53244
and https://github.com/llvm/llvm-project/issues/53245


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D119095

Files:
  clang/lib/Sema/SemaExpr.cpp
  clang/test/SemaCXX/cxx2a-consteval.cpp


Index: clang/test/SemaCXX/cxx2a-consteval.cpp
===================================================================
--- clang/test/SemaCXX/cxx2a-consteval.cpp
+++ clang/test/SemaCXX/cxx2a-consteval.cpp
@@ -613,6 +613,45 @@
 
 } // namespace unevaluated
 
+namespace mandatory_copy_elision {
+
+struct A {
+  consteval A() {}
+  consteval A(const A &);
+  consteval A(A &&);
+  consteval void f() {}
+};
+
+struct B {
+  consteval B() {}
+  consteval B(const B &) = delete;
+  consteval B(B &&) = delete;
+  consteval void f() {}
+};
+
+struct C {
+  consteval C() {}
+  consteval void f() {}
+
+private:
+  consteval C(const C &){};
+  consteval C(C &&){};
+};
+
+void test() {
+  { A{}.f(); }
+  { A{A{}}.f(); }
+  { A{A{A{A{A{A{A{A{}}}}}}}}.f(); }
+  { B{}.f(); }
+  { B{B{}}.f(); }
+  { B{B{B{B{B{B{B{B{}}}}}}}}.f(); }
+  { C{}.f(); }
+  { C{C{}}.f(); }
+  { C{C{C{C{C{C{C{C{}}}}}}}}.f(); }
+}
+
+} // namespace mandatory_copy_elision
+
 namespace PR50779 {
 struct derp {
   int b = 0;
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -16796,6 +16796,14 @@
       DRSet.erase(cast<DeclRefExpr>(E->getCallee()->IgnoreImplicit()));
       return Base::TransformCXXOperatorCallExpr(E);
     }
+    /// Delete extra no-op functional casts to avoid calling a constructor
+    ExprResult TransformCXXFunctionalCastExpr(CXXFunctionalCastExpr *E) {
+      auto *CE = dyn_cast<ConstantExpr>(E->getSubExpr());
+      if (E->getCastKind() != CK_NoOp || !CE || !CE->isImmediateInvocation())
+        return Base::TransformCXXFunctionalCastExpr(E);
+      RemoveImmediateInvocation(CE);
+      return Base::TransformExpr(CE->getSubExpr());
+    }
     /// Base::TransformInitializer skip ConstantExpr so we need to visit them
     /// here.
     ExprResult TransformInitializer(Expr *Init, bool NotCopyInit) {


Index: clang/test/SemaCXX/cxx2a-consteval.cpp
===================================================================
--- clang/test/SemaCXX/cxx2a-consteval.cpp
+++ clang/test/SemaCXX/cxx2a-consteval.cpp
@@ -613,6 +613,45 @@
 
 } // namespace unevaluated
 
+namespace mandatory_copy_elision {
+
+struct A {
+  consteval A() {}
+  consteval A(const A &);
+  consteval A(A &&);
+  consteval void f() {}
+};
+
+struct B {
+  consteval B() {}
+  consteval B(const B &) = delete;
+  consteval B(B &&) = delete;
+  consteval void f() {}
+};
+
+struct C {
+  consteval C() {}
+  consteval void f() {}
+
+private:
+  consteval C(const C &){};
+  consteval C(C &&){};
+};
+
+void test() {
+  { A{}.f(); }
+  { A{A{}}.f(); }
+  { A{A{A{A{A{A{A{A{}}}}}}}}.f(); }
+  { B{}.f(); }
+  { B{B{}}.f(); }
+  { B{B{B{B{B{B{B{B{}}}}}}}}.f(); }
+  { C{}.f(); }
+  { C{C{}}.f(); }
+  { C{C{C{C{C{C{C{C{}}}}}}}}.f(); }
+}
+
+} // namespace mandatory_copy_elision
+
 namespace PR50779 {
 struct derp {
   int b = 0;
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -16796,6 +16796,14 @@
       DRSet.erase(cast<DeclRefExpr>(E->getCallee()->IgnoreImplicit()));
       return Base::TransformCXXOperatorCallExpr(E);
     }
+    /// Delete extra no-op functional casts to avoid calling a constructor
+    ExprResult TransformCXXFunctionalCastExpr(CXXFunctionalCastExpr *E) {
+      auto *CE = dyn_cast<ConstantExpr>(E->getSubExpr());
+      if (E->getCastKind() != CK_NoOp || !CE || !CE->isImmediateInvocation())
+        return Base::TransformCXXFunctionalCastExpr(E);
+      RemoveImmediateInvocation(CE);
+      return Base::TransformExpr(CE->getSubExpr());
+    }
     /// Base::TransformInitializer skip ConstantExpr so we need to visit them
     /// here.
     ExprResult TransformInitializer(Expr *Init, bool NotCopyInit) {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to