Skip to content

Commit 224116a

Browse files
committed
Revert "[Clang][CWG1815] Support lifetime extension of temporary created by aggregate initialization using a default member initializer (#87933)"
This reverts commit 17daa20. Multiple examples on the PR #87933 show regressions, so reverting until they can be fixed in the followup.
1 parent 033fa81 commit 224116a

12 files changed

+57
-89
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10037,6 +10037,12 @@ def warn_new_dangling_initializer_list : Warning<
1003710037
"the allocated initializer list}0 "
1003810038
"will be destroyed at the end of the full-expression">,
1003910039
InGroup<DanglingInitializerList>;
10040+
def warn_unsupported_lifetime_extension : Warning<
10041+
"lifetime extension of "
10042+
"%select{temporary|backing array of initializer list}0 created "
10043+
"by aggregate initialization using a default member initializer "
10044+
"is not yet supported; lifetime of %select{temporary|backing array}0 "
10045+
"will end at the end of the full-expression">, InGroup<Dangling>;
1004010046

1004110047
// For non-floating point, expressions of the form x == x or x != x
1004210048
// should result in a warning, since these always evaluate to a constant.

clang/lib/Sema/SemaExpr.cpp

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5618,9 +5618,10 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
56185618
Res = Immediate.TransformInitializer(Param->getInit(),
56195619
/*NotCopy=*/false);
56205620
});
5621-
if (Res.isUsable())
5622-
Res = ConvertParamDefaultArgument(Param, Res.get(),
5623-
Res.get()->getBeginLoc());
5621+
if (Res.isInvalid())
5622+
return ExprError();
5623+
Res = ConvertParamDefaultArgument(Param, Res.get(),
5624+
Res.get()->getBeginLoc());
56245625
if (Res.isInvalid())
56255626
return ExprError();
56265627
Init = Res.get();
@@ -5656,7 +5657,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
56565657
Expr *Init = nullptr;
56575658

56585659
bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
5659-
bool InLifetimeExtendingContext = isInLifetimeExtendingContext();
5660+
56605661
EnterExpressionEvaluationContext EvalContext(
56615662
*this, ExpressionEvaluationContext::PotentiallyEvaluated, Field);
56625663

