[clang] [Clang] Extend lifetime of temporaries in mem-default-init for P2718R0 (PR #86960)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Oct 6 05:07:41 PDT 2024
https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/86960
>From a8a87484760874d3b36673970e7158f0247df69b Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Thu, 26 Sep 2024 22:28:07 +0800
Subject: [PATCH 1/2] [clang][C++23] Extend lifetime of temporaries in
mem-default-init for P2718R0
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
clang/lib/Sema/SemaExpr.cpp | 2 +
clang/lib/Sema/SemaInit.cpp | 2 +
clang/test/CXX/special/class.temporary/p6.cpp | 106 +++++++++++++++++-
clang/www/cxx_status.html | 9 +-
4 files changed, 110 insertions(+), 9 deletions(-)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 66df9c969256a2..bde73670b0b07a 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5639,6 +5639,8 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
runWithSufficientStackSpace(Loc, [&] {
MarkDeclarationsReferencedInExpr(E, /*SkipLocalVariables=*/false);
});
+ if (isInLifetimeExtendingContext())
+ DiscardCleanupsInEvaluationContext();
// C++11 [class.base.init]p7:
// The initialization of each base and member constitutes a
// full-expression.
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 4d11f2a43fcc6b..a5a716e7499f38 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -763,6 +763,8 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field,
SemaRef.currentEvaluationContext().DelayedDefaultInitializationContext =
SemaRef.parentEvaluationContext()
.DelayedDefaultInitializationContext;
+ SemaRef.currentEvaluationContext().InLifetimeExtendingContext =
+ SemaRef.parentEvaluationContext().InLifetimeExtendingContext;
DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field);
}
if (DIE.isInvalid()) {
diff --git a/clang/test/CXX/special/class.temporary/p6.cpp b/clang/test/CXX/special/class.temporary/p6.cpp
index a6d2adfd1fd2c5..6c79de5dcbfae3 100644
--- a/clang/test/CXX/special/class.temporary/p6.cpp
+++ b/clang/test/CXX/special/class.temporary/p6.cpp
@@ -304,6 +304,26 @@ void check_dr1815() { // dr1815: yes
}
namespace P2718R0 {
+extern void block_scope_begin_function();
+extern void block_scope_end_function();
+
+template <typename E> using T2 = std::list<E>;
+template <typename E> const T2<E> &f1_temp(const T2<E> &t) { return t; }
+template <typename E> const T2<E> &f2_temp(T2<E> t) { return t; }
+template <typename E> T2<E> g_temp() { return T2<E>{}; }
+
+// -- Examples from https://wg21.link/p2718r0
+namespace std_examples {
+using T = std::list<int>;
+const T& f1(const T& t) { return t; }
+const T& f2(T t) { return t; }
+T g();
+void foo() {
+ for (auto e : f1(g())) {} // OK, lifetime of return value of g() extended
+ for (auto e : f2(g())) {} // undefined behavior
+}
+} // namespace std_examples
+
namespace basic {
template <typename E> using T2 = std::list<E>;
template <typename E> const T2<E> &f1_temp(const T2<E> &t) { return t; }
@@ -463,6 +483,44 @@ template void default_arg_dependent_context2<int>();
template void default_arg_dependent_context3<int>();
} // namespace default_arg
+namespace default_init {
+template <class T>
+struct DepA {
+ T arr[1];
+ ~DepA() {}
+};
+
+template <class T>
+struct DepB {
+ int x;
+ const DepA<T> &a = DepA<T>{{0}};
+ ~DepB() {}
+ const int *begin() { return a.arr; }
+ const int *end() { return &a.arr[1]; }
+};
+
+template <typename T>
+void default_init1_dependent() {
+ // CHECK-CXX23: void @_ZN7P2718R012default_init23default_init1_dependentINS0_4DepBIiEEEEvv()
+ // CHECK-CXX23-LABEL: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init4DepBIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init4DepAIiED1Ev(
+ for (auto &&x : T{0}) {}
+}
+
+template <typename T>
+void default_init2_dependent() {
+ // CHECK-CXX23: void @_ZN7P2718R012default_init23default_init2_dependentINS0_4DepBIiEEEEvv()
+ // CHECK-CXX23-LABEL: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init4DepBIiED1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init4DepAIiED1Ev(
+ for (auto &&x : T{0}.a.arr) {}
+}
+
+template void default_init1_dependent<DepB<int>>();
+template void default_init2_dependent<DepB<int>>();
+} // namespace default_init
+
namespace basic {
using T = std::list<int>;
const T& f1(const T& t) { return t; }
@@ -579,5 +637,51 @@ void default_arg3() {
for (auto e : C(0, C(0, C(0, C())))) {}
}
} // namespace default_arg
-} // namespace P2718R0
+namespace default_init {
+struct X {
+ int x;
+ ~X() {}
+};
+
+struct Y {
+ int y;
+ const X &x = X{1};
+ ~Y() {}
+};
+
+struct A {
+ int arr[1];
+ const Y &y = Y{1};
+ ~A() {}
+};
+
+struct B {
+ int x;
+ const A &a = A{{0}};
+ ~B() {}
+ const int *begin() { return a.arr; }
+ const int *end() { return &a.arr[1]; }
+};
+
+void default_init1() {
+ // CHECK-CXX23: void @_ZN7P2718R012default_init13default_init1Ev()
+ // CHECK-CXX23-LABEL: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1BD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1AD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1YD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1XD1Ev(
+ for (auto &&x : B{0}) {}
+}
+
+void default_init2() {
+ // CHECK-CXX23: void @_ZN7P2718R012default_init13default_init2Ev()
+ // CHECK-CXX23-LABEL: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1BD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1AD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1YD1Ev(
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1XD1Ev(
+ for (auto &&x : B{0}.a.arr) {}
+}
+} // namespace default_init
+} // namespace P2718R0
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 3f6a46c08c8514..d59cbbbbec1b5b 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -475,14 +475,7 @@ <h2 id="cxx23">C++23 implementation status</h2>
<tr>
<td>Lifetime extension in range-based for loops</td>
<td><a href="https://wg21.link/P2718R0">P2718R0</a></td>
- <td class="partial" align="center">
- <details>
- <summary>Clang 19 (Partial)</summary>
- The lifetime extension of temporaries bound to member references
- by default member initializers in aggregate initialization was
- not supported now.
- </details>
- </td>
+ <td class="full" align="center">Clang 20</td>
</tr>
<!--Issaquah 2023 papers-->
<tr>
>From c2d5f790641f5297e557cf6af359123eedbe93fb Mon Sep 17 00:00:00 2001
From: yronglin <yronglin777 at gmail.com>
Date: Sun, 29 Sep 2024 22:42:15 +0800
Subject: [PATCH 2/2] Add more test
Signed-off-by: yronglin <yronglin777 at gmail.com>
---
.../test/AST/ast-dump-for-range-lifetime.cpp | 59 +++++++++++++++++++
clang/test/CXX/special/class.temporary/p6.cpp | 56 +++++++++++-------
2 files changed, 95 insertions(+), 20 deletions(-)
diff --git a/clang/test/AST/ast-dump-for-range-lifetime.cpp b/clang/test/AST/ast-dump-for-range-lifetime.cpp
index 0e92b6990ed504..ee046be19ab632 100644
--- a/clang/test/AST/ast-dump-for-range-lifetime.cpp
+++ b/clang/test/AST/ast-dump-for-range-lifetime.cpp
@@ -449,4 +449,63 @@ void test13() {
for (auto e : dg<A>().r().g().r().g().r().g())
bar(e);
}
+
+extern "C" void exit(int);
+
+struct A14 {
+ int arr[1];
+ ~A14() noexcept(false) { throw 42; }
+};
+
+struct B14 {
+ int x;
+ const A14 &a = A14{{0}};
+ const int *begin() { return a.arr; }
+ const int *end() { return &a.arr[1]; }
+};
+
+void test14() {
+ // The ExprWithCleanups in CXXDefaultInitExpr will be ignored.
+
+ // CHECK: FunctionDecl {{.*}} test14 'void ()'
+ // CHECK: -CXXForRangeStmt {{.*}}
+ // CHECK-NEXT: |-<<<NULL>>>
+ // CHECK-NEXT: |-DeclStmt {{.*}}
+ // CHECK-NEXT: | `-VarDecl {{.*}} implicit used __range1 'const int (&)[1]' cinit
+ // CHECK-NEXT: | `-ExprWithCleanups {{.*}} 'const int[1]' lvalue
+ // CHECK-NEXT: | `-MemberExpr {{.*}} 'const int[1]' lvalue .arr {{.*}}
+ // CHECK-NEXT: | `-MemberExpr {{.*}} 'const A14':'const P2718R0::A14' lvalue .a {{.*}}
+ // CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'B14':'P2718R0::B14' xvalue extended by Var {{.*}} '__range1' 'const int (&)[1]'
+ // CHECK-NEXT: | `-CXXFunctionalCastExpr {{.*}} 'B14':'P2718R0::B14' functional cast to B14 <NoOp>
+ // CHECK-NEXT: | `-InitListExpr {{.*}} 'B14':'P2718R0::B14'
+ // CHECK-NEXT: | |-IntegerLiteral {{.*}} 'int' 0
+ // CHECK-NEXT: | `-CXXDefaultInitExpr {{.*}} 'const A14':'const P2718R0::A14' lvalue has rewritten init
+ // CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'const A14':'const P2718R0::A14' lvalue extended by Var {{.*}} '__range1' 'const int (&)[1]'
+ // CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'const A14':'const P2718R0::A14' <NoOp>
+ // CHECK-NEXT: | `-CXXFunctionalCastExpr {{.*}} 'A14':'P2718R0::A14' functional cast to A14 <NoOp>
+ // CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'A14':'P2718R0::A14' (CXXTemporary {{.*}})
+ // CHECK-NEXT: | `-InitListExpr {{.*}} 'A14':'P2718R0::A14'
+ // CHECK-NEXT: | `-InitListExpr {{.*}} 'int[1]'
+ // CHECK-NEXT: | `-IntegerLiteral {{.*}} 'int' 0
+ for (auto &&x : B14{0}.a.arr) { exit(0); }
+
+ // CHECK: -CXXForRangeStmt {{.*}}
+ // CHECK-NEXT: |-<<<NULL>>>
+ // CHECK-NEXT: |-DeclStmt {{.*}}
+ // CHECK-NEXT: | `-VarDecl {{.*}} col:19 implicit used __range1 'B14 &&' cinit
+ // CHECK-NEXT: | `-ExprWithCleanups {{.*}} 'B14':'P2718R0::B14' xvalue
+ // CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'B14':'P2718R0::B14' xvalue extended by Var {{.*}} '__range1' 'B14 &&'
+ // CHECK-NEXT: | `-CXXFunctionalCastExpr {{.*}} 'B14':'P2718R0::B14' functional cast to B14 <NoOp>
+ // CHECK-NEXT: | `-InitListExpr {{.*}} 'B14':'P2718R0::B14'
+ // CHECK-NEXT: | |-IntegerLiteral {{.*}} 'int' 0
+ // CHECK-NEXT: | `-CXXDefaultInitExpr {{.*}} 'const A14':'const P2718R0::A14' lvalue has rewritten init
+ // CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'const A14':'const P2718R0::A14' lvalue extended by Var {{.*}} '__range1' 'B14 &&'
+ // CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'const A14':'const P2718R0::A14' <NoOp>
+ // CHECK-NEXT: | `-CXXFunctionalCastExpr {{.*}} 'A14':'P2718R0::A14' functional cast to A14 <NoOp>
+ // CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'A14':'P2718R0::A14' (CXXTemporary {{.*}})
+ // CHECK-NEXT: | `-InitListExpr {{.*}} 'A14':'P2718R0::A14'
+ // CHECK-NEXT: | `-InitListExpr {{.*}} 'int[1]'
+ // CHECK-NEXT: | `-IntegerLiteral {{.*}} 'int' 0
+ for (auto &&x : B14{0}) { exit(0); }
+}
} // namespace P2718R0
diff --git a/clang/test/CXX/special/class.temporary/p6.cpp b/clang/test/CXX/special/class.temporary/p6.cpp
index 6c79de5dcbfae3..2b1b531b7172ca 100644
--- a/clang/test/CXX/special/class.temporary/p6.cpp
+++ b/clang/test/CXX/special/class.temporary/p6.cpp
@@ -304,26 +304,6 @@ void check_dr1815() { // dr1815: yes
}
namespace P2718R0 {
-extern void block_scope_begin_function();
-extern void block_scope_end_function();
-
-template <typename E> using T2 = std::list<E>;
-template <typename E> const T2<E> &f1_temp(const T2<E> &t) { return t; }
-template <typename E> const T2<E> &f2_temp(T2<E> t) { return t; }
-template <typename E> T2<E> g_temp() { return T2<E>{}; }
-
-// -- Examples from https://wg21.link/p2718r0
-namespace std_examples {
-using T = std::list<int>;
-const T& f1(const T& t) { return t; }
-const T& f2(T t) { return t; }
-T g();
-void foo() {
- for (auto e : f1(g())) {} // OK, lifetime of return value of g() extended
- for (auto e : f2(g())) {} // undefined behavior
-}
-} // namespace std_examples
-
namespace basic {
template <typename E> using T2 = std::list<E>;
template <typename E> const T2<E> &f1_temp(const T2<E> &t) { return t; }
@@ -521,6 +501,42 @@ template void default_init1_dependent<DepB<int>>();
template void default_init2_dependent<DepB<int>>();
} // namespace default_init
+// -- Examples from https://wg21.link/p2718r0
+extern void block_scope_begin_function();
+extern void block_scope_end_function();
+namespace std_examples {
+using T = std::list<int>;
+const T& f1(const T& t) { return t; }
+const T& f2(T t) { return t; }
+T g();
+void foo() {
+ // CHECK-CXX23: define {{.*}} void @_ZN7P2718R012std_examples3fooEv()
+ // CHECK-CXX23: call void @_ZN7P2718R026block_scope_begin_functionEv
+ block_scope_begin_function();
+ {
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R012std_examples1gEv
+ // CHECK-CXX23-NEXT: call {{.*}} @_ZN7P2718R012std_examples2f1ERKSt4listIiE
+ // CHECK-CXX23: for.cond.cleanup:
+ // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev
+ for (auto e : f1(g())) {} // OK, lifetime of return value of g() extended
+ }
+ // CHECK-CXX23: call void @_ZN7P2718R024block_scope_end_functionEv
+ block_scope_end_function();
+
+ // The lifetime of temporary returned by g() in this case will not be extended.
+ // CHECK-CXX23: call void @_ZN7P2718R026block_scope_begin_functionEv
+ block_scope_begin_function();
+ {
+ // CHECK-CXX23-NEXT: call void @_ZN7P2718R012std_examples1gEv
+ // CHECK-CXX23-NEXT: call {{.*}} @_ZN7P2718R012std_examples2f2ESt4listIiE
+ // CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev
+ for (auto e : f2(g())) {} // undefined behavior
+ }
+ // CHECK-CXX23: call void @_ZN7P2718R024block_scope_end_functionEv
+ block_scope_end_function();
+}
+} // namespace std_examples
+
namespace basic {
using T = std::list<int>;
const T& f1(const T& t) { return t; }
More information about the cfe-commits
mailing list