Skip to content

Commit 433df06

Browse files
committed
[Clang][CWG1815] Support lifetime extension of temporary created by aggregate initialization using a default member initializer
Signed-off-by: yronglin <yronglin777@gmail.com>
1 parent bbe40b9 commit 433df06

File tree

15 files changed

+171
-64
lines changed

15 files changed

+171
-64
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

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

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

clang/lib/Sema/SemaExpr.cpp

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5621,10 +5621,9 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
56215621
Res = Immediate.TransformInitializer(Param->getInit(),
56225622
/*NotCopy=*/false);
56235623
});
5624-
if (Res.isInvalid())
5625-
return ExprError();
5626-
Res = ConvertParamDefaultArgument(Param, Res.get(),
5627-
Res.get()->getBeginLoc());
5624+
if (Res.isUsable())
5625+
Res = ConvertParamDefaultArgument(Param, Res.get(),
5626+
Res.get()->getBeginLoc());
56285627
if (Res.isInvalid())
56295628
return ExprError();
56305629
Init = Res.get();
@@ -5660,7 +5659,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
56605659
Expr *Init = nullptr;
56615660

56625661
bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
5663-
5662+
bool InLifetimeExtendingContext = isInLifetimeExtendingContext();
56645663
EnterExpressionEvaluationContext EvalContext(
56655664
*this, ExpressionEvaluationContext::PotentiallyEvaluated, Field);
56665665

