Skip to content

Commit 25d9688

Browse files
authored
[Clang] Extend lifetime of temporaries in mem-default-init for P2718R0 (#86960)
Depends on [CWG1815](#108039). Fixes #85613. In [[Clang] Implement P2718R0 "Lifetime extension in range-based for loops"](#76361), we've not implement the lifetime extensions for the temporaries which in `CXXDefaultInitExpr`. As the confirmation in #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 <[email protected]>
1 parent cb5fbd2 commit 25d9688

File tree

6 files changed

+188
-9
lines changed

6 files changed

+188
-9
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,9 @@ C++23 Feature Support
171171
^^^^^^^^^^^^^^^^^^^^^
172172
- Removed the restriction to literal types in constexpr functions in C++23 mode.
173173

174+
- Extend lifetime of temporaries in mem-default-init for P2718R0. Clang now fully
175+
supported `P2718R0 Lifetime extension in range-based for loops <https://wg21.link/P2718R0>`_.
176+
174177
C++20 Feature Support
175178
^^^^^^^^^^^^^^^^^^^^^
176179

clang/lib/Sema/SemaExpr.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5649,6 +5649,8 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
56495649
runWithSufficientStackSpace(Loc, [&] {
56505650
MarkDeclarationsReferencedInExpr(E, /*SkipLocalVariables=*/false);
56515651
});
5652+
if (isInLifetimeExtendingContext())
5653+
DiscardCleanupsInEvaluationContext();
56525654
// C++11 [class.base.init]p7:
56535655
// The initialization of each base and member constitutes a
56545656
// full-expression.

clang/lib/Sema/SemaInit.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,8 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field,
763763
SemaRef.currentEvaluationContext().DelayedDefaultInitializationContext =
764764
SemaRef.parentEvaluationContext()
765765
.DelayedDefaultInitializationContext;
766+
SemaRef.currentEvaluationContext().InLifetimeExtendingContext =
767+
SemaRef.parentEvaluationContext().InLifetimeExtendingContext;
766768
DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field);
767769
}
768770
if (DIE.isInvalid()) {

clang/test/AST/ast-dump-for-range-lifetime.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,4 +449,63 @@ void test13() {
449449
for (auto e : dg<A>().r().g().r().g().r().g())
450450
bar(e);
451451
}
452+
453+
extern "C" void exit(int);
454+
455+
struct A14 {
456+
int arr[1];
457+
~A14() noexcept(false) { throw 42; }
458+
};
459+
460+
struct B14 {
461+
int x;
462+
const A14 &a = A14{{0}};
463+
const int *begin() { return a.arr; }
464+
const int *end() { return &a.arr[1]; }
465+
};
466+
467+
void test14() {
468+
// The ExprWithCleanups in CXXDefaultInitExpr will be ignored.
469+
470+
// CHECK: FunctionDecl {{.*}} test14 'void ()'
471+
// CHECK: -CXXForRangeStmt {{.*}}
472+
// CHECK-NEXT: |-<<<NULL>>>
473+
// CHECK-NEXT: |-DeclStmt {{.*}}
474+
// CHECK-NEXT: | `-VarDecl {{.*}} implicit used __range1 'const int (&)[1]' cinit
475+
// CHECK-NEXT: | `-ExprWithCleanups {{.*}} 'const int[1]' lvalue
476+
// CHECK-NEXT: | `-MemberExpr {{.*}} 'const int[1]' lvalue .arr {{.*}}
477+
// CHECK-NEXT: | `-MemberExpr {{.*}} 'const A14':'const P2718R0::A14' lvalue .a {{.*}}
478+
// CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'B14':'P2718R0::B14' xvalue extended by Var {{.*}} '__range1' 'const int (&)[1]'
479+
// CHECK-NEXT: | `-CXXFunctionalCastExpr {{.*}} 'B14':'P2718R0::B14' functional cast to B14 <NoOp>
480+
// CHECK-NEXT: | `-InitListExpr {{.*}} 'B14':'P2718R0::B14'
481+
// CHECK-NEXT: | |-IntegerLiteral {{.*}} 'int' 0
482+
// CHECK-NEXT: | `-CXXDefaultInitExpr {{.*}} 'const A14':'const P2718R0::A14' lvalue has rewritten init
483+
// CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'const A14':'const P2718R0::A14' lvalue extended by Var {{.*}} '__range1' 'const int (&)[1]'
484+
// CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'const A14':'const P2718R0::A14' <NoOp>
485+
// CHECK-NEXT: | `-CXXFunctionalCastExpr {{.*}} 'A14':'P2718R0::A14' functional cast to A14 <NoOp>
486+
// CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'A14':'P2718R0::A14' (CXXTemporary {{.*}})
487+
// CHECK-NEXT: | `-InitListExpr {{.*}} 'A14':'P2718R0::A14'
488+
// CHECK-NEXT: | `-InitListExpr {{.*}} 'int[1]'
489+
// CHECK-NEXT: | `-IntegerLiteral {{.*}} 'int' 0
490+
for (auto &&x : B14{0}.a.arr) { exit(0); }
491+
492+
// CHECK: -CXXForRangeStmt {{.*}}
493+
// CHECK-NEXT: |-<<<NULL>>>
494+
// CHECK-NEXT: |-DeclStmt {{.*}}
495+
// CHECK-NEXT: | `-VarDecl {{.*}} col:19 implicit used __range1 'B14 &&' cinit
496+
// CHECK-NEXT: | `-ExprWithCleanups {{.*}} 'B14':'P2718R0::B14' xvalue
497+
// CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'B14':'P2718R0::B14' xvalue extended by Var {{.*}} '__range1' 'B14 &&'
498+
// CHECK-NEXT: | `-CXXFunctionalCastExpr {{.*}} 'B14':'P2718R0::B14' functional cast to B14 <NoOp>
499+
// CHECK-NEXT: | `-InitListExpr {{.*}} 'B14':'P2718R0::B14'
500+
// CHECK-NEXT: | |-IntegerLiteral {{.*}} 'int' 0
501+
// CHECK-NEXT: | `-CXXDefaultInitExpr {{.*}} 'const A14':'const P2718R0::A14' lvalue has rewritten init
502+
// CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'const A14':'const P2718R0::A14' lvalue extended by Var {{.*}} '__range1' 'B14 &&'
503+
// CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'const A14':'const P2718R0::A14' <NoOp>
504+
// CHECK-NEXT: | `-CXXFunctionalCastExpr {{.*}} 'A14':'P2718R0::A14' functional cast to A14 <NoOp>
505+
// CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'A14':'P2718R0::A14' (CXXTemporary {{.*}})
506+
// CHECK-NEXT: | `-InitListExpr {{.*}} 'A14':'P2718R0::A14'
507+
// CHECK-NEXT: | `-InitListExpr {{.*}} 'int[1]'
508+
// CHECK-NEXT: | `-IntegerLiteral {{.*}} 'int' 0
509+
for (auto &&x : B14{0}) { exit(0); }
510+
}
452511
} // namespace P2718R0

