https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/86960
>From e5f2bfb1652118be0112b8242263942063b75b89 Mon Sep 17 00:00:00 2001 From: yronglin <yronglin...@gmail.com> Date: Tue, 2 Apr 2024 23:47:33 +0800 Subject: [PATCH] [Clang] Extend lifetime of temporaries in mem-default-init for P2718R0 Signed-off-by: yronglin <yronglin...@gmail.com> --- clang/include/clang/Sema/Sema.h | 47 -------- clang/lib/CodeGen/CGExpr.cpp | 62 +++++----- clang/lib/CodeGen/CGStmt.cpp | 15 +++ clang/lib/CodeGen/CodeGenFunction.h | 31 +++++ clang/lib/Parse/ParseDecl.cpp | 4 - clang/lib/Sema/SemaExpr.cpp | 11 +- clang/lib/Sema/SemaExprCXX.cpp | 2 +- clang/lib/Sema/SemaInit.cpp | 36 +++--- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 1 - clang/lib/Sema/TreeTransform.h | 4 - clang/test/CXX/drs/dr16xx.cpp | 3 +- clang/test/CXX/drs/dr18xx.cpp | 7 +- clang/test/CXX/special/class.temporary/p6.cpp | 106 ++++++++++++++++++ clang/test/SemaCXX/constexpr-default-arg.cpp | 4 +- clang/test/SemaCXX/eval-crashes.cpp | 6 +- 15 files changed, 222 insertions(+), 117 deletions(-) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index f49bc724c96c89..0a07e850c74e47 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5078,34 +5078,6 @@ class Sema final : public SemaBase { /// example, in a for-range initializer). bool InLifetimeExtendingContext = false; - /// Whether we are currently in a context in which all temporaries must be - /// materialized. - /// - /// [class.temporary]/p2: - /// The materialization of a temporary object is generally delayed as long - /// as possible in order to avoid creating unnecessary temporary objects. - /// - /// Temporary objects are materialized: - /// (2.1) when binding a reference to a prvalue ([dcl.init.ref], - /// [expr.type.conv], [expr.dynamic.cast], [expr.static.cast], - /// [expr.const.cast], [expr.cast]), - /// - /// (2.2) when performing member access on a class prvalue ([expr.ref], - /// [expr.mptr.oper]), - /// - /// (2.3) when performing an array-to-pointer conversion or subscripting - /// on an array prvalue ([conv.array], [expr.sub]), - /// - /// (2.4) when initializing an object of type - /// std::initializer_list<T> from a braced-init-list - /// ([dcl.init.list]), - /// - /// (2.5) for certain unevaluated operands ([expr.typeid], [expr.sizeof]) - /// - /// (2.6) when a prvalue that has type other than cv void appears as a - /// discarded-value expression ([expr.context]). - bool InMaterializeTemporaryObjectContext = 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 @@ -6386,19 +6358,6 @@ class Sema final : public SemaBase { } } - /// keepInMaterializeTemporaryObjectContext - Pull down - /// InMaterializeTemporaryObjectContext flag from previous context. - void keepInMaterializeTemporaryObjectContext() { - if (ExprEvalContexts.size() > 2 && - ExprEvalContexts[ExprEvalContexts.size() - 2] - .InMaterializeTemporaryObjectContext) { - auto &LastRecord = ExprEvalContexts.back(); - auto &PrevRecord = ExprEvalContexts[ExprEvalContexts.size() - 2]; - LastRecord.InMaterializeTemporaryObjectContext = - PrevRecord.InMaterializeTemporaryObjectContext; - } - } - DefaultedComparisonKind getDefaultedComparisonKind(const FunctionDecl *FD) { return getDefaultedFunctionKind(FD).asComparison(); } @@ -6542,12 +6501,6 @@ class Sema final : public SemaBase { /// used in initializer of the field. llvm::MapVector<FieldDecl *, DeleteLocs> DeleteExprs; - bool isInMaterializeTemporaryObjectContext() const { - assert(!ExprEvalContexts.empty() && - "Must be in an expression evaluation context"); - return ExprEvalContexts.back().InMaterializeTemporaryObjectContext; - } - ParsedType getInheritingConstructorName(CXXScopeSpec &SS, SourceLocation NameLoc, IdentifierInfo &Name); diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 2480972f1432f7..27f616d497a2c5 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -274,9 +274,9 @@ void CodeGenFunction::EmitAnyExprToMem(const Expr *E, llvm_unreachable("bad evaluation kind"); } -static void -pushTemporaryCleanup(CodeGenFunction &CGF, const MaterializeTemporaryExpr *M, - const Expr *E, Address ReferenceTemporary) { +void CodeGenFunction::pushTemporaryCleanup(const MaterializeTemporaryExpr *M, + const Expr *E, + Address ReferenceTemporary) { // Objective-C++ ARC: // If we are binding a reference to a temporary that has ownership, we // need to perform retain/release operations on the temporary. @@ -311,9 +311,9 @@ pushTemporaryCleanup(CodeGenFunction &CGF, const MaterializeTemporaryExpr *M, CleanupKind CleanupKind; if (Lifetime == Qualifiers::OCL_Strong) { const ValueDecl *VD = M->getExtendingDecl(); - bool Precise = - VD && isa<VarDecl>(VD) && VD->hasAttr<ObjCPreciseLifetimeAttr>(); - CleanupKind = CGF.getARCCleanupKind(); + bool Precise = isa_and_nonnull<VarDecl>(VD) && + VD->hasAttr<ObjCPreciseLifetimeAttr>(); + CleanupKind = getARCCleanupKind(); Destroy = Precise ? &CodeGenFunction::destroyARCStrongPrecise : &CodeGenFunction::destroyARCStrongImprecise; } else { @@ -323,13 +323,12 @@ pushTemporaryCleanup(CodeGenFunction &CGF, const MaterializeTemporaryExpr *M, Destroy = &CodeGenFunction::destroyARCWeak; } if (Duration == SD_FullExpression) - CGF.pushDestroy(CleanupKind, ReferenceTemporary, - M->getType(), *Destroy, - CleanupKind & EHCleanup); + pushDestroy(CleanupKind, ReferenceTemporary, M->getType(), *Destroy, + CleanupKind & EHCleanup); else - CGF.pushLifetimeExtendedDestroy(CleanupKind, ReferenceTemporary, - M->getType(), - *Destroy, CleanupKind & EHCleanup); + pushLifetimeExtendedDestroy(CleanupKind, ReferenceTemporary, + M->getType(), *Destroy, + CleanupKind & EHCleanup); return; case SD_Dynamic: @@ -358,32 +357,31 @@ pushTemporaryCleanup(CodeGenFunction &CGF, const MaterializeTemporaryExpr *M, llvm::FunctionCallee CleanupFn; llvm::Constant *CleanupArg; if (E->getType()->isArrayType()) { - CleanupFn = CodeGenFunction(CGF.CGM).generateDestroyHelper( - ReferenceTemporary, E->getType(), - CodeGenFunction::destroyCXXObject, CGF.getLangOpts().Exceptions, + CleanupFn = CodeGenFunction(CGM).generateDestroyHelper( + ReferenceTemporary, E->getType(), CodeGenFunction::destroyCXXObject, + getLangOpts().Exceptions, dyn_cast_or_null<VarDecl>(M->getExtendingDecl())); - CleanupArg = llvm::Constant::getNullValue(CGF.Int8PtrTy); + CleanupArg = llvm::Constant::getNullValue(Int8PtrTy); } else { - CleanupFn = CGF.CGM.getAddrAndTypeOfCXXStructor( + CleanupFn = CGM.getAddrAndTypeOfCXXStructor( GlobalDecl(ReferenceTemporaryDtor, Dtor_Complete)); - CleanupArg = cast<llvm::Constant>(ReferenceTemporary.emitRawPointer(CGF)); + CleanupArg = + cast<llvm::Constant>(ReferenceTemporary.emitRawPointer(*this)); } - CGF.CGM.getCXXABI().registerGlobalDtor( - CGF, *cast<VarDecl>(M->getExtendingDecl()), CleanupFn, CleanupArg); + CGM.getCXXABI().registerGlobalDtor( + *this, *cast<VarDecl>(M->getExtendingDecl()), CleanupFn, CleanupArg); break; } case SD_FullExpression: - CGF.pushDestroy(NormalAndEHCleanup, ReferenceTemporary, E->getType(), - CodeGenFunction::destroyCXXObject, - CGF.getLangOpts().Exceptions); + pushDestroy(NormalAndEHCleanup, ReferenceTemporary, E->getType(), + CodeGenFunction::destroyCXXObject, getLangOpts().Exceptions); break; case SD_Automatic: - CGF.pushLifetimeExtendedDestroy(NormalAndEHCleanup, - ReferenceTemporary, E->getType(), - CodeGenFunction::destroyCXXObject, - CGF.getLangOpts().Exceptions); + pushLifetimeExtendedDestroy(NormalAndEHCleanup, ReferenceTemporary, + E->getType(), CodeGenFunction::destroyCXXObject, + getLangOpts().Exceptions); break; case SD_Dynamic: @@ -490,7 +488,7 @@ EmitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *M) { } } - pushTemporaryCleanup(*this, M, E, Object); + pushTemporaryCleanup(M, E, Object); return RefTempDst; } @@ -579,7 +577,13 @@ EmitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *M) { } EmitAnyExprToMem(E, Object, Qualifiers(), /*IsInit*/true); } - pushTemporaryCleanup(*this, M, E, Object); + + // If this temporary extended by for-range variable, delay to emitting + // cleanup. + if (CurLexicalScope && CurLexicalScope->isExtendedByForRangeVar(M)) + CurLexicalScope->addForRangeInitTemp(M, Object); + else + pushTemporaryCleanup(M, E, Object); // Perform derived-to-base casts and/or field accesses, to get from the // temporary object we created (and, potentially, for which we extended diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index 576fe2f7a2d46f..75bba56bb89856 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -1230,11 +1230,26 @@ CodeGenFunction::EmitCXXForRangeStmt(const CXXForRangeStmt &S, JumpDest LoopExit = getJumpDestInCurrentScope("for.end"); LexicalScope ForScope(*this, S.getSourceRange()); + const DeclStmt *RangeDS = cast<DeclStmt>(S.getRangeStmt()); + const VarDecl *RangeVar = cast<VarDecl>(RangeDS->getSingleDecl()); + if (getLangOpts().CPlusPlus23) + ForScope.setForRangeVar(RangeVar); // Evaluate the first pieces before the loop. if (S.getInit()) EmitStmt(S.getInit()); EmitStmt(S.getRangeStmt()); + + // Emit cleanup for tempories in for-range-init expression. + if (getLangOpts().CPlusPlus23) { + RunCleanupsScope Scope(*this); + auto LifetimeExtendTemps = ForScope.getForRangeInitTemps(); + for (const auto &Temp : LifetimeExtendTemps) { + auto [M, Object] = Temp; + pushTemporaryCleanup(M, M->getSubExpr(), Object); + } + } + EmitStmt(S.getBeginStmt()); EmitStmt(S.getEndStmt()); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index e2a7e28c8211ea..167ac71fefb722 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -875,6 +875,9 @@ class CodeGenFunction : public CodeGenTypeCache { new (Buffer + sizeof(Header) + sizeof(T)) RawAddress(ActiveFlag); } + void pushTemporaryCleanup(const MaterializeTemporaryExpr *M, const Expr *E, + Address ReferenceTemporary); + /// Set up the last cleanup that was pushed as a conditional /// full-expression cleanup. void initFullExprCleanup() { @@ -982,11 +985,24 @@ class CodeGenFunction : public CodeGenTypeCache { EHScopeStack::stable_iterator CurrentCleanupScopeDepth = EHScopeStack::stable_end(); + struct ForRangeInitLifetimeExtendTemporary { + const MaterializeTemporaryExpr *M; + RawAddress Object; + }; + class LexicalScope : public RunCleanupsScope { SourceRange Range; SmallVector<const LabelDecl*, 4> Labels; + SmallVector<ForRangeInitLifetimeExtendTemporary, 4> ForRangeInitTemps; LexicalScope *ParentScope; + // This will be set to `__range` variable when we emitting a + // CXXForRangeStmt. It was used to check whether we are emitting a + // materialized temporary which in for-range-init and lifetime-extended by + // __range var. If so, the codegen of cleanup for that temporary object + // needs to be delayed. + const VarDecl *ForRangeVar = nullptr; + LexicalScope(const LexicalScope &) = delete; void operator=(const LexicalScope &) = delete; @@ -1004,6 +1020,21 @@ class CodeGenFunction : public CodeGenTypeCache { Labels.push_back(label); } + void addForRangeInitTemp(const MaterializeTemporaryExpr *M, + RawAddress Object) { + assert(PerformCleanup && "adding temps to dead scope?"); + ForRangeInitTemps.push_back({M, Object}); + } + + ArrayRef<ForRangeInitLifetimeExtendTemporary> getForRangeInitTemps() const { + return ForRangeInitTemps; + } + + void setForRangeVar(const VarDecl *Var) { ForRangeVar = Var; } + bool isExtendedByForRangeVar(const MaterializeTemporaryExpr *M) const { + return M && ForRangeVar && M->getExtendingDecl() == ForRangeVar; + } + /// Exit this cleanup scope, emitting any accumulated /// cleanups. ~LexicalScope() { diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 0aa14b0510746b..ae5d2cae06e536 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2379,10 +2379,6 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, if (getLangOpts().CPlusPlus23) { auto &LastRecord = Actions.ExprEvalContexts.back(); LastRecord.InLifetimeExtendingContext = true; - - // Materialize non-`cv void` prvalue temporaries in discarded - // expressions. These materialized temporaries may be lifetime-extented. - LastRecord.InMaterializeTemporaryObjectContext = true; } if (getLangOpts().OpenMP) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index ffe7e44e2387f4..2caf8a3a3c8e83 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -6331,7 +6331,6 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, // Pass down lifetime extending flag, and collect temporaries in // CreateMaterializeTemporaryExpr when we rewrite the call argument. keepInLifetimeExtendingContext(); - keepInMaterializeTemporaryObjectContext(); EnsureImmediateInvocationInDefaultArgs Immediate(*this); ExprResult Res; runWithSufficientStackSpace(CallLoc, [&] { @@ -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,19 +6411,21 @@ 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; 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 db84f181012268..9f13e3827c742d 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -8361,7 +8361,7 @@ ExprResult Sema::IgnoredValueConversions(Expr *E) { // unnecessary temporary objects. If we skip this step, IR generation is // able to synthesize the storage for itself in the aggregate case, and // adding the extra node to the AST is just clutter. - if (isInMaterializeTemporaryObjectContext() && getLangOpts().CPlusPlus17 && + if (isInLifetimeExtendingContext() && getLangOpts().CPlusPlus17 && E->isPRValue() && !E->getType()->isVoidType()) { ExprResult Res = TemporaryMaterializationConversion(E); if (Res.isInvalid()) diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index a75e9925a43146..c2269206988efc 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(); @@ -8194,24 +8216,12 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity, switch (shouldLifetimeExtendThroughPath(Path)) { case PathLifetimeKind::Extend: + case PathLifetimeKind::ShouldExtend: // Update the storage duration of the materialized temporary. - // FIXME: Rebuild the expression instead of mutating it. MTE->setExtendingDecl(ExtendingEntity->getDecl(), 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/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 1cb071e4eb7d1c..15d0b8a69bcd2c 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -5477,7 +5477,6 @@ void Sema::InstantiateVariableInitializer( *this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, Var); keepInLifetimeExtendingContext(); - keepInMaterializeTemporaryObjectContext(); // Instantiate the initializer. ExprResult Init; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 7df352c24e8648..818837064a38b7 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -8749,10 +8749,6 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) { if (getSema().getLangOpts().CPlusPlus23) { auto &LastRecord = getSema().ExprEvalContexts.back(); LastRecord.InLifetimeExtendingContext = true; - - // Materialize non-`cv void` prvalue temporaries in discarded - // expressions. These materialized temporaries may be lifetime-extented. - LastRecord.InMaterializeTemporaryObjectContext = true; } StmtResult Init = S->getInit() ? getDerived().TransformStmt(S->getInit()) : StmtResult(); diff --git a/clang/test/CXX/drs/dr16xx.cpp b/clang/test/CXX/drs/dr16xx.cpp index f4d6c04fb8e073..c316555eec47c5 100644 --- a/clang/test/CXX/drs/dr16xx.cpp +++ b/clang/test/CXX/drs/dr16xx.cpp @@ -484,8 +484,7 @@ 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 e78730e8992cf8..440db2eb6bc93b 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 + 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@#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 5554363cc69abb..ab781c34ac357f 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>; @@ -429,6 +449,45 @@ template void default_arg_dependent_context2<int>(); template void default_arg_dependent_context3<int>(); } // namespace default_arg +namespace default_init { + +template <class T> +struct DepA { + T arr[1]; + ~DepA() {} +}; + +template <class T> +struct DepB { + int x; + const DepA<T> &a = DepA<T>{{0}}; + ~DepB() {} + const int *begin() { return a.arr; } + const int *end() { return &a.arr[1]; } +}; + +template <typename T> +void default_init1_dependent() { + // CHECK-CXX23: void @_ZN7P2718R012default_init23default_init1_dependentINS0_4DepBIiEEEEvv() + // CHECK-CXX23-LABEL: for.cond.cleanup: + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init4DepBIiED1Ev( + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init4DepAIiED1Ev( + for (auto &&x : T{0}) {} +} + +template <typename T> +void default_init2_dependent() { + // CHECK-CXX23: void @_ZN7P2718R012default_init23default_init2_dependentINS0_4DepBIiEEEEvv() + // CHECK-CXX23-LABEL: for.cond.cleanup: + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init4DepBIiED1Ev( + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init4DepAIiED1Ev( + for (auto &&x : T{0}.a.arr) {} +} + +template void default_init1_dependent<DepB<int>>(); +template void default_init2_dependent<DepB<int>>(); +} // namespace default_init + namespace basic { using T = std::list<int>; const T& f1(const T& t) { return t; } @@ -545,5 +604,52 @@ void default_arg3() { for (auto e : C(0, C(0, C(0, C())))) {} } } // namespace default_arg + +namespace default_init { +struct X { + int x; + ~X() {} +}; + +struct Y { + int y; + const X &x = X{1}; + ~Y() {} +}; + +struct A { + int arr[1]; + const Y &y = Y{1}; + ~A() {} +}; + +struct B { + int x; + const A &a = A{{0}}; + ~B() {} + const int *begin() { return a.arr; } + const int *end() { return &a.arr[1]; } +}; + +void default_init1() { + // CHECK-CXX23: void @_ZN7P2718R012default_init13default_init1Ev() + // CHECK-CXX23-LABEL: for.cond.cleanup: + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1BD1Ev( + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1AD1Ev( + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1YD1Ev( + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1XD1Ev( + for (auto &&x : B{0}) {} +} + +void default_init2() { + // CHECK-CXX23: void @_ZN7P2718R012default_init13default_init2Ev() + // CHECK-CXX23-LABEL: for.cond.cleanup: + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1BD1Ev( + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1AD1Ev( + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1YD1Ev( + // CHECK-CXX23-NEXT: call void @_ZN7P2718R012default_init1XD1Ev( + for (auto &&x : B{0}.a.arr) {} +} +} // namespace default_init } // namespace P2718R0 diff --git a/clang/test/SemaCXX/constexpr-default-arg.cpp b/clang/test/SemaCXX/constexpr-default-arg.cpp index 7c883692829548..8510a6ddc80399 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 017df977b26b7b..a06f60f71e9c7e 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 { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits