Skip to content

Commit 45c8766

Browse files
authored
Reapply "[Clang][CWG1815] Support lifetime extension of temporary created by aggregate initialization using a default member initializer" (#97308)
The PR reapply #92527. Implemented CWG1815 and fixed the bugs mentioned in the comments of #92527 and #87933. The reason why the original PR was reverted was that errors might occur during the rebuild. --------- Signed-off-by: yronglin <[email protected]>
1 parent dec0781 commit 45c8766

20 files changed

+251
-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
@@ -10159,13 +10159,6 @@ def warn_dangling_pointer_assignment : Warning<
1015910159
"will be destroyed at the end of the full-expression">,
1016010160
InGroup<DanglingAssignment>;
1016110161

10162-
def warn_unsupported_lifetime_extension : Warning<
10163-
"lifetime extension of "
10164-
"%select{temporary|backing array of initializer list}0 created "
10165-
"by aggregate initialization using a default member initializer "
10166-
"is not yet supported; lifetime of %select{temporary|backing array}0 "
10167-
"will end at the end of the full-expression">, InGroup<Dangling>;
10168-
1016910162
// For non-floating point, expressions of the form x == x or x != x
1017010163
// should result in a warning, since these always evaluate to a constant.
1017110164
// 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 needRebuildDefaultArgOrInit() 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
@@ -871,11 +871,6 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
871871
enum PathLifetimeKind {
872872
/// Lifetime-extend along this path.
873873
Extend,
874-
/// We should lifetime-extend, but we don't because (due to technical
875-
/// limitations) we can't. This happens for default member initializers,
876-
/// which we don't clone for every use, so we don't have a unique
877-
/// MaterializeTemporaryExpr to update.
878-
ShouldExtend,
879874
/// Do not lifetime extend along this path.
880875
NoExtend
881876
};
@@ -887,7 +882,7 @@ shouldLifetimeExtendThroughPath(const IndirectLocalPath &Path) {
887882
PathLifetimeKind Kind = PathLifetimeKind::Extend;
888883
for (auto Elem : Path) {
889884
if (Elem.Kind == IndirectLocalPathEntry::DefaultInit)
890-
Kind = PathLifetimeKind::ShouldExtend;
885+
return PathLifetimeKind::Extend;
891886
else if (Elem.Kind != IndirectLocalPathEntry::LambdaCaptureInit)
892887
return PathLifetimeKind::NoExtend;
893888
}
@@ -1034,17 +1029,6 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
10341029
// Also visit the temporaries lifetime-extended by this initializer.
10351030
return true;
10361031

1037-
case PathLifetimeKind::ShouldExtend:
1038-
// We're supposed to lifetime-extend the temporary along this path (per
1039-
// the resolution of DR1815), but we don't support that yet.
1040-
//
1041-
// FIXME: Properly handle this situation. Perhaps the easiest approach
1042-
// would be to clone the initializer expression on each use that would
1043-
// lifetime extend its temporaries.
1044-
SemaRef.Diag(DiagLoc, diag::warn_unsupported_lifetime_extension)
1045-
<< RK << DiagRange;
1046-
break;
1047-
10481032
case PathLifetimeKind::NoExtend:
10491033
// If the path goes through the initialization of a variable or field,
10501034
// 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 = needRebuildDefaultArgOrInit();
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 = needRebuildDefaultArgOrInit();
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: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -750,8 +750,20 @@ 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+
// Just copy previous record, make sure we haven't forget anything.
762+
SemaRef.currentEvaluationContext() = SemaRef.parentEvaluationContext();
763+
SemaRef.currentEvaluationContext().RebuildDefaultArgOrDefaultInit =
764+
true;
765+
DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field);
766+
}
755767
if (DIE.isInvalid()) {
756768
hadError = true;
757769
return;
@@ -7521,10 +7533,8 @@ Sema::CreateMaterializeTemporaryExpr(QualType T, Expr *Temporary,
75217533
// are done in both CreateMaterializeTemporaryExpr and MaybeBindToTemporary,
75227534
// but there may be a chance to merge them.
75237535
Cleanup.setExprNeedsCleanups(false);
7524-
if (isInLifetimeExtendingContext()) {
7525-
auto &Record = ExprEvalContexts.back();
7526-
Record.ForRangeLifetimeExtendTemps.push_back(MTE);
7527-
}
7536+
if (isInLifetimeExtendingContext())
7537+
currentEvaluationContext().ForRangeLifetimeExtendTemps.push_back(MTE);
75287538
return MTE;
75297539
}
75307540

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)