From e6ee9bef17ca8c597f816661f5bdf16eb5a6a982 Mon Sep 17 00:00:00 2001 From: yronglin Date: Wed, 7 Aug 2024 22:55:40 +0800 Subject: [PATCH 01/13] Reapply "[Clang][CWG1815] Support lifetime extension of temporary created by aggregate initialization using a default member initializer" Signed-off-by: yronglin --- .../clang/Basic/DiagnosticSemaKinds.td | 7 -- clang/lib/Sema/CheckExprLifetime.cpp | 18 +---- clang/lib/Sema/SemaExpr.cpp | 31 ++++++-- clang/lib/Sema/SemaExprCXX.cpp | 3 - clang/lib/Sema/TreeTransform.h | 12 ++- clang/test/AST/ast-dump-default-init-json.cpp | 6 +- clang/test/AST/ast-dump-default-init.cpp | 2 +- .../Analysis/lifetime-extended-regions.cpp | 9 ++- clang/test/CXX/drs/cwg16xx.cpp | 2 - clang/test/CXX/drs/cwg18xx.cpp | 19 +++-- clang/test/CXX/special/class.temporary/p6.cpp | 34 +++++++++ clang/test/SemaCXX/constexpr-default-arg.cpp | 4 +- .../cxx11-default-member-initializers.cpp | 74 +++++++++++++++++++ clang/test/SemaCXX/eval-crashes.cpp | 6 +- clang/www/cxx_dr_status.html | 2 +- 15 files changed, 168 insertions(+), 61 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 02dc0ffe41594..67b60f3b9b2b0 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10148,13 +10148,6 @@ def warn_dangling_pointer_assignment : Warning< "will be destroyed at the end of the full-expression">, InGroup; -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; - // 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. // Array comparisons have similar warnings diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp index 7389046eaddde..79cb8b2ffb720 100644 --- a/clang/lib/Sema/CheckExprLifetime.cpp +++ b/clang/lib/Sema/CheckExprLifetime.cpp @@ -912,11 +912,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 }; @@ -928,7 +923,7 @@ shouldLifetimeExtendThroughPath(const IndirectLocalPath &Path) { PathLifetimeKind Kind = PathLifetimeKind::Extend; for (auto Elem : Path) { if (Elem.Kind == IndirectLocalPathEntry::DefaultInit) - Kind = PathLifetimeKind::ShouldExtend; + return PathLifetimeKind::Extend; else if (Elem.Kind != IndirectLocalPathEntry::LambdaCaptureInit) return PathLifetimeKind::NoExtend; } @@ -1060,17 +1055,6 @@ static void checkExprLifetimeImpl(Sema &SemaRef, // 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. - SemaRef.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/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 21c8ae6bad0ea..273bb86478c12 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -5493,10 +5493,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(); @@ -5532,7 +5531,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { Expr *Init = nullptr; bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer(); - + bool InLifetimeExtendingContext = isInLifetimeExtendingContext(); EnterExpressionEvaluationContext EvalContext( *this, ExpressionEvaluationContext::PotentiallyEvaluated, Field); @@ -5567,19 +5566,35 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { ImmediateCallVisitor V(getASTContext()); if (!NestedDefaultChecking) V.TraverseDecl(Field); - if (V.HasImmediateCalls) { + + // 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(Field->getInClassInitializer()); + if (V.HasImmediateCalls || InLifetimeExtendingContext || + ContainsAnyTemporaries) { 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; + + // Rebuild CXXDefaultInitExpr might cause diagnostics. + SFINAETrap Trap(*this); runWithSufficientStackSpace(Loc, [&] { 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/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 1b56b4cabd133..4899716905cdf 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -1539,9 +1539,6 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo, bool ListInitialization) { QualType Ty = TInfo->getType(); SourceLocation TyBeginLoc = TInfo->getTypeLoc().getBeginLoc(); - - assert((!ListInitialization || Exprs.size() == 1) && - "List initialization must have exactly one expression."); SourceRange FullRange = SourceRange(TyBeginLoc, RParenOrBraceLoc); InitializedEntity Entity = diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 8f6f30434af65..7e6e1e811209e 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -14402,6 +14402,13 @@ TreeTransform::TransformCXXTemporaryObjectExpr( if (TransformExprs(E->getArgs(), E->getNumArgs(), true, Args, &ArgumentChanged)) return ExprError(); + + if (E->isListInitialization() && !E->isStdInitListInitialization()) { + ExprResult Res = RebuildInitList(E->getBeginLoc(), Args, E->getEndLoc()); + if (Res.isInvalid()) + return ExprError(); + Args = {Res.get()}; + } } if (!getDerived().AlwaysRebuild() && @@ -14413,12 +14420,9 @@ TreeTransform::TransformCXXTemporaryObjectExpr( return SemaRef.MaybeBindToTemporary(E); } - // FIXME: We should just pass E->isListInitialization(), but we're not - // prepared to handle list-initialization without a child InitListExpr. SourceLocation LParenLoc = T->getTypeLoc().getEndLoc(); return getDerived().RebuildCXXTemporaryObjectExpr( - T, LParenLoc, Args, E->getEndLoc(), - /*ListInitialization=*/LParenLoc.isInvalid()); + T, LParenLoc, Args, E->getEndLoc(), E->isListInitialization()); } template 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 15b29f04bf21b..26864fbf15424 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' // CHECK-NEXT: `-CXXFunctionalCastExpr 0x{{[^ ]*}} <{{.*}}> 'A' functional cast to A // CHECK-NEXT: `-InitListExpr 0x{{[^ ]*}} <{{.*}}> 'A' diff --git a/clang/test/Analysis/lifetime-extended-regions.cpp b/clang/test/Analysis/lifetime-extended-regions.cpp index 4e98bd4b0403e..4458ad294af7c 100644 --- a/clang/test/Analysis/lifetime-extended-regions.cpp +++ b/clang/test/Analysis/lifetime-extended-regions.cpp @@ -120,10 +120,11 @@ 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`) - RefAggregate defaultInitExtended{i}; // clang-bug does not extend `Composite` + + // 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_analyzer_dump(defaultInitExtended.ry); // expected-warning {{Unknown }} } diff --git a/clang/test/CXX/drs/cwg16xx.cpp b/clang/test/CXX/drs/cwg16xx.cpp index cf6b45ceabf2c..82ef871939d2c 100644 --- a/clang/test/CXX/drs/cwg16xx.cpp +++ b/clang/test/CXX/drs/cwg16xx.cpp @@ -483,8 +483,6 @@ namespace cwg1696 { // cwg1696: 7 const A &a = A(); // #cwg1696-D1-a }; D1 d1 = {}; // #cwg1696-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@#cwg1696-D1-a {{initializing field 'a' with default member initializer}} struct D2 { const A &a = A(); // #cwg1696-D2-a diff --git a/clang/test/CXX/drs/cwg18xx.cpp b/clang/test/CXX/drs/cwg18xx.cpp index adfdb738e81c9..042ad6a17fa75 100644 --- a/clang/test/CXX/drs/cwg18xx.cpp +++ b/clang/test/CXX/drs/cwg18xx.cpp @@ -206,19 +206,28 @@ namespace cwg1814 { // cwg1814: yes #endif } -namespace cwg1815 { // cwg1815: no +namespace cwg1815 { // cwg1815: 19 #if __cplusplus >= 201402L - // FIXME: needs codegen test - struct A { int &&r = 0; }; // #cwg1815-A + struct A { int &&r = 0; }; 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@#cwg1815-A {{initializing field 'r' with default member initializer}} struct B { int &&r = 0; }; // #cwg1815-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}} // 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 5554363cc69ab..a6d2adfd1fd2c 100644 --- a/clang/test/CXX/special/class.temporary/p6.cpp +++ b/clang/test/CXX/special/class.temporary/p6.cpp @@ -269,6 +269,40 @@ 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() {} + }; + 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 +} + namespace P2718R0 { namespace basic { template using T2 = std::list; diff --git a/clang/test/SemaCXX/constexpr-default-arg.cpp b/clang/test/SemaCXX/constexpr-default-arg.cpp index ec9b2927880bd..901123bfb359f 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/cxx11-default-member-initializers.cpp b/clang/test/SemaCXX/cxx11-default-member-initializers.cpp index dd8e9c6b7fc11..1ea8b98cd8636 100644 --- a/clang/test/SemaCXX/cxx11-default-member-initializers.cpp +++ b/clang/test/SemaCXX/cxx11-default-member-initializers.cpp @@ -27,6 +27,80 @@ class MemInit { C m = s; }; +namespace std { +typedef decltype(sizeof(int)) size_t; + +// libc++'s implementation +template class initializer_list { + const _E *__begin_; + size_t __size_; + + initializer_list(const _E *__b, size_t __s) : __begin_(__b), __size_(__s) {} + +public: + typedef _E value_type; + typedef const _E &reference; + typedef const _E &const_reference; + typedef size_t size_type; + + typedef const _E *iterator; + typedef const _E *const_iterator; + + initializer_list() : __begin_(nullptr), __size_(0) {} + + size_t size() const { return __size_; } + const _E *begin() const { return __begin_; } + const _E *end() const { return __begin_ + __size_; } +}; +} // namespace std + +#if __cplusplus >= 201703L +namespace test_rebuild { +template class C { +public: + C(std::initializer_list); +}; + +template using Ptr = __remove_pointer(T) *; +template C(T) -> C, sizeof(T)>; + +class A { +public: + template T1 *some_func(T2 &&); +}; + +struct B : A { + // Test CXXDefaultInitExpr rebuild issue in + // https://github.com/llvm/llvm-project/pull/87933 + int *ar = some_func(C{some_func(0)}); + B() {} +}; + +int TestBody_got; +template class Vector { +public: + Vector(std::initializer_list); +}; +template Vector(Ts...) -> Vector; +class ProgramBuilder { +public: + template int *create(ARGS); +}; + +struct TypeTest : ProgramBuilder { + int *str_f16 = create(Vector{0}); + TypeTest() {} +}; +class TypeTest_Element_Test : TypeTest { + void TestBody(); +}; +void TypeTest_Element_Test::TestBody() { + int *expect = str_f16; + &TestBody_got != expect; // expected-warning {{inequality comparison result unused}} +} +} // namespace test_rebuild +#endif // __cplusplus >= 201703L + #if __cplusplus >= 202002L // This test ensures cleanup expressions are correctly produced // in the presence of default member initializers. diff --git a/clang/test/SemaCXX/eval-crashes.cpp b/clang/test/SemaCXX/eval-crashes.cpp index 0865dafe4bf92..21e05f19be0ca 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 { diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index b8b733dbfa716..0476aab0b22ee 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -10705,7 +10705,7 @@

