Author: Martin Storsjö Date: 2024-09-09T15:09:45+03:00 New Revision: cca54e347ac34912cdfb9983533c61836db135e0
URL: https://github.com/llvm/llvm-project/commit/cca54e347ac34912cdfb9983533c61836db135e0 DIFF: https://github.com/llvm/llvm-project/commit/cca54e347ac34912cdfb9983533c61836db135e0.diff LOG: Revert "Reapply "[Clang][CWG1815] Support lifetime extension of temporary created by aggregate initialization using a default member initializer" (#97308)" This reverts commit 45c8766973bb3bb73dd8d996231e114dcf45df9f and 049512e39d96995cb373a76cf2d009a86eaf3aab. This change triggers failed asserts on inputs like this: struct a { } constexpr b; class c { public: c(a); }; class B { public: using d = int; struct e { enum { f } g; int h; c i; d j{}; }; }; B::e k{B::e::f, int(), b}; Compiled like this: clang -target x86_64-linux-gnu -c repro.cpp clang: ../../clang/lib/CodeGen/CGExpr.cpp:3105: clang::CodeGen::LValue clang::CodeGen::CodeGenFunction::EmitDeclRefLValue(const clang::DeclRefExpr*): Assertion `(ND->isUsed(false) || !isa<VarDecl>(ND) || E->isNonOdrUse() || !E->getLocation().isValid()) && "Should not use decl without marking it used!"' failed. Added: Modified: clang/docs/ReleaseNotes.rst clang/include/clang/Basic/DiagnosticSemaKinds.td clang/include/clang/Sema/Sema.h clang/lib/Parse/ParseDecl.cpp clang/lib/Sema/CheckExprLifetime.cpp clang/lib/Sema/SemaExpr.cpp clang/lib/Sema/SemaExprCXX.cpp clang/lib/Sema/SemaInit.cpp clang/lib/Sema/SemaTemplateInstantiateDecl.cpp clang/lib/Sema/TreeTransform.h clang/test/AST/ast-dump-default-init-json.cpp clang/test/AST/ast-dump-default-init.cpp clang/test/Analysis/lifetime-extended-regions.cpp clang/test/CXX/drs/cwg16xx.cpp clang/test/CXX/drs/cwg18xx.cpp clang/test/CXX/special/class.temporary/p6.cpp clang/test/SemaCXX/constexpr-default-arg.cpp clang/test/SemaCXX/cxx11-default-member-initializers.cpp clang/test/SemaCXX/eval-crashes.cpp clang/www/cxx_dr_status.html Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 07f3544e2324e3..250821a9f9c45c 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -108,9 +108,6 @@ 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 <https://wg21.link/CWG1815>`_. Support lifetime extension - of temporary created by aggregate initialization using a default member - initializer. - Accept C++26 user-defined ``static_assert`` messages in C++11 as an extension. diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 4609d2ec2b7209..58819a64813fce 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10159,6 +10159,13 @@ def warn_dangling_pointer_assignment : Warning< "will be destroyed at the end of the full-expression">, InGroup<DanglingAssignment>; +def warn_unsupported_lifetime_extension : Warning< + "lifetime extension of " + "%select{temporary|backing array of initializer list}0 created " + "by aggregate initialization using a default member initializer " + "is not yet supported; lifetime of %select{temporary|backing array}0 " + "will end at the end of the full-expression">, InGroup<Dangling>; + // For non-floating point, expressions of the form x == x or x != x // should result in a warning, since these always evaluate to a constant. // Array comparisons have similar warnings diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index ac4c11964b126f..68c782a15c6f1b 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -6403,9 +6403,6 @@ 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 @@ -7813,11 +7810,9 @@ class Sema final : public SemaBase { } bool isInLifetimeExtendingContext() const { - return currentEvaluationContext().InLifetimeExtendingContext; - } - - bool needRebuildDefaultArgOrInit() const { - return currentEvaluationContext().RebuildDefaultArgOrDefaultInit; + assert(!ExprEvalContexts.empty() && + "Must be in an expression evaluation context"); + return ExprEvalContexts.back().InLifetimeExtendingContext; } bool isCheckingDefaultArgumentOrInitializer() const { @@ -7859,6 +7854,18 @@ 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 1f56884be392d6..61a1ca3da6bca0 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -2509,9 +2509,8 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS, // P2718R0 - Lifetime extension in range-based for loops. if (getLangOpts().CPlusPlus23) { - auto &LastRecord = Actions.currentEvaluationContext(); + auto &LastRecord = Actions.ExprEvalContexts.back(); LastRecord.InLifetimeExtendingContext = true; - LastRecord.RebuildDefaultArgOrDefaultInit = true; } if (getLangOpts().OpenMP) diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp index f62e18543851c1..f1507ebb9a5068 100644 --- a/clang/lib/Sema/CheckExprLifetime.cpp +++ b/clang/lib/Sema/CheckExprLifetime.cpp @@ -871,6 +871,11 @@ 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 }; @@ -882,7 +887,7 @@ shouldLifetimeExtendThroughPath(const IndirectLocalPath &Path) { PathLifetimeKind Kind = PathLifetimeKind::Extend; for (auto Elem : Path) { if (Elem.Kind == IndirectLocalPathEntry::DefaultInit) - return PathLifetimeKind::Extend; + Kind = PathLifetimeKind::ShouldExtend; else if (Elem.Kind != IndirectLocalPathEntry::LambdaCaptureInit) return PathLifetimeKind::NoExtend; } @@ -1029,6 +1034,17 @@ 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 30e5fe4543bcaa..32dac4440fb82a 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -5429,8 +5429,6 @@ 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. @@ -5472,7 +5470,7 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, assert(Param->hasDefaultArg() && "can't build nonexistent default arg"); bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer(); - bool NeedRebuild = needRebuildDefaultArgOrInit(); + bool InLifetimeExtendingContext = isInLifetimeExtendingContext(); std::optional<ExpressionEvaluationContextRecord::InitializationContext> InitializationContext = OutermostDeclarationWithDelayedImmediateInvocations(); @@ -5508,15 +5506,13 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, // Rewrite the call argument that was created from the corresponding // parameter's default argument. - if (V.HasImmediateCalls || - (NeedRebuild && isa_and_present<ExprWithCleanups>(Param->getInit()))) { + if (V.HasImmediateCalls || InLifetimeExtendingContext) { 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. - currentEvaluationContext().InLifetimeExtendingContext = - parentEvaluationContext().InLifetimeExtendingContext; + keepInLifetimeExtendingContext(); EnsureImmediateInvocationInDefaultArgs Immediate(*this); ExprResult Res; runWithSufficientStackSpace(CallLoc, [&] { @@ -5562,7 +5558,7 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { Expr *Init = nullptr; bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer(); - bool NeedRebuild = needRebuildDefaultArgOrInit(); + EnterExpressionEvaluationContext EvalContext( *this, ExpressionEvaluationContext::PotentiallyEvaluated, Field); @@ -5597,27 +5593,12 @@ ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) { ImmediateCallVisitor V(getASTContext()); if (!NestedDefaultChecking) V.TraverseDecl(Field); - - // 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<ExprWithCleanups>(Field->getInClassInitializer()); - if (Field->getInClassInitializer() && - !Field->getInClassInitializer()->containsErrors() && - (V.HasImmediateCalls || (NeedRebuild && ContainsAnyTemporaries))) { + if (V.HasImmediateCalls) { 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, [&] { @@ -17694,10 +17675,11 @@ 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 && - parentEvaluationContext().InLifetimeExtendingContext && + PrevRecord.InLifetimeExtendingContext && !Rec.ForRangeLifetimeExtendTemps.empty()) { - parentEvaluationContext().ForRangeLifetimeExtendTemps.append( + PrevRecord.ForRangeLifetimeExtendTemps.append( Rec.ForRangeLifetimeExtendTemps); } diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index e086c601107041..14feafd1e6b17f 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -1540,6 +1540,9 @@ 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 de2686e447892e..7dc17187524621 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -750,20 +750,8 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, if (Field->hasInClassInitializer()) { if (VerifyOnly) return; - 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); - } + + ExprResult DIE = SemaRef.BuildCXXDefaultInitExpr(Loc, Field); if (DIE.isInvalid()) { hadError = true; return; @@ -7533,8 +7521,10 @@ 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()) - currentEvaluationContext().ForRangeLifetimeExtendTemps.push_back(MTE); + if (isInLifetimeExtendingContext()) { + auto &Record = ExprEvalContexts.back(); + Record.ForRangeLifetimeExtendTemps.push_back(MTE); + } return MTE; } diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index bb311e38409280..6df412cbb09c83 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -5481,10 +5481,7 @@ void Sema::InstantiateVariableInitializer( EnterExpressionEvaluationContext Evaluated( *this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, Var); - currentEvaluationContext().InLifetimeExtendingContext = - parentEvaluationContext().InLifetimeExtendingContext; - currentEvaluationContext().RebuildDefaultArgOrDefaultInit = - parentEvaluationContext().RebuildDefaultArgOrDefaultInit; + keepInLifetimeExtendingContext(); // Instantiate the initializer. ExprResult Init; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 4bbc024587915c..0daf620b4123e4 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -4254,10 +4254,7 @@ ExprResult TreeTransform<Derived>::TransformInitializer(Expr *Init, getSema(), EnterExpressionEvaluationContext::InitList, Construct->isListInitialization()); - getSema().currentEvaluationContext().InLifetimeExtendingContext = - getSema().parentEvaluationContext().InLifetimeExtendingContext; - getSema().currentEvaluationContext().RebuildDefaultArgOrDefaultInit = - getSema().parentEvaluationContext().RebuildDefaultArgOrDefaultInit; + getSema().keepInLifetimeExtendingContext(); SmallVector<Expr*, 8> NewArgs; bool ArgChanged = false; if (getDerived().TransformExprs(Construct->getArgs(), Construct->getNumArgs(), @@ -8927,9 +8924,8 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) { // P2718R0 - Lifetime extension in range-based for loops. if (getSema().getLangOpts().CPlusPlus23) { - auto &LastRecord = getSema().currentEvaluationContext(); + auto &LastRecord = getSema().ExprEvalContexts.back(); LastRecord.InLifetimeExtendingContext = true; - LastRecord.RebuildDefaultArgOrDefaultInit = true; } StmtResult Init = S->getInit() ? getDerived().TransformStmt(S->getInit()) : StmtResult(); @@ -14447,13 +14443,6 @@ TreeTransform<Derived>::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() && @@ -14465,9 +14454,12 @@ TreeTransform<Derived>::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(), E->isListInitialization()); + T, LParenLoc, Args, E->getEndLoc(), + /*ListInitialization=*/LParenLoc.isInvalid()); } template<typename Derived> diff --git a/clang/test/AST/ast-dump-default-init-json.cpp b/clang/test/AST/ast-dump-default-init-json.cpp index f4949a9c9eedf4..1058b4e3ea4d93 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": "VarDecl", -// CHECK-NEXT: "name": "b", +// CHECK-NEXT: "kind": "FieldDecl", +// CHECK-NEXT: "name": "a", // CHECK-NEXT: "type": { -// CHECK-NEXT: "qualType": "B" +// CHECK-NEXT: "qualType": "const A &" // 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 26864fbf15424d..15b29f04bf21bf 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 Var 0x{{[^ ]*}} 'b' 'B' +// CHECK-NEXT: `-MaterializeTemporaryExpr 0x{{[^ ]*}} <{{.*}}> 'const A' lvalue extended by Field 0x{{[^ ]*}} 'a' 'const A &' // CHECK-NEXT: `-ImplicitCastExpr 0x{{[^ ]*}} <{{.*}}> 'const A' <NoOp> // CHECK-NEXT: `-CXXFunctionalCastExpr 0x{{[^ ]*}} <{{.*}}> 'A' functional cast to A <NoOp> // CHECK-NEXT: `-InitListExpr 0x{{[^ ]*}} <{{.*}}> 'A' diff --git a/clang/test/Analysis/lifetime-extended-regions.cpp b/clang/test/Analysis/lifetime-extended-regions.cpp index 4458ad294af7cb..4e98bd4b0403eb 100644 --- a/clang/test/Analysis/lifetime-extended-regions.cpp +++ b/clang/test/Analysis/lifetime-extended-regions.cpp @@ -120,11 +120,10 @@ void aggregateWithReferences() { clang_analyzer_dump(viaReference); // expected-warning-re {{&lifetime_extended_object{RefAggregate, viaReference, S{{[0-9]+}}} }} clang_analyzer_dump(viaReference.rx); // expected-warning-re {{&lifetime_extended_object{int, viaReference, S{{[0-9]+}}} }} clang_analyzer_dump(viaReference.ry); // expected-warning-re {{&lifetime_extended_object{Composite, viaReference, S{{[0-9]+}}} }} - - // 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 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` 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 95e241f0d03e9b..cf6b45ceabf2cc 100644 --- a/clang/test/CXX/drs/cwg16xx.cpp +++ b/clang/test/CXX/drs/cwg16xx.cpp @@ -449,27 +449,6 @@ 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 } @@ -504,6 +483,8 @@ 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 7f0fb8cf589d48..61b7faa96a9fbb 100644 --- a/clang/test/CXX/drs/cwg18xx.cpp +++ b/clang/test/CXX/drs/cwg18xx.cpp @@ -206,28 +206,19 @@ namespace cwg1814 { // cwg1814: yes #endif } -namespace cwg1815 { // cwg1815: 20 +namespace cwg1815 { // cwg1815: no #if __cplusplus >= 201402L - struct A { int &&r = 0; }; + // FIXME: needs codegen test + struct A { int &&r = 0; }; // #cwg1815-A A a = {}; + // since-cxx14-warning@-1 {{lifetime extension of temporary created by aggregate initialization using a default member initializer is not yet supported; lifetime of temporary will end at the end of the full-expression}} FIXME + // since-cxx14-note@#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 a6d2adfd1fd2c5..5554363cc69abb 100644 --- a/clang/test/CXX/special/class.temporary/p6.cpp +++ b/clang/test/CXX/special/class.temporary/p6.cpp @@ -269,40 +269,6 @@ 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 <typename E> using T2 = std::list<E>; diff --git a/clang/test/SemaCXX/constexpr-default-arg.cpp b/clang/test/SemaCXX/constexpr-default-arg.cpp index 901123bfb359ff..ec9b2927880bdf 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; }; +struct A { int &&r = 0; }; // expected-note 2{{default member initializer}} struct B { A x, y; }; -B b = {}; // expected-no-diagnostics +B b = {}; // expected-warning 2{{lifetime extension of temporary created by aggregate initialization using a default member initializer is not yet supported}} } diff --git a/clang/test/SemaCXX/cxx11-default-member-initializers.cpp b/clang/test/SemaCXX/cxx11-default-member-initializers.cpp index b3f3eb508446b6..dd8e9c6b7fc11f 100644 --- a/clang/test/SemaCXX/cxx11-default-member-initializers.cpp +++ b/clang/test/SemaCXX/cxx11-default-member-initializers.cpp @@ -27,99 +27,6 @@ class MemInit { C m = s; }; -namespace std { -typedef decltype(sizeof(int)) size_t; - -// libc++'s implementation -template <class _E> 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 <typename T, int> class C { -public: - C(std::initializer_list<T>); -}; - -template <typename T> using Ptr = __remove_pointer(T) *; -template <typename T> C(T) -> C<Ptr<T>, sizeof(T)>; - -class A { -public: - template <typename T1, typename T2> T1 *some_func(T2 &&); -}; - -struct B : A { - // Test CXXDefaultInitExpr rebuild issue in - // https://github.com/llvm/llvm-project/pull/87933 - int *ar = some_func<int>(C{some_func<int>(0)}); - B() {} -}; - -int TestBody_got; -template <int> class Vector { -public: - Vector(std::initializer_list<int>); -}; -template <typename... Ts> Vector(Ts...) -> Vector<sizeof...(Ts)>; -class ProgramBuilder { -public: - template <typename T, typename ARGS> int *create(ARGS); -}; - -struct TypeTest : ProgramBuilder { - int *str_f16 = create<int>(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 21e05f19be0caa..0865dafe4bf92a 100644 --- a/clang/test/SemaCXX/eval-crashes.cpp +++ b/clang/test/SemaCXX/eval-crashes.cpp @@ -25,9 +25,11 @@ namespace pr33140_0b { } namespace pr33140_2 { - struct A { int &&r = 0; }; + // 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 B { A x, y; }; - B b = {}; + B b = {}; // expected-warning 2{{lifetime extension of temporary created by aggregate initialization using a default member initializer is not yet supported}} } namespace pr33140_3 { diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index f036fc5add2413..b638f0ff30bcce 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -10717,7 +10717,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2> <td><a href="https://cplusplus.github.io/CWG/issues/1815.html">1815</a></td> <td>CD4</td> <td>Lifetime extension in aggregate initialization</td> - <td class="unreleased" align="center">Clang 20</td> + <td class="none" align="center">No</td> </tr> <tr id="1816"> <td><a href="https://cplusplus.github.io/CWG/issues/1816.html">1816</a></td> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits