Skip to content

Commit 0601370

Browse files
authored
Reapply "[Clang][CWG1815] Support lifetime extension of temporary created by aggregate initialization using a default member initializer" (#108039)
The PR reapply #97308. - Implement [CWG1815](https://wg21.link/CWG1815): Support lifetime extension of temporary created by aggregate initialization using a default member initializer. - Fix crash that introduced in #97308. In `InitListChecker::FillInEmptyInitForField`, when we enter rebuild-default-init context, we copy all the contents of the parent context to the current context, which will cause the `MaybeODRUseExprs` to be lost. But we don't need to copy the entire context, only the `DelayedDefaultInitializationContext` was required, which is used to build `SourceLocExpr`, etc. --------- Signed-off-by: yronglin <[email protected]>
1 parent c98d6c2 commit 0601370

21 files changed

+277
-87
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ C++ Language Changes
108108
- Allow single element access of GCC vector/ext_vector_type object to be
109109
constant expression. Supports the `V.xyzw` syntax and other tidbits
110110
as seen in OpenCL. Selecting multiple elements is left as a future work.
111+
- Implement `CWG1815 <https://wg21.link/CWG1815>`_. Support lifetime extension
112+
of temporary created by aggregate initialization using a default member
113+
initializer.
111114

112115
- Accept C++26 user-defined ``static_assert`` messages in C++11 as an extension.
113116

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10162,13 +10162,6 @@ def warn_dangling_pointer_assignment : Warning<
1016210162
"will be destroyed at the end of the full-expression">,
1016310163
InGroup<DanglingAssignment>;
1016410164

10165-
def warn_unsupported_lifetime_extension : Warning<
10166-
"lifetime extension of "
10167-
"%select{temporary|backing array of initializer list}0 created "
10168-
"by aggregate initialization using a default member initializer "
10169-
"is not yet supported; lifetime of %select{temporary|backing array}0 "
10170-
"will end at the end of the full-expression">, InGroup<Dangling>;
10171-
1017210165
// For non-floating point, expressions of the form x == x or x != x
1017310166
// should result in a warning, since these always evaluate to a constant.
1017410167
// Array comparisons have similar warnings

clang/include/clang/Sema/Sema.h

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6403,6 +6403,9 @@ class Sema final : public SemaBase {
64036403
/// example, in a for-range initializer).
64046404
bool InLifetimeExtendingContext = false;
64056405

6406+
/// Whether we should rebuild CXXDefaultArgExpr and CXXDefaultInitExpr.
6407+
bool RebuildDefaultArgOrDefaultInit = false;
6408+
64066409
// When evaluating immediate functions in the initializer of a default
64076410
// argument or default member initializer, this is the declaration whose
64086411
// default initializer is being evaluated and the location of the call
@@ -7810,9 +7813,11 @@ class Sema final : public SemaBase {
78107813
}
78117814

78127815
bool isInLifetimeExtendingContext() const {
7813-
assert(!ExprEvalContexts.empty() &&
7814-
"Must be in an expression evaluation context");
7815-
return ExprEvalContexts.back().InLifetimeExtendingContext;
7816+
return currentEvaluationContext().InLifetimeExtendingContext;
7817+
}
7818+
7819+
bool needsRebuildOfDefaultArgOrInit() const {
7820+
return currentEvaluationContext().RebuildDefaultArgOrDefaultInit;
78167821
}
78177822

78187823
bool isCheckingDefaultArgumentOrInitializer() const {
@@ -7854,18 +7859,6 @@ class Sema final : public SemaBase {
78547859
return Res;
78557860
}
78567861

7857-
/// keepInLifetimeExtendingContext - Pull down InLifetimeExtendingContext
7858-
/// flag from previous context.
7859-
void keepInLifetimeExtendingContext() {
7860-
if (ExprEvalContexts.size() > 2 &&
7861-
parentEvaluationContext().InLifetimeExtendingContext) {
7862-
auto &LastRecord = ExprEvalContexts.back();
7863-
auto &PrevRecord = parentEvaluationContext();
7864-
LastRecord.InLifetimeExtendingContext =
7865-
PrevRecord.InLifetimeExtendingContext;
7866-
}
7867-
}
7868-
78697862
DefaultedComparisonKind getDefaultedComparisonKind(const FunctionDecl *FD) {
78707863
return getDefaultedFunctionKind(FD).asComparison();
78717864
}

clang/lib/Parse/ParseDecl.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2509,8 +2509,9 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
25092509

25102510
// P2718R0 - Lifetime extension in range-based for loops.
25112511
if (getLangOpts().CPlusPlus23) {
2512-
auto &LastRecord = Actions.ExprEvalContexts.back();
2512+
auto &LastRecord = Actions.currentEvaluationContext();
25132513
LastRecord.InLifetimeExtendingContext = true;
2514+
LastRecord.RebuildDefaultArgOrDefaultInit = true;
25142515
}
25152516

25162517
if (getLangOpts().OpenMP)

clang/lib/Sema/CheckExprLifetime.cpp

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -896,11 +896,6 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
896896
enum PathLifetimeKind {
897897
/// Lifetime-extend along this path.
898898
Extend,
899-
/// We should lifetime-extend, but we don't because (due to technical
900-
/// limitations) we can't. This happens for default member initializers,
901-
/// which we don't clone for every use, so we don't have a unique
902-
/// MaterializeTemporaryExpr to update.
903-
ShouldExtend,
904899
/// Do not lifetime extend along this path.
905900
NoExtend
906901
};
@@ -912,7 +907,7 @@ shouldLifetimeExtendThroughPath(const IndirectLocalPath &Path) {
912907
PathLifetimeKind Kind = PathLifetimeKind::Extend;
913908
for (auto Elem : Path) {
914909
if (Elem.Kind == IndirectLocalPathEntry::DefaultInit)
915-
Kind = PathLifetimeKind::ShouldExtend;
910+
return PathLifetimeKind::Extend;
916911
else if (Elem.Kind != IndirectLocalPathEntry::LambdaCaptureInit)
917912
return PathLifetimeKind::NoExtend;
918913
}
@@ -1058,17 +1053,6 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
10581053
// Also visit the temporaries lifetime-extended by this initializer.
10591054
return true;
10601055

1061-
case PathLifetimeKind::ShouldExtend:
1062-
// We're supposed to lifetime-extend the temporary along this path (per
1063-
// the resolution of DR1815), but we don't support that yet.
1064-
//
1065-
// FIXME: Properly handle this situation. Perhaps the easiest approach
1066-
// would be to clone the initializer expression on each use that would
1067-
// lifetime extend its temporaries.
1068-
SemaRef.Diag(DiagLoc, diag::warn_unsupported_lifetime_extension)
1069-
<< RK << DiagRange;
1070-
break;
1071-
10721056
case PathLifetimeKind::NoExtend:
10731057
// If the path goes through the initialization of a variable or field,
10741058
// it can't possibly reach a temporary created in this full-expression.

clang/lib/Sema/SemaExpr.cpp

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5429,6 +5429,8 @@ struct EnsureImmediateInvocationInDefaultArgs
54295429
EnsureImmediateInvocationInDefaultArgs(Sema &SemaRef)
54305430
: TreeTransform(SemaRef) {}
54315431

5432+
bool AlwaysRebuild() { return true; }
5433+
54325434
// Lambda can only have immediate invocations in the default
54335435
// args of their parameters, which is transformed upon calling the closure.
54345436
// The body is not a subexpression, so we have nothing to do.
@@ -5470,7 +5472,7 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
54705472
assert(Param->hasDefaultArg() && "can't build nonexistent default arg");
54715473

54725474
bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
5473-
bool InLifetimeExtendingContext = isInLifetimeExtendingContext();
5475+
bool NeedRebuild = needsRebuildOfDefaultArgOrInit();
54745476
std::optional<ExpressionEvaluationContextRecord::InitializationContext>
54755477
InitializationContext =
54765478
OutermostDeclarationWithDelayedImmediateInvocations();
@@ -5506,13 +5508,15 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
55065508

55075509
// Rewrite the call argument that was created from the corresponding
55085510
// parameter's default argument.
5509-
if (V.HasImmediateCalls || InLifetimeExtendingContext) {
5511+
if (V.HasImmediateCalls ||
5512+
(NeedRebuild && isa_and_present<ExprWithCleanups>(Param->getInit()))) {
55105513
if (V.HasImmediateCalls)
55115514
ExprEvalContexts.back().DelayedDefaultInitializationContext = {
55125515
CallLoc, Param, CurContext};
55135516
// Pass down lifetime extending flag, and collect temporaries in
55145517
// CreateMaterializeTemporaryExpr when we rewrite the call argument.
5515-
keepInLifetimeExtendingContext();
5518+
currentEvaluationContext().InLifetimeExtendingContext =
5519+
parentEvaluationContext().InLifetimeExtendingContext;
55165520
EnsureImmediateInvocationInDefaultArgs Immediate(*this);
55175521
ExprResult Res;
55185522
runWithSufficientStackSpace(CallLoc, [&] {
@@ -5558,7 +5562,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
55585562
Expr *Init = nullptr;
55595563

55605564
bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
5561-
5565+
bool NeedRebuild = needsRebuildOfDefaultArgOrInit();
55625566
EnterExpressionEvaluationContext EvalContext(
55635567
*this, ExpressionEvaluationContext::PotentiallyEvaluated, Field);
55645568

@@ -5593,12 +5597,27 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
55935597
ImmediateCallVisitor V(getASTContext());
55945598
if (!NestedDefaultChecking)
55955599
V.TraverseDecl(Field);
5596-
if (V.HasImmediateCalls) {
5600+
5601+
// CWG1815
5602+
// Support lifetime extension of temporary created by aggregate
5603+
// initialization using a default member initializer. We should rebuild
5604+
// the initializer in a lifetime extension context if the initializer
5605+
// expression is an ExprWithCleanups. Then make sure the normal lifetime
5606+
// extension code recurses into the default initializer and does lifetime
5607+
// extension when warranted.
5608+
bool ContainsAnyTemporaries =
5609+
isa_and_present<ExprWithCleanups>(Field->getInClassInitializer());
5610+
if (Field->getInClassInitializer() &&
5611+
!Field->getInClassInitializer()->containsErrors() &&
5612+
(V.HasImmediateCalls || (NeedRebuild && ContainsAnyTemporaries))) {
55975613
ExprEvalContexts.back().DelayedDefaultInitializationContext = {Loc, Field,
55985614
CurContext};
55995615
ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer =
56005616
NestedDefaultChecking;
5601-
5617+
// Pass down lifetime extending flag, and collect temporaries in
5618+
// CreateMaterializeTemporaryExpr when we rewrite the call argument.
5619+
currentEvaluationContext().InLifetimeExtendingContext =
5620+
parentEvaluationContext().InLifetimeExtendingContext;
56025621
EnsureImmediateInvocationInDefaultArgs Immediate(*this);
56035622
ExprResult Res;
56045623
runWithSufficientStackSpace(Loc, [&] {
@@ -17675,11 +17694,10 @@ void Sema::PopExpressionEvaluationContext() {
1767517694

1767617695
// Append the collected materialized temporaries into previous context before
1767717696
// exit if the previous also is a lifetime extending context.
17678-
auto &PrevRecord = parentEvaluationContext();
1767917697
if (getLangOpts().CPlusPlus23 && Rec.InLifetimeExtendingContext &&
17680-
PrevRecord.InLifetimeExtendingContext &&
17698+
parentEvaluationContext().InLifetimeExtendingContext &&
1768117699
!Rec.ForRangeLifetimeExtendTemps.empty()) {
17682-
PrevRecord.ForRangeLifetimeExtendTemps.append(
17700+
parentEvaluationContext().ForRangeLifetimeExtendTemps.append(
1768317701
Rec.ForRangeLifetimeExtendTemps);
1768417702
}
1768517703

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1540,9 +1540,6 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo,
15401540
bool ListInitialization) {
15411541
QualType Ty = TInfo->getType();
15421542
SourceLocation TyBeginLoc = TInfo->getTypeLoc().getBeginLoc();
1543-
1544-
assert((!ListInitialization || Exprs.size() == 1) &&
1545-
"List initialization must have exactly one expression.");
15461543
SourceRange FullRange = SourceRange(TyBeginLoc, RParenOrBraceLoc);
15471544

15481545
InitializedEntity Entity =

clang/lib/Sema/SemaInit.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -750,8 +750,21 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field,
750750
if (Field->hasInClassInitializer()) {
751751
if (VerifyOnly)
752752
return;
753-
754-
ExprResult DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field);
753+
ExprResult DIE;
754+
{
755+
// Enter a default initializer rebuild context, then we can support
756+
// lifetime extension of temporary created by aggregate initialization
757+
// using a default member initializer.
758+
// CWG1815 (https://wg21.link/CWG1815).
759+
EnterExpressionEvaluationContext RebuildDefaultInit(
760+
SemaRef, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
761+
SemaRef.currentEvaluationContext().RebuildDefaultArgOrDefaultInit =
762+
true;
763+
SemaRef.currentEvaluationContext().DelayedDefaultInitializationContext =
764+
SemaRef.parentEvaluationContext()
765+
.DelayedDefaultInitializationContext;
766+
DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field);
767+
}
755768
if (DIE.isInvalid()) {
756769
hadError = true;
757770
return;
@@ -7521,10 +7534,8 @@ Sema::CreateMaterializeTemporaryExpr(QualType T, Expr *Temporary,
75217534
// are done in both CreateMaterializeTemporaryExpr and MaybeBindToTemporary,
75227535
// but there may be a chance to merge them.
75237536
Cleanup.setExprNeedsCleanups(false);
7524-
if (isInLifetimeExtendingContext()) {
7525-
auto &Record = ExprEvalContexts.back();
7526-
Record.ForRangeLifetimeExtendTemps.push_back(MTE);
7527-
}
7537+
if (isInLifetimeExtendingContext())
7538+
currentEvaluationContext().ForRangeLifetimeExtendTemps.push_back(MTE);
75287539
return MTE;
75297540
}
75307541

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5481,7 +5481,10 @@ void Sema::InstantiateVariableInitializer(
54815481
EnterExpressionEvaluationContext Evaluated(
54825482
*this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, Var);
54835483

5484-
keepInLifetimeExtendingContext();
5484+
currentEvaluationContext().InLifetimeExtendingContext =
5485+
parentEvaluationContext().InLifetimeExtendingContext;
5486+
currentEvaluationContext().RebuildDefaultArgOrDefaultInit =
5487+
parentEvaluationContext().RebuildDefaultArgOrDefaultInit;
54855488
// Instantiate the initializer.
54865489
ExprResult Init;
54875490

clang/lib/Sema/TreeTransform.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4254,7 +4254,10 @@ ExprResult TreeTransform<Derived>::TransformInitializer(Expr *Init,
42544254
getSema(), EnterExpressionEvaluationContext::InitList,
42554255
Construct->isListInitialization());
42564256

4257-
getSema().keepInLifetimeExtendingContext();
4257+
getSema().currentEvaluationContext().InLifetimeExtendingContext =
4258+
getSema().parentEvaluationContext().InLifetimeExtendingContext;
4259+
getSema().currentEvaluationContext().RebuildDefaultArgOrDefaultInit =
4260+
getSema().parentEvaluationContext().RebuildDefaultArgOrDefaultInit;
42584261
SmallVector<Expr*, 8> NewArgs;
42594262
bool ArgChanged = false;
42604263
if (getDerived().TransformExprs(Construct->getArgs(), Construct->getNumArgs(),
@@ -8924,8 +8927,9 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
89248927

89258928
// P2718R0 - Lifetime extension in range-based for loops.
89268929
if (getSema().getLangOpts().CPlusPlus23) {
8927-
auto &LastRecord = getSema().ExprEvalContexts.back();
8930+
auto &LastRecord = getSema().currentEvaluationContext();
89288931
LastRecord.InLifetimeExtendingContext = true;
8932+
LastRecord.RebuildDefaultArgOrDefaultInit = true;
89298933
}
89308934
StmtResult Init =
89318935
S->getInit() ? getDerived().TransformStmt(S->getInit()) : StmtResult();
@@ -14443,6 +14447,13 @@ TreeTransform<Derived>::TransformCXXTemporaryObjectExpr(
1444314447
if (TransformExprs(E->getArgs(), E->getNumArgs(), true, Args,
1444414448
&ArgumentChanged))
1444514449
return ExprError();
14450+
14451+
if (E->isListInitialization() && !E->isStdInitListInitialization()) {
14452+
ExprResult Res = RebuildInitList(E->getBeginLoc(), Args, E->getEndLoc());
14453+
if (Res.isInvalid())
14454+
return ExprError();
14455+
Args = {Res.get()};
14456+
}
1444614457
}
1444714458

1444814459
if (!getDerived().AlwaysRebuild() &&
@@ -14454,12 +14465,9 @@ TreeTransform<Derived>::TransformCXXTemporaryObjectExpr(
1445414465
return SemaRef.MaybeBindToTemporary(E);
1445514466
}
1445614467

14457-
// FIXME: We should just pass E->isListInitialization(), but we're not
14458-
// prepared to handle list-initialization without a child InitListExpr.
1445914468
SourceLocation LParenLoc = T->getTypeLoc().getEndLoc();
1446014469
return getDerived().RebuildCXXTemporaryObjectExpr(
14461-
T, LParenLoc, Args, E->getEndLoc(),
14462-
/*ListInitialization=*/LParenLoc.isInvalid());
14470+
T, LParenLoc, Args, E->getEndLoc(), E->isListInitialization());
1446314471
}
1446414472

1446514473
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: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,27 @@ namespace cwg1696 { // cwg1696: 7
449449
// since-cxx14-note@-2 {{default member initializer declared here}}
450450
};
451451
A a{a, a};
452+
453+
struct A1 {
454+
A1() : v(42) {}
455+
// since-cxx14-error@-1 {{reference member 'v' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
456+
// since-cxx14-note@#cwg1696-A1 {{reference member declared here}}
457+
const int &v; // #cwg1696-A1
458+
};
459+
460+
struct A2 {
461+
A2() = default;
462+
// since-cxx14-error@-1 {{reference member 'v' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}}
463+
// since-cxx14-note-re@#cwg1696-A2-b {{in defaulted default constructor for {{.*}} first required here}}
464+
// since-cxx14-note@#cwg1696-A2-a {{initializing field 'v' with default member initializer}}
465+
A2(int v) : v(v) {}
466+
// since-cxx14-warning@-1 {{binding reference member 'v' to stack allocated parameter 'v'}}
467+
// since-cxx14-note@#cwg1696-A2-a {{reference member declared here}}
468+
const int &v = 42; // #cwg1696-A2-a
469+
};
470+
A2 a1; // #cwg1696-A2-b
471+
472+
A2 a2(1); // OK, unfortunately
452473
#endif
453474
}
454475

@@ -483,8 +504,6 @@ namespace cwg1696 { // cwg1696: 7
483504
const A &a = A(); // #cwg1696-D1-a
484505
};
485506
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}}
488507

489508
struct D2 {
490509
const A &a = A(); // #cwg1696-D2-a

0 commit comments

Comments
 (0)