clang/test/CXX/special/class.temporary/p6.cpp

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,80 @@ template void default_arg_dependent_context2<int>();
463463
template void default_arg_dependent_context3<int>();
464464
} // namespace default_arg
465465

466+
namespace default_init {
467+
template <class T>
468+
struct DepA {
469+
T arr[1];
470+
~DepA() {}
471+
};
472+
473+
template <class T>
474+
struct DepB {
475+
int x;
476+
const DepA<T> &a = DepA<T>{{0}};
477+
~DepB() {}
478+
const int *begin() { return a.arr; }
479+
const int *end() { return &a.arr[1]; }
480+
};
481+
482+
template <typename T>
483+
void default_init1_dependent() {
484+
// CHECK-CXX23: void @_ZN7P2718R012default_init23default_init1_dependentINS0_4DepBIiEEEEvv()
485+
// CHECK-CXX23-LABEL: for.cond.cleanup:
486+
// CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init4DepBIiED1Ev(
487+
// CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init4DepAIiED1Ev(
488+
for (auto &&x : T{0}) {}
489+
}
490+
491+
template <typename T>
492+
void default_init2_dependent() {
493+
// CHECK-CXX23: void @_ZN7P2718R012default_init23default_init2_dependentINS0_4DepBIiEEEEvv()
494+
// CHECK-CXX23-LABEL: for.cond.cleanup:
495+
// CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init4DepBIiED1Ev(
496+
// CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init4DepAIiED1Ev(
497+
for (auto &&x : T{0}.a.arr) {}
498+
}
499+
500+
template void default_init1_dependent<DepB<int>>();
501+
template void default_init2_dependent<DepB<int>>();
502+
} // namespace default_init
503+
504+
// -- Examples from https://wg21.link/p2718r0
505+
extern void block_scope_begin_function();
506+
extern void block_scope_end_function();
507+
namespace std_examples {
508+
using T = std::list<int>;
509+
const T& f1(const T& t) { return t; }
510+
const T& f2(T t) { return t; }
511+
T g();
512+
void foo() {
513+
// CHECK-CXX23: define {{.*}} void @_ZN7P2718R012std_examples3fooEv()
514+
// CHECK-CXX23: call void @_ZN7P2718R026block_scope_begin_functionEv
515+
block_scope_begin_function();
516+
{
517+
// CHECK-CXX23-NEXT: call void @_ZN7P2718R012std_examples1gEv
518+
// CHECK-CXX23-NEXT: call {{.*}} @_ZN7P2718R012std_examples2f1ERKSt4listIiE
519+
// CHECK-CXX23: for.cond.cleanup:
520+
// CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev
521+
for (auto e : f1(g())) {} // OK, lifetime of return value of g() extended
522+
}
523+
// CHECK-CXX23: call void @_ZN7P2718R024block_scope_end_functionEv
524+
block_scope_end_function();
525+
526+
// The lifetime of temporary returned by g() in this case will not be extended.
527+
// CHECK-CXX23: call void @_ZN7P2718R026block_scope_begin_functionEv
528+
block_scope_begin_function();
529+
{
530+
// CHECK-CXX23-NEXT: call void @_ZN7P2718R012std_examples1gEv
531+
// CHECK-CXX23-NEXT: call {{.*}} @_ZN7P2718R012std_examples2f2ESt4listIiE
532+
// CHECK-CXX23-NEXT: call void @_ZNSt4listIiED1Ev
533+
for (auto e : f2(g())) {} // undefined behavior
534+
}
535+
// CHECK-CXX23: call void @_ZN7P2718R024block_scope_end_functionEv
536+
block_scope_end_function();
537+
}
538+
} // namespace std_examples
539+
466540
namespace basic {
467541
using T = std::list<int>;
468542
const T& f1(const T& t) { return t; }
@@ -579,5 +653,51 @@ void default_arg3() {
579653
for (auto e : C(0, C(0, C(0, C())))) {}
580654
}
581655
} // namespace default_arg
582-
} // namespace P2718R0
583656