@@ -5691,35 +5692,19 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
56915692
ImmediateCallVisitor V(getASTContext());
56925693
if (!NestedDefaultChecking)
56935694
V.TraverseDecl(Field);
5694-
5695-
// CWG1815
5696-
// Support lifetime extension of temporary created by aggregate
5697-
// initialization using a default member initializer. We should always rebuild
5698-
// the initializer if it contains any temporaries (if the initializer
5699-
// expression is an ExprWithCleanups). Then make sure the normal lifetime
5700-
// extension code recurses into the default initializer and does lifetime
5701-
// extension when warranted.
5702-
bool ContainsAnyTemporaries =
5703-
isa_and_present<ExprWithCleanups>(Field->getInClassInitializer());
5704-
if (V.HasImmediateCalls || InLifetimeExtendingContext ||
5705-
ContainsAnyTemporaries) {
5695+
if (V.HasImmediateCalls) {
57065696
ExprEvalContexts.back().DelayedDefaultInitializationContext = {Loc, Field,
57075697
CurContext};
57085698
ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer =
57095699
NestedDefaultChecking;
5710-
// Pass down lifetime extending flag, and collect temporaries in
5711-
// CreateMaterializeTemporaryExpr when we rewrite the call argument.
5712-
keepInLifetimeExtendingContext();
5700+
57135701
EnsureImmediateInvocationInDefaultArgs Immediate(*this);
57145702
ExprResult Res;
5715-
5716-
// Rebuild CXXDefaultInitExpr might cause diagnostics.
5717-
SFINAETrap Trap(*this);
57185703
runWithSufficientStackSpace(Loc, [&] {
57195704
Res = Immediate.TransformInitializer(Field->getInClassInitializer(),
57205705
/*CXXDirectInit=*/false);
57215706
});
5722-
if (Res.isUsable())
5707+
if (!Res.isInvalid())
57235708
Res = ConvertMemberDefaultInitExpression(Field, Res.get(), Loc);
57245709
if (Res.isInvalid()) {
57255710
Field->setInvalidDecl();

clang/lib/Sema/SemaInit.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8066,6 +8066,11 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
80668066
enum PathLifetimeKind {
80678067
/// Lifetime-extend along this path.
80688068
Extend,
8069+
/// We should lifetime-extend, but we don't because (due to technical
8070+
/// limitations) we can't. This happens for default member initializers,
8071+
/// which we don't clone for every use, so we don't have a unique
8072+
/// MaterializeTemporaryExpr to update.
8073+
ShouldExtend,
80698074
/// Do not lifetime extend along this path.
80708075
NoExtend
80718076
};
@@ -8077,7 +8082,7 @@ shouldLifetimeExtendThroughPath(const IndirectLocalPath &Path) {
80778082
PathLifetimeKind Kind = PathLifetimeKind::Extend;
80788083
for (auto Elem : Path) {
80798084
if (Elem.Kind == IndirectLocalPathEntry::DefaultInit)
8080-
Kind = PathLifetimeKind::Extend;
8085+
Kind = PathLifetimeKind::ShouldExtend;
80818086
else if (Elem.Kind != IndirectLocalPathEntry::LambdaCaptureInit)
80828087
return PathLifetimeKind::NoExtend;
80838088
}
@@ -8197,6 +8202,18 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
81978202
ExtendingEntity->allocateManglingNumber());
81988203
// Also visit the temporaries lifetime-extended by this initializer.
81998204
return true;
8205+
8206+
case PathLifetimeKind::ShouldExtend:
8207+
// We're supposed to lifetime-extend the temporary along this path (per
8208+
// the resolution of DR1815), but we don't support that yet.
8209+
//
8210+
// FIXME: Properly handle this situation. Perhaps the easiest approach
8211+
// would be to clone the initializer expression on each use that would
8212+
// lifetime extend its temporaries.
8213+
Diag(DiagLoc, diag::warn_unsupported_lifetime_extension)
8214+
<< RK << DiagRange;
8215+
break;
8216+
82008217
case PathLifetimeKind::NoExtend:
82018218
// If the path goes through the initialization of a variable or field,
82028219
// it can't possibly reach a temporary created in this full-expression.

clang/test/AST/ast-dump-default-init-json.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -789,10 +789,10 @@ void test() {
789789
// CHECK-NEXT: "valueCategory": "lvalue",
790790
// CHECK-NEXT: "extendingDecl": {
791791
// CHECK-NEXT: "id": "0x{{.*}}",
792-
// CHECK-NEXT: "kind": "VarDecl",
793-
// CHECK-NEXT: "name": "b",
792+
// CHECK-NEXT: "kind": "FieldDecl",
793+
// CHECK-NEXT: "name": "a",
794794
// CHECK-NEXT: "type": {
795-
// CHECK-NEXT: "qualType": "B"
795+
// CHECK-NEXT: "qualType": "const A &"
796796
// CHECK-NEXT: }
797797
// CHECK-NEXT: },
798798
// CHECK-NEXT: "storageDuration": "automatic",

clang/test/AST/ast-dump-default-init.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ void test() {
1313
}
1414
// CHECK: -CXXDefaultInitExpr 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue has rewritten init
1515
// CHECK-NEXT: `-ExprWithCleanups 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue
16-
// CHECK-NEXT: `-MaterializeTemporaryExpr 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue extended by Var 0x{{[^ ]*}} 'b' 'B'
16+
// CHECK-NEXT: `-MaterializeTemporaryExpr 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue extended by Field 0x{{[^ ]*}} 'a' 'const A &'
1717
// CHECK-NEXT: `-ImplicitCastExpr 0x{{[^ ]*}} <{{.*}}> 'const A' <NoOp>
1818
// CHECK-NEXT: `-CXXFunctionalCastExpr 0x{{[^ ]*}} <{{.*}}> 'A' functional cast to A <NoOp>
1919
// CHECK-NEXT: `-InitListExpr 0x{{[^ ]*}} <{{.*}}> 'A'

clang/test/Analysis/lifetime-extended-regions.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,10 @@ void aggregateWithReferences() {
120120
clang_analyzer_dump(viaReference); // expected-warning-re {{&lifetime_extended_object{RefAggregate, viaReference, S{{[0-9]+}}} }}
121121
clang_analyzer_dump(viaReference.rx); // expected-warning-re {{&lifetime_extended_object{int, viaReference, S{{[0-9]+}}} }}
122122
clang_analyzer_dump(viaReference.ry); // expected-warning-re {{&lifetime_extended_object{Composite, viaReference, S{{[0-9]+}}} }}
123-
124-
// FIXME: clang currently support extending lifetime of object bound to reference members of aggregates,
125-
// that are created from default member initializer. But CFG and ExprEngine need to be updated to address this change.
126-
// The following expect warning: {{&lifetime_extended_object{Composite, defaultInitExtended, S{{[0-9]+}}} }}
127-
RefAggregate defaultInitExtended{i};
123+
124+
// clang does not currently implement extending lifetime of object bound to reference members of aggregates,
125+
// that are created from default member initializer (see `warn_unsupported_lifetime_extension` from `-Wdangling`)
126+
RefAggregate defaultInitExtended{i}; // clang-bug does not extend `Composite`
128127
clang_analyzer_dump(defaultInitExtended.ry); // expected-warning {{Unknown }}
129128
}
130129

clang/test/CXX/drs/cwg16xx.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,8 @@ namespace cwg1696 { // cwg1696: 7
483483
const A &a = A(); // #cwg1696-D1-a
484484
};
485485
D1 d1 = {}; // #cwg1696-d1
486+
// since-cxx14-warning@-1 {{lifetime extension of temporary created by aggregate initialization using a default member initializer is not yet supported; lifetime of temporary will end at the end of the full-expression}}
487+
// since-cxx14-note@#cwg1696-D1-a {{initializing field 'a' with default member initializer}}
486488

487489
struct D2 {
488490
const A &a = A(); // #cwg1696-D2-a

clang/test/CXX/drs/cwg18xx.cpp

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ namespace cwg1804 { // cwg1804: 2.7
5656
template <typename, typename>
5757
struct A {
5858
void f1();
59-
59+
6060
template <typename V>
6161
void f2(V);
6262

@@ -73,7 +73,7 @@ struct A {
7373
template <typename U>
7474
struct A<int, U> {
7575
void f1();
76-
76+
7777
template <typename V>
7878
void f2(V);
7979

@@ -97,7 +97,7 @@ class D {
9797
template <typename U>
9898
struct A<double, U> {
9999
void f1();
100-
100+
101101
template <typename V>
102102
void f2(V);
103103

@@ -206,28 +206,19 @@ namespace cwg1814 { // cwg1814: yes
206206
#endif
207207
}
208208

209-
namespace cwg1815 { // cwg1815: 19
209+
namespace cwg1815 { // cwg1815: no
210210
#if __cplusplus >= 201402L
211-
struct A { int &&r = 0; };
211+
// FIXME: needs codegen test
212+
struct A { int &&r = 0; }; // #cwg1815-A
212213
A a = {};
214+
// since-cxx14-warning@-1 {{lifetime extension of temporary created by aggregate initialization using a default member initializer is not yet supported; lifetime of temporary will end at the end of the full-expression}} FIXME
215+
// since-cxx14-note@#cwg1815-A {{initializing field 'r' with default member initializer}}
213216

214217
struct B { int &&r = 0; }; // #cwg1815-B
215218
// since-cxx14-error@-1 {{reference member 'r' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
216219
// since-cxx14-note@#cwg1815-B {{initializing field 'r' with default member initializer}}
217220
// since-cxx14-note@#cwg1815-b {{in implicit default constructor for 'cwg1815::B' first required here}}
218221
B b; // #cwg1815-b
219-
220-
#if __cplusplus >= 201703L
221-
struct C { const int &r = 0; };
222-
constexpr C c = {}; // OK, since cwg1815
223-
static_assert(c.r == 0);
224-
225-
constexpr int f() {
226-
A a = {}; // OK, since cwg1815
227-
return a.r;
228-
}
229-
static_assert(f() == 0);
230-
#endif
231222
#endif
232223
}
233224

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

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -269,40 +269,6 @@ void init_capture_init_list() {
269269
// CHECK: }
270270
}
271271

272-
void check_dr1815() { // dr1815: yes
273-
#if __cplusplus >= 201402L
274-
275-
struct A {
276-
int &&r = 0;
277-
~A() {}
278-
};
279-
280-
struct B {
281-
A &&a = A{};
282-
~B() {}
283-
};
284-
B a = {};
285-
286-
// CHECK: call {{.*}}block_scope_begin_function
287-
extern void block_scope_begin_function();
288-
extern void block_scope_end_function();
289-
block_scope_begin_function();
290-
{
291-
// CHECK: call void @_ZZ12check_dr1815vEN1BD1Ev
292-
// CHECK: call void @_ZZ12check_dr1815vEN1AD1Ev
293-
B b = {};
294-
}
295-
// CHECK: call {{.*}}block_scope_end_function
296-
block_scope_end_function();
297-
298-
// CHECK: call {{.*}}some_other_function
299-
extern void some_other_function();
300-
some_other_function();
301-
// CHECK: call void @_ZZ12check_dr1815vEN1BD1Ev
302-
// CHECK: call void @_ZZ12check_dr1815vEN1AD1Ev
303-
#endif
304-
}
305-
306272
namespace P2718R0 {
307273
namespace basic {
308274
template <typename E> using T2 = std::list<E>;

clang/test/SemaCXX/constexpr-default-arg.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ void test_default_arg2() {
3232
}
3333

3434
// Check that multiple CXXDefaultInitExprs don't cause an assertion failure.
35-
struct A { int &&r = 0; };
35+
struct A { int &&r = 0; }; // expected-note 2{{default member initializer}}
3636
struct B { A x, y; };
37-
B b = {}; // expected-no-diagnostics
37+
B b = {}; // expected-warning 2{{lifetime extension of temporary created by aggregate initialization using a default member initializer is not yet supported}}
3838

3939
}

clang/test/SemaCXX/eval-crashes.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@ namespace pr33140_0b {
2525
}
2626

2727
namespace pr33140_2 {
28-
struct A { int &&r = 0; };
28+
// FIXME: The declaration of 'b' below should lifetime-extend two int
29+
// temporaries.
30+
struct A { int &&r = 0; }; // expected-note 2{{initializing field 'r' with default member initializer}}
2931
struct B { A x, y; };
30-
B b = {};
32+
B b = {}; // expected-warning 2{{lifetime extension of temporary created by aggregate initialization using a default member initializer is not yet supported}}
3133
}
3234

3335
namespace pr33140_3 {

clang/www/cxx_dr_status.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10698,7 +10698,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
1069810698
<td><a href="https://cplusplus.github.io/CWG/issues/1815.html">1815</a></td>
1069910699
<td>CD4</td>
1070010700
<td>Lifetime extension in aggregate initialization</td>
10701-
<td class="unreleased" align="center">Clang 19</td>
10701+
<td class="none" align="center">No</td>
1070210702
</tr>
1070310703
<tr id="1816">
1070410704
<td><a href="https://cplusplus.github.io/CWG/issues/1816.html">1816</a></td>

0 commit comments

Comments
 (0)