[Clang] Extend lifetime of temporaries in mem-default-init for P2718R0 (#86960)
Depends on [CWG1815](https://github.com/llvm/llvm-project/pull/108039). Fixes https://github.com/llvm/llvm-project/issues/85613. In [[Clang] Implement P2718R0 "Lifetime extension in range-based for loops"](https://github.com/llvm/llvm-project/pull/76361), we've not implement the lifetime extensions for the temporaries which in `CXXDefaultInitExpr`. As the confirmation in https://github.com/llvm/llvm-project/issues/85613, we should extend lifetime for that. To avoid modifying current CodeGen rules, in a lifetime extension context, the cleanup of `CXXDefaultInitExpr` was ignored. --------- Signed-off-by: yronglin <yronglin777@gmail.com>
This commit is contained in:
@@ -171,6 +171,9 @@ C++23 Feature Support
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
- Removed the restriction to literal types in constexpr functions in C++23 mode.
|
||||
|
||||
- Extend lifetime of temporaries in mem-default-init for P2718R0. Clang now fully
|
||||
supported `P2718R0 Lifetime extension in range-based for loops <https://wg21.link/P2718R0>`_.
|
||||
|
||||
C++20 Feature Support
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
@@ -5649,6 +5649,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.
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -463,6 +463,80 @@ 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
|
||||
|
||||
// -- 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; }
|
||||
@@ -579,5 +653,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
|
||||
|
||||
@@ -475,14 +475,7 @@ C++23, informally referred to as C++26.</p>
|
||||
<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>
|
||||
|
||||
Reference in New Issue
Block a user