C++ defect report implementation status

1815 CD4 Lifetime extension in aggregate initialization - No + Clang 19 1816 From dbe5cd3087de18de28e5406e3364cf2f4ef132aa Mon Sep 17 00:00:00 2001 From: yronglin Date: Wed, 7 Aug 2024 23:19:14 +0800 Subject: [PATCH 02/13] [Clang] Always rebuild AST nodes when rebuild default-arg/init Signed-off-by: yronglin --- clang/lib/Sema/SemaExpr.cpp | 2 ++ .../cxx11-default-member-initializers.cpp | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 273bb86478c12..b5134650b0167 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -5416,6 +5416,8 @@ struct EnsureImmediateInvocationInDefaultArgs EnsureImmediateInvocationInDefaultArgs(Sema &SemaRef) : TreeTransform(SemaRef) {} + bool AlwaysRebuild() { return true; } + // Lambda can only have immediate invocations in the default // args of their parameters, which is transformed upon calling the closure. // The body is not a subexpression, so we have nothing to do. diff --git a/clang/test/SemaCXX/cxx11-default-member-initializers.cpp b/clang/test/SemaCXX/cxx11-default-member-initializers.cpp index 1ea8b98cd8636..b3f3eb508446b 100644 --- a/clang/test/SemaCXX/cxx11-default-member-initializers.cpp +++ b/clang/test/SemaCXX/cxx11-default-member-initializers.cpp @@ -99,6 +99,25 @@ void TypeTest_Element_Test::TestBody() { &TestBody_got != expect; // expected-warning {{inequality comparison result unused}} } } // namespace test_rebuild +namespace test_rebuild2 { +struct F { + int g; +}; +struct H {}; +struct I { + I(const F &); + I(H); +}; +struct L { + I i = I({.g = 0}); +}; +struct N : L {}; + +void f() { + delete new L; // Ok + delete new N; // Ok +} +} // namespace test_rebuild2 #endif // __cplusplus >= 201703L #if __cplusplus >= 202002L From 58c947b0970a4820b60ffaa3f21ff6f9fd850cf9 Mon Sep 17 00:00:00 2001 From: yronglin Date: Wed, 7 Aug 2024 23:22:56 +0800 Subject: [PATCH 03/13] [Clang] Update ReleaseNotes Signed-off-by: yronglin --- clang/docs/ReleaseNotes.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 978b4ac8ea2e3..326962b42df80 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -78,6 +78,9 @@ C++ Language Changes - Allow single element access of GCC vector/ext_vector_type object to be constant expression. Supports the `V.xyzw` syntax and other tidbits as seen in OpenCL. Selecting multiple elements is left as a future work. +- Implement `CWG1815 `_. Support lifetime extension + of temporary created by aggregate initialization using a default member + initializer. C++17 Feature Support ^^^^^^^^^^^^^^^^^^^^^ From 0a8214c8f38826d0be8a54d5c4a41448484286f1 Mon Sep 17 00:00:00 2001 From: yronglin Date: Mon, 26 Aug 2024 19:53:07 +0800 Subject: [PATCH 04/13] [Clang] Update clang version in cxx_dr_status Signed-off-by: yronglin --- 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 44da3536edbc3..552c21484ddfa 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -10705,7 +10705,7 @@