@@ -5695,19 +5694,35 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
56955694
ImmediateCallVisitor V(getASTContext());
56965695
if (!NestedDefaultChecking)
56975696
V.TraverseDecl(Field);
5698-
if (V.HasImmediateCalls) {
5697+
5698+
// CWG1815
5699+
// Support lifetime extension of temporary created by aggregate
5700+
// initialization using a default member initializer. We should always rebuild
5701+
// the initializer if it contains any temporaries (if the initializer
5702+
// expression is an ExprWithCleanups). Then make sure the normal lifetime
5703+
// extension code recurses into the default initializer and does lifetime
5704+
// extension when warranted.
5705+
bool ContainsAnyTemporaries =
5706+
isa_and_present<ExprWithCleanups>(Field->getInClassInitializer());
5707+
if (V.HasImmediateCalls || InLifetimeExtendingContext ||
5708+
ContainsAnyTemporaries) {
56995709
ExprEvalContexts.back().DelayedDefaultInitializationContext = {Loc, Field,
57005710
CurContext};
57015711
ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer =
57025712
NestedDefaultChecking;
5703-
5713+
// Pass down lifetime extending flag, and collect temporaries in
5714+
// CreateMaterializeTemporaryExpr when we rewrite the call argument.
5715+
keepInLifetimeExtendingContext();
57045716
EnsureImmediateInvocationInDefaultArgs Immediate(*this);
57055717
ExprResult Res;
5718+
5719+
// Rebuild CXXDefaultInitExpr might cause diagnostics.
5720+
SFINAETrap Trap(*this);
57065721
runWithSufficientStackSpace(Loc, [&] {
57075722
Res = Immediate.TransformInitializer(Field->getInClassInitializer(),
57085723
/*CXXDirectInit=*/false);
57095724
});
5710-
if (!Res.isInvalid())
5725+
if (Res.isUsable())
57115726
Res = ConvertMemberDefaultInitExpression(Field, Res.get(), Loc);
57125727
if (Res.isInvalid()) {
57135728
Field->setInvalidDecl();

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1554,9 +1554,6 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo,
15541554
bool ListInitialization) {
15551555
QualType Ty = TInfo->getType();
15561556
SourceLocation TyBeginLoc = TInfo->getTypeLoc().getBeginLoc();
1557-
1558-
assert((!ListInitialization || Exprs.size() == 1) &&
1559-
"List initialization must have exactly one expression.");
15601557
SourceRange FullRange = SourceRange(TyBeginLoc, RParenOrBraceLoc);
15611558

15621559
InitializedEntity Entity =

clang/lib/Sema/SemaInit.cpp

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8066,11 +8066,6 @@ 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,
80748069
/// Do not lifetime extend along this path.
80758070
NoExtend
80768071
};
@@ -8082,7 +8077,7 @@ shouldLifetimeExtendThroughPath(const IndirectLocalPath &Path) {
80828077
PathLifetimeKind Kind = PathLifetimeKind::Extend;
80838078
for (auto Elem : Path) {
80848079
if (Elem.Kind == IndirectLocalPathEntry::DefaultInit)
8085-
Kind = PathLifetimeKind::ShouldExtend;
8080+
Kind = PathLifetimeKind::Extend;
80868081
else if (Elem.Kind != IndirectLocalPathEntry::LambdaCaptureInit)
80878082
return PathLifetimeKind::NoExtend;
80888083
}
@@ -8202,18 +8197,6 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
82028197
ExtendingEntity->allocateManglingNumber());
82038198
// Also visit the temporaries lifetime-extended by this initializer.
82048199
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-
82178200
case PathLifetimeKind::NoExtend:
82188201
// If the path goes through the initialization of a variable or field,
82198202
// it can't possibly reach a temporary created in this full-expression.

clang/lib/Sema/TreeTransform.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14102,6 +14102,13 @@ TreeTransform<Derived>::TransformCXXTemporaryObjectExpr(
1410214102
if (TransformExprs(E->getArgs(), E->getNumArgs(), true, Args,
1410314103
&ArgumentChanged))
1410414104
return ExprError();
14105+
14106+
if (E->isListInitialization() && !E->isStdInitListInitialization()) {
14107+
ExprResult Res = RebuildInitList(E->getBeginLoc(), Args, E->getEndLoc());
14108+
if (Res.isInvalid())
14109+
return ExprError();
14110+
Args = {Res.get()};
14111+
}
1410514112
}
1410614113

1410714114
if (!getDerived().AlwaysRebuild() &&
@@ -14113,12 +14120,9 @@ TreeTransform<Derived>::TransformCXXTemporaryObjectExpr(
1411314120
return SemaRef.MaybeBindToTemporary(E);
1411414121
}
1411514122

14116-
// FIXME: We should just pass E->isListInitialization(), but we're not
14117-
// prepared to handle list-initialization without a child InitListExpr.
1411814123
SourceLocation LParenLoc = T->getTypeLoc().getEndLoc();
1411914124
return getDerived().RebuildCXXTemporaryObjectExpr(
14120-
T, LParenLoc, Args, E->getEndLoc(),
14121-
/*ListInitialization=*/LParenLoc.isInvalid());
14125+
T, LParenLoc, Args, E->getEndLoc(), E->isListInitialization());
1412214126
}
1412314127

1412414128
template<typename Derived>

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": "FieldDecl",
793-
// CHECK-NEXT: "name": "a",
792+
// CHECK-NEXT: "kind": "VarDecl",
793+
// CHECK-NEXT: "name": "b",
794794
// CHECK-NEXT: "type": {
795-
// CHECK-NEXT: "qualType": "const A &"
795+
// CHECK-NEXT: "qualType": "B"
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 Field 0x{{[^ ]*}} 'a' 'const A &'
16+
// CHECK-NEXT: `-MaterializeTemporaryExpr 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue extended by Var 0x{{[^ ]*}} 'b' 'B'
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: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,11 @@ 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-
// 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`
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};
127128
clang_analyzer_dump(defaultInitExtended.ry); // expected-warning {{Unknown }}
128129
}
129130

clang/test/CXX/drs/cwg16xx.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -483,8 +483,6 @@ 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}}
488486

489487
struct D2 {
490488
const A &a = A(); // #cwg1696-D2-a

clang/test/CXX/drs/cwg18xx.cpp

Lines changed: 17 additions & 8 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,19 +206,28 @@ namespace cwg1814 { // cwg1814: yes
206206
#endif
207207
}
208208

209-
namespace cwg1815 { // cwg1815: no
209+
namespace cwg1815 { // cwg1815: 19
210210
#if __cplusplus >= 201402L
211-
// FIXME: needs codegen test
212-
struct A { int &&r = 0; }; // #cwg1815-A
211+
struct A { int &&r = 0; };
213212
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}}
216213

217214
struct B { int &&r = 0; }; // #cwg1815-B
218215
// since-cxx14-error@-1 {{reference member 'r' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
219216
// since-cxx14-note@#cwg1815-B {{initializing field 'r' with default member initializer}}
220217
// since-cxx14-note@#cwg1815-b {{in implicit default constructor for 'cwg1815::B' first required here}}
221218
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
222231
#endif
223232
}
224233

0 commit comments

Comments
 (0)