657+
namespace default_init {
658+
struct X {
659+
int x;
660+
~X() {}
661+
};
662+
663+
struct Y {
664+
int y;
665+
const X &x = X{1};
666+
~Y() {}
667+
};
668+
669+
struct A {
670+
int arr[1];
671+
const Y &y = Y{1};
672+
~A() {}
673+
};
674+
675+
struct B {
676+
int x;
677+
const A &a = A{{0}};
678+
~B() {}
679+
const int *begin() { return a.arr; }
680+
const int *end() { return &a.arr[1]; }
681+
};
682+
683+
void default_init1() {
684+
// CHECK-CXX23: void @_ZN7P2718R012default_init13default_init1Ev()
685+
// CHECK-CXX23-LABEL: for.cond.cleanup:
686+
// CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1BD1Ev(
687+
// CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1AD1Ev(
688+
// CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1YD1Ev(
689+
// CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1XD1Ev(
690+
for (auto &&x : B{0}) {}
691+
}
692+
693+
void default_init2() {
694+
// CHECK-CXX23: void @_ZN7P2718R012default_init13default_init2Ev()
695+
// CHECK-CXX23-LABEL: for.cond.cleanup:
696+
// CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1BD1Ev(
697+
// CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1AD1Ev(
698+
// CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1YD1Ev(
699+
// CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1XD1Ev(
700+
for (auto &&x : B{0}.a.arr) {}
701+
}
702+
} // namespace default_init
703+
} // namespace P2718R0

clang/www/cxx_status.html

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -475,14 +475,7 @@ <h2 id="cxx23">C++23 implementation status</h2>
475475
<tr>
476476
<td>Lifetime extension in range-based for loops</td>
477477
<td><a href="https://wg21.link/P2718R0">P2718R0</a></td>
478-
<td class="partial" align="center">
479-
<details>
480-
<summary>Clang 19 (Partial)</summary>
481-
The lifetime extension of temporaries bound to member references
482-
by default member initializers in aggregate initialization was
483-
not supported now.
484-
</details>
485-
</td>
478+
<td class="full" align="center">Clang 20</td>
486479
</tr>
487480
<!--Issaquah 2023 papers-->
488481
<tr>

0 commit comments

Comments
 (0)