diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 2c6c7e083b9c9..9b71c0d5f1637 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -108,6 +108,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++2c Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index edf22b909c4d5..da37c3b791c9f 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10159,13 +10159,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/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 1f7e555d1b871..b6e0123ab1c1d 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -6382,6 +6382,9 @@ class Sema final : public SemaBase { /// example, in a for-range initializer). 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 // default initializer is being evaluated and the location of the call @@ -7802,9 +7805,11 @@ 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; + } + + bool needRebuildDefaultArgOrInit() const { + return currentEvaluationContext().RebuildDefaultArgOrDefaultInit; } bool isCheckingDefaultArgumentOrInitializer() const { @@ -7846,18 +7851,6 @@ class Sema final : public SemaBase { return Res; } - /// keepInLifetimeExtendingContext - Pull down InLifetimeExtendingContext - /// flag from previous context. - void keepInLifetimeExtendingContext() { - if (ExprEvalContexts.size() > 2 && - parentEvaluationContext().InLifetimeExtendingContext) { - auto &LastRecord = ExprEvalContexts.back(); - auto &PrevRecord = parentEvaluationContext(); - LastRecord.InLifetimeExtendingContext = - PrevRecord.InLifetimeExtendingContext; - } - } - DefaultedComparisonKind getDefaultedComparisonKind(const FunctionDecl *FD) { return getDefaultedFunctionKind(FD).asComparison(); } diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 78d729c5ef7d8..1a1652a4157fe 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2484,8 +2484,9 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, // P2718R0 - Lifetime extension in range-based for loops. if (getLangOpts().CPlusPlus23) { - auto &LastRecord = Actions.ExprEvalContexts.back(); + auto &LastRecord = Actions.currentEvaluationContext(); LastRecord.InLifetimeExtendingContext = true; + LastRecord.RebuildDefaultArgOrDefaultInit = true; } if (getLangOpts().OpenMP) diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp index c136255953696..5c92d301955fb 100644 --- a/clang/lib/Sema/CheckExprLifetime.cpp +++ b/clang/lib/Sema/CheckExprLifetime.cpp @@ -895,11 +895,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 }; @@ -911,7 +906,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; } @@ -1043,17 +1038,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 ea57316ad8014..ad5ee44737e30 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -5427,6 +5427,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. @@ -5455,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(); @@ -5491,13 +5493,15 @@ 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}; // 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, [&] { @@ -5543,7 +5547,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { Expr *Init = nullptr; bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer(); - + bool NeedRebuild = needRebuildDefaultArgOrInit(); EnterExpressionEvaluationContext EvalContext( *this, ExpressionEvaluationContext::PotentiallyEvaluated, Field); @@ -5578,12 +5582,27 @@ 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 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 = + isa_and_present(Field->getInClassInitializer()); + if (Field->getInClassInitializer() && + !Field->getInClassInitializer()->containsErrors() && + (V.HasImmediateCalls || (NeedRebuild && 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. + currentEvaluationContext().InLifetimeExtendingContext = + parentEvaluationContext().InLifetimeExtendingContext; EnsureImmediateInvocationInDefaultArgs Immediate(*this); ExprResult Res; runWithSufficientStackSpace(Loc, [&] { @@ -17635,11 +17654,10 @@ 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 && + parentEvaluationContext().InLifetimeExtendingContext && !Rec.ForRangeLifetimeExtendTemps.empty()) { - PrevRecord.ForRangeLifetimeExtendTemps.append( + parentEvaluationContext().ForRangeLifetimeExtendTemps.append( Rec.ForRangeLifetimeExtendTemps); } diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 746c67ff1e979..4230fc7911646 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/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index dcfe3bc80c87a..61b5f5e4f0110 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -750,8 +750,20 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, if (Field->hasInClassInitializer()) { if (VerifyOnly) return; - - ExprResult DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field); + ExprResult DIE; + { + // 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); + // Just copy previous record, make sure we haven't forget anything. + SemaRef.currentEvaluationContext() = SemaRef.parentEvaluationContext(); + SemaRef.currentEvaluationContext().RebuildDefaultArgOrDefaultInit = + true; + DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field); + } if (DIE.isInvalid()) { hadError = true; return; @@ -7475,10 +7487,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 (isInLifetimeExtendingContext()) + currentEvaluationContext().ForRangeLifetimeExtendTemps.push_back(MTE); return MTE; } diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 0e064be239183..0025d071934d9 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -5478,7 +5478,10 @@ void Sema::InstantiateVariableInitializer( EnterExpressionEvaluationContext Evaluated( *this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, Var); - keepInLifetimeExtendingContext(); + 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 62287c2d26375..b346793d9b944 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -4254,7 +4254,10 @@ ExprResult TreeTransform::TransformInitializer(Expr *Init, getSema(), EnterExpressionEvaluationContext::InitList, Construct->isListInitialization()); - getSema().keepInLifetimeExtendingContext(); + getSema().currentEvaluationContext().InLifetimeExtendingContext = + getSema().parentEvaluationContext().InLifetimeExtendingContext; + getSema().currentEvaluationContext().RebuildDefaultArgOrDefaultInit = + getSema().parentEvaluationContext().RebuildDefaultArgOrDefaultInit; SmallVector NewArgs; bool ArgChanged = false; if (getDerived().TransformExprs(Construct->getArgs(), Construct->getNumArgs(), @@ -8893,8 +8896,9 @@ TreeTransform::TransformCXXForRangeStmt(CXXForRangeStmt *S) { // P2718R0 - Lifetime extension in range-based for loops. if (getSema().getLangOpts().CPlusPlus23) { - auto &LastRecord = getSema().ExprEvalContexts.back(); + auto &LastRecord = getSema().currentEvaluationContext(); LastRecord.InLifetimeExtendingContext = true; + LastRecord.RebuildDefaultArgOrDefaultInit = true; } StmtResult Init = S->getInit() ? getDerived().TransformStmt(S->getInit()) : StmtResult(); @@ -14412,6 +14416,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() && @@ -14423,12 +14434,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..95e241f0d03e9 100644 --- a/clang/test/CXX/drs/cwg16xx.cpp +++ b/clang/test/CXX/drs/cwg16xx.cpp @@ -449,6 +449,27 @@ 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 + }; + + 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 } @@ -483,8 +504,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..b3f3eb508446b 100644 --- a/clang/test/SemaCXX/cxx11-default-member-initializers.cpp +++ b/clang/test/SemaCXX/cxx11-default-member-initializers.cpp @@ -27,6 +27,99 @@ 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 +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 // 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 395b5d3bff49a..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 - No + Clang 20 1816