C++ defect report implementation status

1815 CD4 Lifetime extension in aggregate initialization - Clang 19 + Clang 20 1816 From ad08b40669831904805bb48e9a85b730b7828512 Mon Sep 17 00:00:00 2001 From: yronglin Date: Wed, 28 Aug 2024 00:05:47 +0800 Subject: [PATCH 05/13] [clang] Only rebuild default member initializer in lifetime extension context Signed-off-by: yronglin --- clang/lib/Sema/SemaExpr.cpp | 10 +++++----- clang/lib/Sema/SemaInit.cpp | 29 +++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 3912a93b737c1..4e87e62b2f5d5 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -5583,14 +5583,16 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { // 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 + // the initializer in a lifetime extension context (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(Field->getInClassInitializer()); - if (V.HasImmediateCalls || InLifetimeExtendingContext || - ContainsAnyTemporaries) { + if (Field->getInClassInitializer() && + !Field->getInClassInitializer()->containsErrors() && + (V.HasImmediateCalls || + (InLifetimeExtendingContext && ContainsAnyTemporaries))) { ExprEvalContexts.back().DelayedDefaultInitializationContext = {Loc, Field, CurContext}; ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer = @@ -5601,8 +5603,6 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { 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 dcfe3bc80c87a..9d7db7ebbe231 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -750,8 +750,33 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, if (Field->hasInClassInitializer()) { if (VerifyOnly) return; - - ExprResult DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field); + ExprResult DIE; + { + // 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 and the temporaries which + // actually bound to a reference member should be extended here. + // + // FIXME: In default member initializer, lifetime extension context will + // collect the MaterializedTemporaryExprs which bound to a reference + // member, but we don't need that, can we avoid this collection action? + 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; + DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field); + } if (DIE.isInvalid()) { hadError = true; return; From 9ad4dcb7b5b56bda8dddf9a8f7d298134c0204a7 Mon Sep 17 00:00:00 2001 From: yronglin Date: Mon, 2 Sep 2024 20:56:21 +0800 Subject: [PATCH 06/13] [clang] Address review comments, extend InLifetimeExtendingContext flag Signed-off-by: yronglin --- clang/include/clang/Sema/Sema.h | 30 +++++++++++++++++++++++------- clang/lib/Parse/ParseDecl.cpp | 7 +++---- clang/lib/Sema/SemaExpr.cpp | 9 +++++---- clang/lib/Sema/SemaInit.cpp | 24 +++++++++--------------- clang/lib/Sema/TreeTransform.h | 7 +++---- 5 files changed, 43 insertions(+), 34 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 1f7e555d1b871..9bb50d641f3d5 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -6298,6 +6298,15 @@ class Sema final : public SemaBase { using ImmediateInvocationCandidate = llvm::PointerIntPair; + enum class LifetimeExtendingContext { + None, // Not in a lifetime extending context. + FlagOnly, // A flag indicating whether we are in lifetime extending + // context. + CollectTemp // A flag indicating whether we are in lifetime + // extending context, additional, collects + // temporary variables created in this context. + }; + /// Data structure used to record current or nested /// expression evaluation contexts. struct ExpressionEvaluationContextRecord { @@ -6380,7 +6389,8 @@ class Sema final : public SemaBase { /// Whether we are currently in a context in which all temporaries must be /// lifetime-extended, even if they're not bound to a reference (for /// example, in a for-range initializer). - bool InLifetimeExtendingContext = false; + LifetimeExtendingContext InLifetimeExtendingContext = + LifetimeExtendingContext::None; // When evaluating immediate functions in the initializer of a default // argument or default member initializer, this is the declaration whose @@ -7804,7 +7814,14 @@ class Sema final : public SemaBase { bool isInLifetimeExtendingContext() const { assert(!ExprEvalContexts.empty() && "Must be in an expression evaluation context"); - return ExprEvalContexts.back().InLifetimeExtendingContext; + return currentEvaluationContext().InLifetimeExtendingContext != + LifetimeExtendingContext::None; + } + + LifetimeExtendingContext getLifetimeExtendingContext() const { + assert(!ExprEvalContexts.empty() && + "Must be in an expression evaluation context"); + return currentEvaluationContext().InLifetimeExtendingContext; } bool isCheckingDefaultArgumentOrInitializer() const { @@ -7850,11 +7867,10 @@ class Sema final : public SemaBase { /// flag from previous context. void keepInLifetimeExtendingContext() { if (ExprEvalContexts.size() > 2 && - parentEvaluationContext().InLifetimeExtendingContext) { - auto &LastRecord = ExprEvalContexts.back(); - auto &PrevRecord = parentEvaluationContext(); - LastRecord.InLifetimeExtendingContext = - PrevRecord.InLifetimeExtendingContext; + parentEvaluationContext().InLifetimeExtendingContext != + LifetimeExtendingContext::None) { + currentEvaluationContext().InLifetimeExtendingContext = + parentEvaluationContext().InLifetimeExtendingContext; } } diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 78d729c5ef7d8..80c37a1275be5 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2483,10 +2483,9 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, getLangOpts().CPlusPlus23); // P2718R0 - Lifetime extension in range-based for loops. - if (getLangOpts().CPlusPlus23) { - auto &LastRecord = Actions.ExprEvalContexts.back(); - LastRecord.InLifetimeExtendingContext = true; - } + if (getLangOpts().CPlusPlus23) + Actions.currentEvaluationContext().InLifetimeExtendingContext = + Sema::LifetimeExtendingContext::CollectTemp; if (getLangOpts().OpenMP) Actions.OpenMP().startOpenMPCXXRangeFor(); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 4e87e62b2f5d5..a976a3d71aa4b 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -17652,11 +17652,12 @@ void Sema::PopExpressionEvaluationContext() { // Append the collected materialized temporaries into previous context before // exit if the previous also is a lifetime extending context. - auto &PrevRecord = parentEvaluationContext(); - if (getLangOpts().CPlusPlus23 && Rec.InLifetimeExtendingContext && - PrevRecord.InLifetimeExtendingContext && + if (getLangOpts().CPlusPlus23 && + Rec.InLifetimeExtendingContext == LifetimeExtendingContext::CollectTemp && + parentEvaluationContext().InLifetimeExtendingContext == + LifetimeExtendingContext::CollectTemp && !Rec.ForRangeLifetimeExtendTemps.empty()) { - PrevRecord.ForRangeLifetimeExtendTemps.append( + parentEvaluationContext().ForRangeLifetimeExtendTemps.append( Rec.ForRangeLifetimeExtendTemps); } diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 9d7db7ebbe231..4b0725d655de2 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -752,29 +752,23 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, return; ExprResult DIE; { - // Enter a lifetime extension context, then we can support lifetime + // Enter a lifetime extending context, then we can support lifetime // extension of temporary created by aggregate initialization using a - // default member initializer (DR1815 https://wg21.link/CWG1815). + // default member initializer (CWG1815 https://wg21.link/CWG1815). // // In a lifetime extension context, BuildCXXDefaultInitExpr will clone - // the initializer expression on each use and the temporaries which - // actually bound to a reference member should be extended here. - // - // FIXME: In default member initializer, lifetime extension context will - // collect the MaterializedTemporaryExprs which bound to a reference - // member, but we don't need that, can we avoid this collection action? - EnterExpressionEvaluationContext LifetimeExtensionContext( + // the initializer expression for each use and the temporaries which + // binds to a reference member should be extended here. + EnterExpressionEvaluationContext LifetimeExtendingContext( 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; + // Just copy parent record, make sure we haven't forget anything. + SemaRef.currentEvaluationContext() = SemaRef.parentEvaluationContext(); + SemaRef.currentEvaluationContext().InLifetimeExtendingContext = + Sema::LifetimeExtendingContext::FlagOnly; DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field); } if (DIE.isInvalid()) { diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index e7e77eb4f3310..7ed3934fb1c5e 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -8892,10 +8892,9 @@ TreeTransform::TransformCXXForRangeStmt(CXXForRangeStmt *S) { getSema().getLangOpts().CPlusPlus23); // P2718R0 - Lifetime extension in range-based for loops. - if (getSema().getLangOpts().CPlusPlus23) { - auto &LastRecord = getSema().ExprEvalContexts.back(); - LastRecord.InLifetimeExtendingContext = true; - } + if (getSema().getLangOpts().CPlusPlus23) + getSema().currentEvaluationContext().InLifetimeExtendingContext = + Sema::LifetimeExtendingContext::CollectTemp; StmtResult Init = S->getInit() ? getDerived().TransformStmt(S->getInit()) : StmtResult(); if (Init.isInvalid()) From 6e0c524073d13e5386bb36a3f48f402a73f03009 Mon Sep 17 00:00:00 2001 From: yronglin Date: Mon, 2 Sep 2024 21:06:47 +0800 Subject: [PATCH 07/13] [clang] amend previous commit Signed-off-by: yronglin --- clang/lib/Sema/SemaInit.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 4b0725d655de2..e719d4158a7e0 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -7494,10 +7494,8 @@ Sema::CreateMaterializeTemporaryExpr(QualType T, Expr *Temporary, // are done in both CreateMaterializeTemporaryExpr and MaybeBindToTemporary, // but there may be a chance to merge them. Cleanup.setExprNeedsCleanups(false); - if (isInLifetimeExtendingContext()) { - auto &Record = ExprEvalContexts.back(); - Record.ForRangeLifetimeExtendTemps.push_back(MTE); - } + if (getLifetimeExtendingContext() == LifetimeExtendingContext::CollectTemp) + currentEvaluationContext().ForRangeLifetimeExtendTemps.push_back(MTE); return MTE; } From 077753f368592e0dfc8b8424a4f8bcc398319b80 Mon Sep 17 00:00:00 2001 From: yronglin Date: Fri, 6 Sep 2024 23:53:02 +0800 Subject: [PATCH 08/13] Introduce RebuildDefaultArgOrDefaultInit and add more std examples into test Signed-off-by: yronglin --- clang/include/clang/Sema/Sema.h | 39 ++++++++----------- clang/lib/Parse/ParseDecl.cpp | 8 ++-- clang/lib/Sema/SemaExpr.cpp | 17 ++++---- clang/lib/Sema/SemaInit.cpp | 23 +++++------ .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 1 + clang/lib/Sema/TreeTransform.h | 9 +++-- clang/test/CXX/drs/cwg16xx.cpp | 22 +++++++++++ 7 files changed, 66 insertions(+), 53 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 9bb50d641f3d5..8bafce72e2a36 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -6298,15 +6298,6 @@ class Sema final : public SemaBase { using ImmediateInvocationCandidate = llvm::PointerIntPair; - enum class LifetimeExtendingContext { - None, // Not in a lifetime extending context. - FlagOnly, // A flag indicating whether we are in lifetime extending - // context. - CollectTemp // A flag indicating whether we are in lifetime - // extending context, additional, collects - // temporary variables created in this context. - }; - /// Data structure used to record current or nested /// expression evaluation contexts. struct ExpressionEvaluationContextRecord { @@ -6389,8 +6380,10 @@ class Sema final : public SemaBase { /// Whether we are currently in a context in which all temporaries must be /// lifetime-extended, even if they're not bound to a reference (for /// example, in a for-range initializer). - LifetimeExtendingContext InLifetimeExtendingContext = - LifetimeExtendingContext::None; + bool InLifetimeExtendingContext = false; + + /// Whether we should rebuild CXXDefaultArgExpr and CXXDefaultInitExpr. + bool RebuildDefaultArgOrDefaultInit = false; // When evaluating immediate functions in the initializer of a default // argument or default member initializer, this is the declaration whose @@ -7812,16 +7805,11 @@ class Sema final : public SemaBase { } bool isInLifetimeExtendingContext() const { - assert(!ExprEvalContexts.empty() && - "Must be in an expression evaluation context"); - return currentEvaluationContext().InLifetimeExtendingContext != - LifetimeExtendingContext::None; + return currentEvaluationContext().InLifetimeExtendingContext; } - LifetimeExtendingContext getLifetimeExtendingContext() const { - assert(!ExprEvalContexts.empty() && - "Must be in an expression evaluation context"); - return currentEvaluationContext().InLifetimeExtendingContext; + bool needRebuildDefaultArgOrInit() const { + return currentEvaluationContext().RebuildDefaultArgOrDefaultInit; } bool isCheckingDefaultArgumentOrInitializer() const { @@ -7866,12 +7854,17 @@ class Sema final : public SemaBase { /// keepInLifetimeExtendingContext - Pull down InLifetimeExtendingContext /// flag from previous context. void keepInLifetimeExtendingContext() { - if (ExprEvalContexts.size() > 2 && - parentEvaluationContext().InLifetimeExtendingContext != - LifetimeExtendingContext::None) { + if (ExprEvalContexts.size() > 2) currentEvaluationContext().InLifetimeExtendingContext = parentEvaluationContext().InLifetimeExtendingContext; - } + } + + /// keepInRebuildDefaultArgInitContext - Pull down + /// RebuildDefaultArgOrDefaultInit flag from previous context. + void keepInRebuildDefaultArgOrInitContext() { + if (ExprEvalContexts.size() > 2) + currentEvaluationContext().RebuildDefaultArgOrDefaultInit = + parentEvaluationContext().RebuildDefaultArgOrDefaultInit; } DefaultedComparisonKind getDefaultedComparisonKind(const FunctionDecl *FD) { diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 80c37a1275be5..1a1652a4157fe 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2483,9 +2483,11 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, getLangOpts().CPlusPlus23); // P2718R0 - Lifetime extension in range-based for loops. - if (getLangOpts().CPlusPlus23) - Actions.currentEvaluationContext().InLifetimeExtendingContext = - Sema::LifetimeExtendingContext::CollectTemp; + if (getLangOpts().CPlusPlus23) { + auto &LastRecord = Actions.currentEvaluationContext(); + LastRecord.InLifetimeExtendingContext = true; + LastRecord.RebuildDefaultArgOrDefaultInit = true; + } if (getLangOpts().OpenMP) Actions.OpenMP().startOpenMPCXXRangeFor(); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index a976a3d71aa4b..00fd56a43005f 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -5457,7 +5457,7 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, assert(Param->hasDefaultArg() && "can't build nonexistent default arg"); bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer(); - bool InLifetimeExtendingContext = isInLifetimeExtendingContext(); + bool NeedRebuild = needRebuildDefaultArgOrInit(); std::optional InitializationContext = OutermostDeclarationWithDelayedImmediateInvocations(); @@ -5493,7 +5493,8 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, // Rewrite the call argument that was created from the corresponding // parameter's default argument. - if (V.HasImmediateCalls || InLifetimeExtendingContext) { + if (V.HasImmediateCalls || + (NeedRebuild && isa_and_present(Param->getInit()))) { if (V.HasImmediateCalls) ExprEvalContexts.back().DelayedDefaultInitializationContext = { CallLoc, Param, CurContext}; @@ -5544,10 +5545,9 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { Expr *Init = nullptr; bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer(); - bool InLifetimeExtendingContext = isInLifetimeExtendingContext(); + bool NeedRebuild = needRebuildDefaultArgOrInit(); EnterExpressionEvaluationContext EvalContext( *this, ExpressionEvaluationContext::PotentiallyEvaluated, Field); - if (!Field->getInClassInitializer()) { // Maybe we haven't instantiated the in-class initializer. Go check the // pattern FieldDecl to see if it has one. @@ -5591,8 +5591,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { isa_and_present(Field->getInClassInitializer()); if (Field->getInClassInitializer() && !Field->getInClassInitializer()->containsErrors() && - (V.HasImmediateCalls || - (InLifetimeExtendingContext && ContainsAnyTemporaries))) { + (V.HasImmediateCalls || (NeedRebuild && ContainsAnyTemporaries))) { ExprEvalContexts.back().DelayedDefaultInitializationContext = {Loc, Field, CurContext}; ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer = @@ -17652,10 +17651,8 @@ void Sema::PopExpressionEvaluationContext() { // Append the collected materialized temporaries into previous context before // exit if the previous also is a lifetime extending context. - if (getLangOpts().CPlusPlus23 && - Rec.InLifetimeExtendingContext == LifetimeExtendingContext::CollectTemp && - parentEvaluationContext().InLifetimeExtendingContext == - LifetimeExtendingContext::CollectTemp && + if (getLangOpts().CPlusPlus23 && Rec.InLifetimeExtendingContext && + parentEvaluationContext().InLifetimeExtendingContext && !Rec.ForRangeLifetimeExtendTemps.empty()) { parentEvaluationContext().ForRangeLifetimeExtendTemps.append( Rec.ForRangeLifetimeExtendTemps); diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index e719d4158a7e0..a8b4b7f935f3f 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -752,23 +752,18 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, return; ExprResult DIE; { - // Enter a lifetime extending context, then we can support lifetime - // extension of temporary created by aggregate initialization using a - // default member initializer (CWG1815 https://wg21.link/CWG1815). - // - // In a lifetime extension context, BuildCXXDefaultInitExpr will clone - // the initializer expression for each use and the temporaries which - // binds to a reference member should be extended here. - EnterExpressionEvaluationContext LifetimeExtendingContext( + // Enter a default initializer rebuild context, then we can support + // lifetime extension of temporary created by aggregate initialization + // using a default member initializer. + // CWG1815 (https://wg21.link/CWG1815). + EnterExpressionEvaluationContext RebuildDefaultInit( SemaRef, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, /*LambdaContextDecl=*/nullptr, Sema::ExpressionEvaluationContextRecord::EK_Other, true); - - // Lifetime extension in default-member-init. - // Just copy parent record, make sure we haven't forget anything. + // Just copy previous record, make sure we haven't forget anything. SemaRef.currentEvaluationContext() = SemaRef.parentEvaluationContext(); - SemaRef.currentEvaluationContext().InLifetimeExtendingContext = - Sema::LifetimeExtendingContext::FlagOnly; + SemaRef.currentEvaluationContext().RebuildDefaultArgOrDefaultInit = + true; DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field); } if (DIE.isInvalid()) { @@ -7494,7 +7489,7 @@ Sema::CreateMaterializeTemporaryExpr(QualType T, Expr *Temporary, // are done in both CreateMaterializeTemporaryExpr and MaybeBindToTemporary, // but there may be a chance to merge them. Cleanup.setExprNeedsCleanups(false); - if (getLifetimeExtendingContext() == LifetimeExtendingContext::CollectTemp) + if (isInLifetimeExtendingContext()) currentEvaluationContext().ForRangeLifetimeExtendTemps.push_back(MTE); return MTE; } diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 0e064be239183..10526d056e20c 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -5479,6 +5479,7 @@ void Sema::InstantiateVariableInitializer( *this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, Var); keepInLifetimeExtendingContext(); + keepInRebuildDefaultArgOrInitContext(); // Instantiate the initializer. ExprResult Init; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 7ed3934fb1c5e..da0a73565753c 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -4255,6 +4255,7 @@ ExprResult TreeTransform::TransformInitializer(Expr *Init, Construct->isListInitialization()); getSema().keepInLifetimeExtendingContext(); + getSema().keepInRebuildDefaultArgOrInitContext(); SmallVector NewArgs; bool ArgChanged = false; if (getDerived().TransformExprs(Construct->getArgs(), Construct->getNumArgs(), @@ -8892,9 +8893,11 @@ TreeTransform::TransformCXXForRangeStmt(CXXForRangeStmt *S) { getSema().getLangOpts().CPlusPlus23); // P2718R0 - Lifetime extension in range-based for loops. - if (getSema().getLangOpts().CPlusPlus23) - getSema().currentEvaluationContext().InLifetimeExtendingContext = - Sema::LifetimeExtendingContext::CollectTemp; + if (getSema().getLangOpts().CPlusPlus23) { + auto &LastRecord = getSema().currentEvaluationContext(); + LastRecord.InLifetimeExtendingContext = true; + LastRecord.RebuildDefaultArgOrDefaultInit = true; + } StmtResult Init = S->getInit() ? getDerived().TransformStmt(S->getInit()) : StmtResult(); if (Init.isInvalid()) diff --git a/clang/test/CXX/drs/cwg16xx.cpp b/clang/test/CXX/drs/cwg16xx.cpp index 82ef871939d2c..b1adfea38c1a7 100644 --- a/clang/test/CXX/drs/cwg16xx.cpp +++ b/clang/test/CXX/drs/cwg16xx.cpp @@ -449,6 +449,28 @@ namespace cwg1696 { // cwg1696: 7 // since-cxx14-note@-2 {{default member initializer declared here}} }; A a{a, a}; + + struct A1 { + A1() : v(42) {} + // since-cxx14-error@-1 {{reference member 'v' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}} + // since-cxx14-note@#cwg1696-A1 {{reference member declared here}} + const int &v; // #cwg1696-A1 + }; +#if 1 + struct A2 { + A2() = default; + // since-cxx14-error@-1 {{reference member 'v' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}} + // since-cxx14-note-re@#cwg1696-A2-b {{in defaulted default constructor for {{.*}} first required here}} + // since-cxx14-note@#cwg1696-A2-a {{initializing field 'v' with default member initializer}} + A2(int v) : v(v) {} + // since-cxx14-warning@-1 {{binding reference member 'v' to stack allocated parameter 'v'}} + // since-cxx14-note@#cwg1696-A2-a {{reference member declared here}} + const int &v = 42; // #cwg1696-A2-a + }; + A2 a1; // #cwg1696-A2-b + + A2 a2(1); // OK, unfortunately +#endif #endif } From d3284e65c2729912b6bcbf0a444681b1179030db Mon Sep 17 00:00:00 2001 From: yronglin Date: Sat, 7 Sep 2024 00:10:48 +0800 Subject: [PATCH 09/13] [NFC] Revert unnecessary changes Signed-off-by: yronglin --- clang/lib/Sema/SemaExpr.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 00fd56a43005f..3d33c2ff07132 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -5507,9 +5507,10 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, Res = Immediate.TransformInitializer(Param->getInit(), /*NotCopy=*/false); }); - if (Res.isUsable()) - Res = ConvertParamDefaultArgument(Param, Res.get(), - Res.get()->getBeginLoc()); + if (Res.isInvalid()) + return ExprError(); + Res = ConvertParamDefaultArgument(Param, Res.get(), + Res.get()->getBeginLoc()); if (Res.isInvalid()) return ExprError(); Init = Res.get(); @@ -5548,6 +5549,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { bool NeedRebuild = needRebuildDefaultArgOrInit(); EnterExpressionEvaluationContext EvalContext( *this, ExpressionEvaluationContext::PotentiallyEvaluated, Field); + if (!Field->getInClassInitializer()) { // Maybe we haven't instantiated the in-class initializer. Go check the // pattern FieldDecl to see if it has one. @@ -5582,9 +5584,9 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { // CWG1815 // Support lifetime extension of temporary created by aggregate - // initialization using a default member initializer. We should always rebuild - // the initializer in a lifetime extension context (if the initializer - // expression is an ExprWithCleanups). Then make sure the normal lifetime + // initialization using a default member initializer. We should rebuild + // the initializer in a lifetime extension context 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 = @@ -5606,7 +5608,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { Res = Immediate.TransformInitializer(Field->getInClassInitializer(), /*CXXDirectInit=*/false); }); - if (Res.isUsable()) + if (!Res.isInvalid()) Res = ConvertMemberDefaultInitExpression(Field, Res.get(), Loc); if (Res.isInvalid()) { Field->setInvalidDecl(); From efbf2356660dd63988cc854c5df9af6fb199dc54 Mon Sep 17 00:00:00 2001 From: yronglin Date: Sat, 7 Sep 2024 00:13:38 +0800 Subject: [PATCH 10/13] Revert unnecessary changes Signed-off-by: yronglin --- clang/lib/Sema/SemaExpr.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 3d33c2ff07132..354417f385fa7 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -5603,7 +5603,6 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { keepInLifetimeExtendingContext(); EnsureImmediateInvocationInDefaultArgs Immediate(*this); ExprResult Res; - runWithSufficientStackSpace(Loc, [&] { Res = Immediate.TransformInitializer(Field->getInClassInitializer(), /*CXXDirectInit=*/false); From 3d425bcd88192717026b28d6a0d8abf953a9bd63 Mon Sep 17 00:00:00 2001 From: yronglin Date: Sat, 7 Sep 2024 00:19:55 +0800 Subject: [PATCH 11/13] [NFC] Remove debug code Signed-off-by: yronglin --- clang/test/CXX/drs/cwg16xx.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/test/CXX/drs/cwg16xx.cpp b/clang/test/CXX/drs/cwg16xx.cpp index b1adfea38c1a7..95e241f0d03e9 100644 --- a/clang/test/CXX/drs/cwg16xx.cpp +++ b/clang/test/CXX/drs/cwg16xx.cpp @@ -456,7 +456,7 @@ namespace cwg1696 { // cwg1696: 7 // since-cxx14-note@#cwg1696-A1 {{reference member declared here}} const int &v; // #cwg1696-A1 }; -#if 1 + struct A2 { A2() = default; // since-cxx14-error@-1 {{reference member 'v' binds to a temporary object whose lifetime would be shorter than the lifetime of the constructed object}} @@ -470,7 +470,6 @@ namespace cwg1696 { // cwg1696: 7 A2 a1; // #cwg1696-A2-b A2 a2(1); // OK, unfortunately -#endif #endif } From 8c3c5842c549bcb92fe9fb09ea933d4803a69c85 Mon Sep 17 00:00:00 2001 From: yronglin Date: Sun, 8 Sep 2024 13:51:14 +0800 Subject: [PATCH 12/13] Remove keepInLifetimeExtendingContext/keepInRebuildDefaultArgOrInitContext Signed-off-by: yronglin --- clang/include/clang/Sema/Sema.h | 16 ---------------- clang/lib/Sema/SemaExpr.cpp | 6 ++++-- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 6 ++++-- clang/lib/Sema/TreeTransform.h | 6 ++++-- 4 files changed, 12 insertions(+), 22 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 8bafce72e2a36..b6e0123ab1c1d 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -7851,22 +7851,6 @@ class Sema final : public SemaBase { return Res; } - /// keepInLifetimeExtendingContext - Pull down InLifetimeExtendingContext - /// flag from previous context. - void keepInLifetimeExtendingContext() { - if (ExprEvalContexts.size() > 2) - currentEvaluationContext().InLifetimeExtendingContext = - parentEvaluationContext().InLifetimeExtendingContext; - } - - /// keepInRebuildDefaultArgInitContext - Pull down - /// RebuildDefaultArgOrDefaultInit flag from previous context. - void keepInRebuildDefaultArgOrInitContext() { - if (ExprEvalContexts.size() > 2) - currentEvaluationContext().RebuildDefaultArgOrDefaultInit = - parentEvaluationContext().RebuildDefaultArgOrDefaultInit; - } - DefaultedComparisonKind getDefaultedComparisonKind(const FunctionDecl *FD) { return getDefaultedFunctionKind(FD).asComparison(); } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 354417f385fa7..ad5ee44737e30 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -5500,7 +5500,8 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, CallLoc, Param, CurContext}; // Pass down lifetime extending flag, and collect temporaries in // CreateMaterializeTemporaryExpr when we rewrite the call argument. - keepInLifetimeExtendingContext(); + currentEvaluationContext().InLifetimeExtendingContext = + parentEvaluationContext().InLifetimeExtendingContext; EnsureImmediateInvocationInDefaultArgs Immediate(*this); ExprResult Res; runWithSufficientStackSpace(CallLoc, [&] { @@ -5600,7 +5601,8 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { NestedDefaultChecking; // Pass down lifetime extending flag, and collect temporaries in // CreateMaterializeTemporaryExpr when we rewrite the call argument. - keepInLifetimeExtendingContext(); + currentEvaluationContext().InLifetimeExtendingContext = + parentEvaluationContext().InLifetimeExtendingContext; EnsureImmediateInvocationInDefaultArgs Immediate(*this); ExprResult Res; runWithSufficientStackSpace(Loc, [&] { diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 10526d056e20c..0025d071934d9 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -5478,8 +5478,10 @@ void Sema::InstantiateVariableInitializer( EnterExpressionEvaluationContext Evaluated( *this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, Var); - keepInLifetimeExtendingContext(); - keepInRebuildDefaultArgOrInitContext(); + currentEvaluationContext().InLifetimeExtendingContext = + parentEvaluationContext().InLifetimeExtendingContext; + currentEvaluationContext().RebuildDefaultArgOrDefaultInit = + parentEvaluationContext().RebuildDefaultArgOrDefaultInit; // Instantiate the initializer. ExprResult Init; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index da0a73565753c..b346793d9b944 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -4254,8 +4254,10 @@ ExprResult TreeTransform::TransformInitializer(Expr *Init, getSema(), EnterExpressionEvaluationContext::InitList, Construct->isListInitialization()); - getSema().keepInLifetimeExtendingContext(); - getSema().keepInRebuildDefaultArgOrInitContext(); + getSema().currentEvaluationContext().InLifetimeExtendingContext = + getSema().parentEvaluationContext().InLifetimeExtendingContext; + getSema().currentEvaluationContext().RebuildDefaultArgOrDefaultInit = + getSema().parentEvaluationContext().RebuildDefaultArgOrDefaultInit; SmallVector NewArgs; bool ArgChanged = false; if (getDerived().TransformExprs(Construct->getArgs(), Construct->getNumArgs(), From a765b6d0f62b5e5221dd02aa8ea1699700ca8243 Mon Sep 17 00:00:00 2001 From: yronglin Date: Sun, 8 Sep 2024 13:56:11 +0800 Subject: [PATCH 13/13] Address review comments Signed-off-by: yronglin --- clang/lib/Sema/SemaInit.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index a8b4b7f935f3f..61b5f5e4f0110 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -757,9 +757,7 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, // using a default member initializer. // CWG1815 (https://wg21.link/CWG1815). EnterExpressionEvaluationContext RebuildDefaultInit( - SemaRef, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, - /*LambdaContextDecl=*/nullptr, - Sema::ExpressionEvaluationContextRecord::EK_Other, true); + SemaRef, Sema::ExpressionEvaluationContext::PotentiallyEvaluated); // Just copy previous record, make sure we haven't forget anything. SemaRef.currentEvaluationContext() = SemaRef.parentEvaluationContext(); SemaRef.currentEvaluationContext().RebuildDefaultArgOrDefaultInit =