https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/87933
>From 9fba6da7cb1ffbc7d46b69c6ac0cfd15a89c4b56 Mon Sep 17 00:00:00 2001 From: yronglin <yronglin...@gmail.com> Date: Mon, 8 Apr 2024 01:38:23 +0800 Subject: [PATCH 1/5] [Clang] Support lifetime extension of temporary created by aggregate initialization using a default member initializer Signed-off-by: yronglin <yronglin...@gmail.com> --- .../clang/Basic/DiagnosticSemaKinds.td | 6 --- clang/lib/Sema/SemaExpr.cpp | 16 ++++---- clang/lib/Sema/SemaInit.cpp | 39 +++++++++++++------ .../Analysis/lifetime-extended-regions.cpp | 2 +- clang/test/CXX/drs/dr16xx.cpp | 2 - clang/test/CXX/drs/dr18xx.cpp | 5 +-- clang/test/CXX/special/class.temporary/p6.cpp | 20 ++++++++++ clang/test/SemaCXX/constexpr-default-arg.cpp | 4 +- clang/test/SemaCXX/eval-crashes.cpp | 6 +-- 9 files changed, 62 insertions(+), 38 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index a1dda2d2461c3..ba779e83d2afd 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10010,12 +10010,6 @@ def warn_new_dangling_initializer_list : Warning< "the allocated initializer list}0 " "will be destroyed at the end of the full-expression">, InGroup<DanglingInitializerList>; -def warn_unsupported_lifetime_extension : Warning< - "lifetime extension of " - "%select{temporary|backing array of initializer list}0 created " - "by aggregate initialization using a default member initializer " - "is not yet supported; lifetime of %select{temporary|backing array}0 " - "will end at the end of the full-expression">, InGroup<Dangling>; // For non-floating point, expressions of the form x == x or x != x // should result in a warning, since these always evaluate to a constant. diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 8db4fffeecfe3..b2e0f2a2a6011 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -6338,10 +6338,9 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, Res = Immediate.TransformInitializer(Param->getInit(), /*NotCopy=*/false); }); - if (Res.isInvalid()) - return ExprError(); - Res = ConvertParamDefaultArgument(Param, Res.get(), - Res.get()->getBeginLoc()); + if (Res.isUsable()) + Res = ConvertParamDefaultArgument(Param, Res.get(), + Res.get()->getBeginLoc()); if (Res.isInvalid()) return ExprError(); Init = Res.get(); @@ -6377,7 +6376,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { Expr *Init = nullptr; bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer(); - + bool InLifetimeExtendingContext = isInLifetimeExtendingContext(); EnterExpressionEvaluationContext EvalContext( *this, ExpressionEvaluationContext::PotentiallyEvaluated, Field); @@ -6412,11 +6411,14 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { ImmediateCallVisitor V(getASTContext()); if (!NestedDefaultChecking) V.TraverseDecl(Field); - if (V.HasImmediateCalls) { + if (V.HasImmediateCalls || InLifetimeExtendingContext) { ExprEvalContexts.back().DelayedDefaultInitializationContext = {Loc, Field, CurContext}; ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer = NestedDefaultChecking; + // Pass down lifetime extending flag, and collect temporaries in + // CreateMaterializeTemporaryExpr when we rewrite the call argument. + keepInLifetimeExtendingContext(); EnsureImmediateInvocationInDefaultArgs Immediate(*this); ExprResult Res; @@ -6424,7 +6426,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { Res = Immediate.TransformInitializer(Field->getInClassInitializer(), /*CXXDirectInit=*/false); }); - if (!Res.isInvalid()) + if (Res.isUsable()) Res = ConvertMemberDefaultInitExpression(Field, Res.get(), Loc); if (Res.isInvalid()) { Field->setInvalidDecl(); diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index a75e9925a4314..842412cd674d8 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -710,6 +710,26 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, if (VerifyOnly) return; + // Enter a lifetime extension context, then we can support lifetime + // extension of temporary created by aggregate initialization using a + // default member initializer (DR1815 https://wg21.link/CWG1815). + // + // In a lifetime extension context, BuildCXXDefaultInitExpr will clone the + // initializer expression on each use that would lifetime extend its + // temporaries. + EnterExpressionEvaluationContext LifetimeExtensionContext( + SemaRef, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, + /*LambdaContextDecl=*/nullptr, + Sema::ExpressionEvaluationContextRecord::EK_Other, true); + + // Lifetime extension in default-member-init. + auto &LastRecord = SemaRef.ExprEvalContexts.back(); + + // Just copy previous record, make sure we haven't forget anything. + LastRecord = + SemaRef.ExprEvalContexts[SemaRef.ExprEvalContexts.size() - 2]; + LastRecord.InLifetimeExtendingContext = true; + ExprResult DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field); if (DIE.isInvalid()) { hadError = true; @@ -7699,6 +7719,8 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, // Step into CXXDefaultInitExprs so we can diagnose cases where a // constructor inherits one as an implicit mem-initializer. if (auto *DIE = dyn_cast<CXXDefaultInitExpr>(Init)) { + assert(DIE->hasRewrittenInit() && + "CXXDefaultInitExpr must has rewritten init"); Path.push_back( {IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()}); Init = DIE->getExpr(); @@ -8193,6 +8215,11 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity, } switch (shouldLifetimeExtendThroughPath(Path)) { + case PathLifetimeKind::ShouldExtend: + // We're supposed to lifetime-extend the temporary along this path (per + // the resolution of DR1815), we supported that by clone the initializer + // expression on each use that would lifetime extend its temporaries. + [[fallthrough]]; case PathLifetimeKind::Extend: // Update the storage duration of the materialized temporary. // FIXME: Rebuild the expression instead of mutating it. @@ -8200,18 +8227,6 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity, ExtendingEntity->allocateManglingNumber()); // Also visit the temporaries lifetime-extended by this initializer. return true; - - case PathLifetimeKind::ShouldExtend: - // We're supposed to lifetime-extend the temporary along this path (per - // the resolution of DR1815), but we don't support that yet. - // - // FIXME: Properly handle this situation. Perhaps the easiest approach - // would be to clone the initializer expression on each use that would - // lifetime extend its temporaries. - Diag(DiagLoc, diag::warn_unsupported_lifetime_extension) - << RK << DiagRange; - break; - case PathLifetimeKind::NoExtend: // If the path goes through the initialization of a variable or field, // it can't possibly reach a temporary created in this full-expression. diff --git a/clang/test/Analysis/lifetime-extended-regions.cpp b/clang/test/Analysis/lifetime-extended-regions.cpp index 4e98bd4b0403e..8df9b8a54a7e0 100644 --- a/clang/test/Analysis/lifetime-extended-regions.cpp +++ b/clang/test/Analysis/lifetime-extended-regions.cpp @@ -122,7 +122,7 @@ void aggregateWithReferences() { clang_analyzer_dump(viaReference.ry); // expected-warning-re {{&lifetime_extended_object{Composite, viaReference, S{{[0-9]+}}} }} // clang does not currently implement extending lifetime of object bound to reference members of aggregates, - // that are created from default member initializer (see `warn_unsupported_lifetime_extension` from `-Wdangling`) + // that are created from default member initializer (see `warn_unsupported_lifetime_extension` from `-Wdangling`) (Supported now). RefAggregate defaultInitExtended{i}; // clang-bug does not extend `Composite` clang_analyzer_dump(defaultInitExtended.ry); // expected-warning {{Unknown }} } diff --git a/clang/test/CXX/drs/dr16xx.cpp b/clang/test/CXX/drs/dr16xx.cpp index f4d6c04fb8e07..52002a7d0db00 100644 --- a/clang/test/CXX/drs/dr16xx.cpp +++ b/clang/test/CXX/drs/dr16xx.cpp @@ -484,8 +484,6 @@ namespace dr1696 { // dr1696: 7 const A &a = A(); // #dr1696-D1-a }; D1 d1 = {}; // #dr1696-d1 - // 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}} - // since-cxx14-note@#dr1696-D1-a {{initializing field 'a' with default member initializer}} struct D2 { const A &a = A(); // #dr1696-D2-a diff --git a/clang/test/CXX/drs/dr18xx.cpp b/clang/test/CXX/drs/dr18xx.cpp index e78730e8992cf..52a90ea3a05d2 100644 --- a/clang/test/CXX/drs/dr18xx.cpp +++ b/clang/test/CXX/drs/dr18xx.cpp @@ -206,13 +206,10 @@ namespace dr1814 { // dr1814: yes #endif } -namespace dr1815 { // dr1815: no +namespace dr1815 { // dr1815: yes #if __cplusplus >= 201402L - // FIXME: needs codegen test struct A { int &&r = 0; }; // #dr1815-A A a = {}; - // 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 - // since-cxx14-note@#dr1815-A {{initializing field 'r' with default member initializer}} struct B { int &&r = 0; }; // #dr1815-B // since-cxx14-error@-1 {{reference member 'r' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}} diff --git a/clang/test/CXX/special/class.temporary/p6.cpp b/clang/test/CXX/special/class.temporary/p6.cpp index 5554363cc69ab..002bf3e1c2674 100644 --- a/clang/test/CXX/special/class.temporary/p6.cpp +++ b/clang/test/CXX/special/class.temporary/p6.cpp @@ -269,6 +269,26 @@ void init_capture_init_list() { // CHECK: } } +void check_dr1815() { // dr1815: yes +#if __cplusplus >= 201402L + + struct A { + int &&r = 0; + ~A() {} + }; + + struct B { + A &&a = A{}; + ~B() {} + }; + + // CHECK: void @_Z12check_dr1815v() + // CHECK: call void @_ZZ12check_dr1815vEN1BD1Ev( + // CHECK: call void @_ZZ12check_dr1815vEN1AD1Ev( + B a = {}; +#endif +} + namespace P2718R0 { namespace basic { template <typename E> using T2 = std::list<E>; diff --git a/clang/test/SemaCXX/constexpr-default-arg.cpp b/clang/test/SemaCXX/constexpr-default-arg.cpp index 7c88369282954..8510a6ddc8039 100644 --- a/clang/test/SemaCXX/constexpr-default-arg.cpp +++ b/clang/test/SemaCXX/constexpr-default-arg.cpp @@ -32,8 +32,8 @@ void test_default_arg2() { } // Check that multiple CXXDefaultInitExprs don't cause an assertion failure. -struct A { int &&r = 0; }; // expected-note 2{{default member initializer}} +struct A { int &&r = 0; }; struct B { A x, y; }; -B b = {}; // expected-warning 2{{lifetime extension of temporary created by aggregate initialization using a default member initializer is not yet supported}} +B b = {}; // expected-no-diagnostics } diff --git a/clang/test/SemaCXX/eval-crashes.cpp b/clang/test/SemaCXX/eval-crashes.cpp index 017df977b26b7..a06f60f71e9c7 100644 --- a/clang/test/SemaCXX/eval-crashes.cpp +++ b/clang/test/SemaCXX/eval-crashes.cpp @@ -25,11 +25,9 @@ namespace pr33140_0b { } namespace pr33140_2 { - // FIXME: The declaration of 'b' below should lifetime-extend two int - // temporaries. - struct A { int &&r = 0; }; // expected-note 2{{initializing field 'r' with default member initializer}} + struct A { int &&r = 0; }; struct B { A x, y; }; - B b = {}; // expected-warning 2{{lifetime extension of temporary created by aggregate initialization using a default member initializer is not yet supported}} + B b = {}; } namespace pr33140_3 { >From bf6f9df9f1b06247bfbe92a1c3409f8b4f37ce83 Mon Sep 17 00:00:00 2001 From: yronglin <yronglin...@gmail.com> Date: Mon, 15 Apr 2024 23:20:44 +0800 Subject: [PATCH 2/5] [Clang] Fix tests Signed-off-by: yronglin <yronglin...@gmail.com> --- clang/test/AST/ast-dump-default-init-json.cpp | 6 +++--- clang/test/AST/ast-dump-default-init.cpp | 2 +- clang/test/CXX/drs/dr16xx.cpp | 2 +- clang/test/CXX/drs/dr18xx.cpp | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/clang/test/AST/ast-dump-default-init-json.cpp b/clang/test/AST/ast-dump-default-init-json.cpp index 1058b4e3ea4d9..f4949a9c9eedf 100644 --- a/clang/test/AST/ast-dump-default-init-json.cpp +++ b/clang/test/AST/ast-dump-default-init-json.cpp @@ -789,10 +789,10 @@ void test() { // CHECK-NEXT: "valueCategory": "lvalue", // CHECK-NEXT: "extendingDecl": { // CHECK-NEXT: "id": "0x{{.*}}", -// CHECK-NEXT: "kind": "FieldDecl", -// CHECK-NEXT: "name": "a", +// CHECK-NEXT: "kind": "VarDecl", +// CHECK-NEXT: "name": "b", // CHECK-NEXT: "type": { -// CHECK-NEXT: "qualType": "const A &" +// CHECK-NEXT: "qualType": "B" // CHECK-NEXT: } // CHECK-NEXT: }, // CHECK-NEXT: "storageDuration": "automatic", diff --git a/clang/test/AST/ast-dump-default-init.cpp b/clang/test/AST/ast-dump-default-init.cpp index 9fe945ee6e932..10dbecdb525f2 100644 --- a/clang/test/AST/ast-dump-default-init.cpp +++ b/clang/test/AST/ast-dump-default-init.cpp @@ -13,7 +13,7 @@ void test() { } // CHECK: -CXXDefaultInitExpr 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue has rewritten init // CHECK-NEXT: `-ExprWithCleanups 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue -// CHECK-NEXT: `-MaterializeTemporaryExpr 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue extended by Field 0x{{[^ ]*}} 'a' 'const A &' +// CHECK-NEXT: `-MaterializeTemporaryExpr 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue extended by Var 0x{{[^ ]*}} 'b' 'B' // CHECK-NEXT: `-ImplicitCastExpr 0x{{[^ ]*}} <{{.*}}> 'const A' <NoOp> // CHECK-NEXT: `-CXXFunctionalCastExpr 0x{{[^ ]*}} <{{.*}}> 'A' functional cast to A <NoOp> // CHECK-NEXT: `-InitListExpr 0x{{[^ ]*}} <{{.*}}> 'A' diff --git a/clang/test/CXX/drs/dr16xx.cpp b/clang/test/CXX/drs/dr16xx.cpp index d8e1bbfda95d5..d3c1313fc034a 100644 --- a/clang/test/CXX/drs/dr16xx.cpp +++ b/clang/test/CXX/drs/dr16xx.cpp @@ -483,7 +483,7 @@ namespace cwg1696 { // cwg1696: 7 // cxx11-note@#cwg1696-D1-a {{initializing field 'a' with default member initializer}} const A &a = A(); // #cwg1696-D1-a }; - D1 d1 = {}; // #dr1696-d1 + D1 d1 = {}; // #cwg1696-d1 struct D2 { const A &a = A(); // #cwg1696-D2-a diff --git a/clang/test/CXX/drs/dr18xx.cpp b/clang/test/CXX/drs/dr18xx.cpp index e3df50e8cf34d..6b4b854a63dce 100644 --- a/clang/test/CXX/drs/dr18xx.cpp +++ b/clang/test/CXX/drs/dr18xx.cpp @@ -206,9 +206,9 @@ namespace cwg1814 { // cwg1814: yes #endif } -namespace dr1815 { // dr1815: yes +namespace cwg1815 { // cwg1815: yes #if __cplusplus >= 201402L - struct A { int &&r = 0; }; + struct A { int &&r = 0; }; A a = {}; struct B { int &&r = 0; }; // #cwg1815-B >From 8df75e24f9295f229766ebc02bad3a3cdcc47a38 Mon Sep 17 00:00:00 2001 From: yronglin <yronglin...@gmail.com> Date: Thu, 25 Apr 2024 07:50:18 +0800 Subject: [PATCH 3/5] Update cxx_dr_status.html Signed-off-by: yronglin <yronglin...@gmail.com> --- clang/www/cxx_dr_status.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index 83b71e7c122d1..235f7ed75a21f 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -10698,7 +10698,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> <td><a href="https://cplusplus.github.io/CWG/issues/1815.html">1815</a></td> <td>CD4</td> <td>Lifetime extension in aggregate initialization</td> - <td class="none" align="center">No</td> + <td class="full" align="center">Clang 19</td> </tr> <tr id="1816"> <td><a href="https://cplusplus.github.io/CWG/issues/1816.html">1816</a></td> >From 727ccfe2365344dd6bddb98e32ea957f0d280869 Mon Sep 17 00:00:00 2001 From: yronglin <yronglin...@gmail.com> Date: Wed, 8 May 2024 23:43:00 +0800 Subject: [PATCH 4/5] Address review comments, but static analyzer's test still WIP Signed-off-by: yronglin <yronglin...@gmail.com> --- clang/lib/Sema/SemaExpr.cpp | 17 ++++++++-- clang/lib/Sema/SemaInit.cpp | 34 +------------------ clang/test/CXX/drs/dr18xx.cpp | 12 +++++++ clang/test/CXX/special/class.temporary/p6.cpp | 22 +++++++++--- clang/www/cxx_dr_status.html | 2 +- 5 files changed, 47 insertions(+), 40 deletions(-) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index a79cd598d2acc..bb6c931f34c48 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -6393,7 +6393,18 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { ImmediateCallVisitor V(getASTContext()); if (!NestedDefaultChecking) V.TraverseDecl(Field); - if (V.HasImmediateCalls || InLifetimeExtendingContext) { + + // CWG1815 + // Support lifetime extension of temporary created by aggregate + // initialization using a default member initializer. We should always rebuild + // the initializer if it contains any temporaries (if the initializer + // expression is an ExprWithCleanups). Then make sure the normal lifetime + // extension code recurses into the default initializer and does lifetime + // extension when warranted. + bool ContainsAnyTemporaries = + isa_and_present<ExprWithCleanups>(Field->getInClassInitializer()); + if (V.HasImmediateCalls || InLifetimeExtendingContext || + ContainsAnyTemporaries) { ExprEvalContexts.back().DelayedDefaultInitializationContext = {Loc, Field, CurContext}; ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer = @@ -6401,9 +6412,11 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { // Pass down lifetime extending flag, and collect temporaries in // CreateMaterializeTemporaryExpr when we rewrite the call argument. keepInLifetimeExtendingContext(); - EnsureImmediateInvocationInDefaultArgs Immediate(*this); ExprResult Res; + + // Rebuild CXXDefaultInitExpr might cause diagnostics. + SFINAETrap Trap(*this); runWithSufficientStackSpace(Loc, [&] { Res = Immediate.TransformInitializer(Field->getInClassInitializer(), /*CXXDirectInit=*/false); diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index cc7efedfa4ec7..0e30078fcc741 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -711,26 +711,6 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, if (VerifyOnly) return; - // Enter a lifetime extension context, then we can support lifetime - // extension of temporary created by aggregate initialization using a - // default member initializer (DR1815 https://wg21.link/CWG1815). - // - // In a lifetime extension context, BuildCXXDefaultInitExpr will clone the - // initializer expression on each use that would lifetime extend its - // temporaries. - EnterExpressionEvaluationContext LifetimeExtensionContext( - SemaRef, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, - /*LambdaContextDecl=*/nullptr, - Sema::ExpressionEvaluationContextRecord::EK_Other, true); - - // Lifetime extension in default-member-init. - auto &LastRecord = SemaRef.ExprEvalContexts.back(); - - // Just copy previous record, make sure we haven't forget anything. - LastRecord = - SemaRef.ExprEvalContexts[SemaRef.ExprEvalContexts.size() - 2]; - LastRecord.InLifetimeExtendingContext = true; - ExprResult DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field); if (DIE.isInvalid()) { hadError = true; @@ -7720,8 +7700,6 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, // Step into CXXDefaultInitExprs so we can diagnose cases where a // constructor inherits one as an implicit mem-initializer. if (auto *DIE = dyn_cast<CXXDefaultInitExpr>(Init)) { - assert(DIE->hasRewrittenInit() && - "CXXDefaultInitExpr must has rewritten init"); Path.push_back( {IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()}); Init = DIE->getExpr(); @@ -8087,11 +8065,6 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, enum PathLifetimeKind { /// Lifetime-extend along this path. Extend, - /// We should lifetime-extend, but we don't because (due to technical - /// limitations) we can't. This happens for default member initializers, - /// which we don't clone for every use, so we don't have a unique - /// MaterializeTemporaryExpr to update. - ShouldExtend, /// Do not lifetime extend along this path. NoExtend }; @@ -8103,7 +8076,7 @@ shouldLifetimeExtendThroughPath(const IndirectLocalPath &Path) { PathLifetimeKind Kind = PathLifetimeKind::Extend; for (auto Elem : Path) { if (Elem.Kind == IndirectLocalPathEntry::DefaultInit) - Kind = PathLifetimeKind::ShouldExtend; + Kind = PathLifetimeKind::Extend; else if (Elem.Kind != IndirectLocalPathEntry::LambdaCaptureInit) return PathLifetimeKind::NoExtend; } @@ -8216,11 +8189,6 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity, } switch (shouldLifetimeExtendThroughPath(Path)) { - case PathLifetimeKind::ShouldExtend: - // We're supposed to lifetime-extend the temporary along this path (per - // the resolution of DR1815), we supported that by clone the initializer - // expression on each use that would lifetime extend its temporaries. - [[fallthrough]]; case PathLifetimeKind::Extend: // Update the storage duration of the materialized temporary. // FIXME: Rebuild the expression instead of mutating it. diff --git a/clang/test/CXX/drs/dr18xx.cpp b/clang/test/CXX/drs/dr18xx.cpp index 6b4b854a63dce..a96a06406e8a4 100644 --- a/clang/test/CXX/drs/dr18xx.cpp +++ b/clang/test/CXX/drs/dr18xx.cpp @@ -216,6 +216,18 @@ namespace cwg1815 { // cwg1815: yes // since-cxx14-note@#cwg1815-B {{initializing field 'r' with default member initializer}} // since-cxx14-note@#cwg1815-b {{in implicit default constructor for 'cwg1815::B' first required here}} B b; // #cwg1815-b + +#if __cplusplus >= 201703L + struct C { const int &r = 0; }; + constexpr C c = {}; // OK, since cwg1815 + static_assert(c.r == 0); + + constexpr int f() { + A a = {}; // OK, since cwg1815 + return a.r; + } + static_assert(f() == 0); +#endif #endif } diff --git a/clang/test/CXX/special/class.temporary/p6.cpp b/clang/test/CXX/special/class.temporary/p6.cpp index 002bf3e1c2674..a6d2adfd1fd2c 100644 --- a/clang/test/CXX/special/class.temporary/p6.cpp +++ b/clang/test/CXX/special/class.temporary/p6.cpp @@ -281,11 +281,25 @@ void check_dr1815() { // dr1815: yes A &&a = A{}; ~B() {} }; - - // CHECK: void @_Z12check_dr1815v() - // CHECK: call void @_ZZ12check_dr1815vEN1BD1Ev( - // CHECK: call void @_ZZ12check_dr1815vEN1AD1Ev( B a = {}; + + // CHECK: call {{.*}}block_scope_begin_function + extern void block_scope_begin_function(); + extern void block_scope_end_function(); + block_scope_begin_function(); + { + // CHECK: call void @_ZZ12check_dr1815vEN1BD1Ev + // CHECK: call void @_ZZ12check_dr1815vEN1AD1Ev + B b = {}; + } + // CHECK: call {{.*}}block_scope_end_function + block_scope_end_function(); + + // CHECK: call {{.*}}some_other_function + extern void some_other_function(); + some_other_function(); + // CHECK: call void @_ZZ12check_dr1815vEN1BD1Ev + // CHECK: call void @_ZZ12check_dr1815vEN1AD1Ev #endif } diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index 235f7ed75a21f..ba7a67e4a8480 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -10698,7 +10698,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> <td><a href="https://cplusplus.github.io/CWG/issues/1815.html">1815</a></td> <td>CD4</td> <td>Lifetime extension in aggregate initialization</td> - <td class="full" align="center">Clang 19</td> + <td class="unreleased" align="center">Clang 19</td> </tr> <tr id="1816"> <td><a href="https://cplusplus.github.io/CWG/issues/1816.html">1816</a></td> >From 84b5a946b39f33d6e05de28530e0fec401028107 Mon Sep 17 00:00:00 2001 From: yronglin <yronglin...@gmail.com> Date: Sun, 12 May 2024 14:36:09 +0800 Subject: [PATCH 5/5] Add comments to explain static analyzer need update Signed-off-by: yronglin <yronglin...@gmail.com> --- clang/test/Analysis/lifetime-extended-regions.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/clang/test/Analysis/lifetime-extended-regions.cpp b/clang/test/Analysis/lifetime-extended-regions.cpp index 8df9b8a54a7e0..5a85b9f2a27aa 100644 --- a/clang/test/Analysis/lifetime-extended-regions.cpp +++ b/clang/test/Analysis/lifetime-extended-regions.cpp @@ -120,9 +120,10 @@ void aggregateWithReferences() { clang_analyzer_dump(viaReference); // expected-warning-re {{&lifetime_extended_object{RefAggregate, viaReference, S{{[0-9]+}}} }} clang_analyzer_dump(viaReference.rx); // expected-warning-re {{&lifetime_extended_object{int, viaReference, S{{[0-9]+}}} }} clang_analyzer_dump(viaReference.ry); // expected-warning-re {{&lifetime_extended_object{Composite, viaReference, S{{[0-9]+}}} }} - - // clang does not currently implement extending lifetime of object bound to reference members of aggregates, - // that are created from default member initializer (see `warn_unsupported_lifetime_extension` from `-Wdangling`) (Supported now). + + // FIXME: clang currently support extending lifetime of object bound to reference members of aggregates, + // that are created from default member initializer. But CFG and ExprEngine need to be updated to address this change. + // The following expect warning: {{&lifetime_extended_object{Composite, defaultInitExtended, S{{[0-9]+}}} }} RefAggregate defaultInitExtended{i}; // clang-bug does not extend `Composite` clang_analyzer_dump(defaultInitExtended.ry); // expected-warning {{Unknown }} } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits