diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2018-07-17 00:11:41 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2018-07-17 00:11:41 +0000 |
commit | bd8c87930bc3817282e4c040c16a90a66ca7f267 (patch) | |
tree | 29a3c5673d9fcd678f97f311c03732c30fd973cf | |
parent | 3ad62f50a0a775140c6c35308ade2f2caea67ed1 (diff) | |
download | clang-bd8c87930bc3817282e4c040c16a90a66ca7f267.tar.gz |
Restructure checking for, and warning on, lifetime extension.
This change implements C++ DR1696, which makes initialization of a
reference member of a class from a temporary object ill-formed. The
standard wording here is imprecise, but we interpret it as meaning that
any time a mem-initializer would result in lifetime extension, the
program is ill-formed.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@337226 91177308-0d34-0410-b5e6-96231b3b80d8
23 files changed, 483 insertions, 240 deletions
diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index e021cf6975..e27dc0d764 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -272,6 +272,7 @@ def ShiftOpParentheses: DiagGroup<"shift-op-parentheses">; def OverloadedShiftOpParentheses: DiagGroup<"overloaded-shift-op-parentheses">; def DanglingElse: DiagGroup<"dangling-else">; def DanglingField : DiagGroup<"dangling-field">; +def DanglingInitializerList : DiagGroup<"dangling-initializer-list">; def DistributedObjectModifiers : DiagGroup<"distributed-object-modifiers">; def ExpansionToDefined : DiagGroup<"expansion-to-defined">; def FlagEnum : DiagGroup<"flag-enum">; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index c32b5ea07a..acfdd260cd 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2049,10 +2049,6 @@ def err_implied_std_initializer_list_not_found : Error< "not found; include <initializer_list>">; def err_malformed_std_initializer_list : Error< "std::initializer_list must be a class template with a single type parameter">; -def warn_dangling_std_initializer_list : Warning< - "array backing the initializer list will be destroyed at the end of " - "%select{the full-expression|the constructor}0">, - InGroup<DiagGroup<"dangling-initializer-list">>; def err_auto_init_list_from_c : Error< "cannot use __auto_type with initializer list in C">; def err_auto_bitfield : Error< @@ -7844,13 +7840,39 @@ def warn_bind_ref_member_to_parameter : Warning< def warn_init_ptr_member_to_parameter_addr : Warning< "initializing pointer member %0 with the stack address of parameter %1">, InGroup<DanglingField>; -def warn_bind_ref_member_to_temporary : Warning< - "binding reference %select{|subobject of }1member %0 to a temporary value">, - InGroup<DanglingField>; def note_ref_or_ptr_member_declared_here : Note< "%select{reference|pointer}0 member declared here">; -def note_ref_subobject_of_member_declared_here : Note< - "member with reference subobject declared here">; + +def err_bind_ref_member_to_temporary : Error< + "%select{reference|backing array for 'std::initializer_list'}2 " + "%select{|subobject of }1member %0 " + "%select{binds to|is}2 a temporary object " + "whose lifetime would be shorter than the constructed object">; +def note_lifetime_extending_member_declared_here : Note< + "%select{%select{reference|'std::initializer_list'}0 member|" + "member with %select{reference|'std::initializer_list'}0 subobject}1 " + "declared here">; +def warn_new_dangling_reference : Warning< + "temporary bound to reference member of allocated object " + "will be destroyed at the end of the full-expression">, + InGroup<DanglingField>; +def warn_new_dangling_initializer_list : Warning< + "array backing " + "%select{initializer list subobject of the allocated object|" + "the allocated initializer list}0 " + "will be destroyed at the end of the full-expression">, + InGroup<DanglingInitializerList>; +def warn_default_member_init_temporary_not_extended : Warning< + "sorry, lifetime extension of temporary created by aggregate initialization " + "using default member initializer is not supported; lifetime of temporary " + "will end at the end of the full-expression">, InGroup<DanglingField>; +def warn_default_member_init_init_list_not_extended : Warning< + "sorry, lifetime extension of backing array of initializer list " + "created by aggregate initialization using default member initializer " + "is not supported; lifetime of backing array will end at the end of the " + "full-expression">, InGroup<DanglingInitializerList>; +def note_in_default_member_initalizer_here : Note< + "in default member initializer for field %0 used here">; // For non-floating point, expressions of the form x == x or x != x // should result in a warning, since these always evaluate to a constant. diff --git a/include/clang/Sema/Initialization.h b/include/clang/Sema/Initialization.h index 1e6423c99d..774a2c9ff9 100644 --- a/include/clang/Sema/Initialization.h +++ b/include/clang/Sema/Initialization.h @@ -63,7 +63,7 @@ public: /// is being thrown. EK_Exception, - /// The entity being initialized is a non-static data member + /// The entity being initialized is a non-static data member /// subobject. EK_Member, @@ -123,7 +123,7 @@ public: // enum as an index for its first %select. When modifying this list, // that diagnostic text needs to be updated as well. }; - + private: /// The kind of entity being initialized. EntityKind Kind; @@ -158,6 +158,10 @@ private: /// initialization in a copy or move constructor. These can perform array /// copies. bool IsImplicitFieldInit; + + /// When Kind == EK_Member, whether this is the initial initialization + /// check for a default member initializer. + bool IsDefaultMemberInit; }; struct C { @@ -203,7 +207,7 @@ private: /// Create the initialization entity for a variable. InitializedEntity(VarDecl *Var, EntityKind EK = EK_Variable) - : Kind(EK), Type(Var->getType()), Variable{Var, false} {} + : Kind(EK), Type(Var->getType()), Variable{Var, false, false} {} /// Create the initialization entity for the result of a /// function, throwing an object, performing an explicit cast, or @@ -217,9 +221,9 @@ private: /// Create the initialization entity for a member subobject. InitializedEntity(FieldDecl *Member, const InitializedEntity *Parent, - bool Implicit) + bool Implicit, bool DefaultMemberInit) : Kind(EK_Member), Parent(Parent), Type(Member->getType()), - Variable{Member, Implicit} {} + Variable{Member, Implicit, DefaultMemberInit} {} /// Create the initialization entity for an array element. InitializedEntity(ASTContext &Context, unsigned Index, @@ -339,21 +343,27 @@ public: static InitializedEntity InitializeDelegation(QualType Type) { return InitializedEntity(EK_Delegating, SourceLocation(), Type); } - + /// Create the initialization entity for a member subobject. static InitializedEntity InitializeMember(FieldDecl *Member, const InitializedEntity *Parent = nullptr, bool Implicit = false) { - return InitializedEntity(Member, Parent, Implicit); + return InitializedEntity(Member, Parent, Implicit, false); } - + /// Create the initialization entity for a member subobject. static InitializedEntity InitializeMember(IndirectFieldDecl *Member, const InitializedEntity *Parent = nullptr, bool Implicit = false) { - return InitializedEntity(Member->getAnonField(), Parent, Implicit); + return InitializedEntity(Member->getAnonField(), Parent, Implicit, false); + } + + /// Create the initialization entity for a default member initializer. + static InitializedEntity + InitializeMemberFromDefaultMemberInitializer(FieldDecl *Member) { + return InitializedEntity(Member, nullptr, false, true); } /// Create the initialization entity for an array element. @@ -453,6 +463,12 @@ public: return getKind() == EK_Member && Variable.IsImplicitFieldInit; } + /// Is this the default member initializer of a member (specified inside + /// the class definition)? + bool isDefaultMemberInitializer() const { + return getKind() == EK_Member && Variable.IsDefaultMemberInit; + } + /// Determine the location of the 'return' keyword when initializing /// the result of a function call. SourceLocation getReturnLoc() const { diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 2425dff005..f13e423cef 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2582,6 +2582,11 @@ public: NamedDecl *FoundDecl, CXXMethodDecl *Method); + /// Check that the lifetime of the initializer (and its subobjects) is + /// sufficient for initializing the entity, and perform lifetime extension + /// (when permitted) if not. + void checkInitializerLifetime(const InitializedEntity &Entity, Expr *Init); + ExprResult PerformContextuallyConvertToBool(Expr *From); ExprResult PerformContextuallyConvertToObjCPointer(Expr *From); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 63d01c9d85..eda6e16195 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -3621,7 +3621,8 @@ void Sema::ActOnFinishCXXInClassMemberInitializer(Decl *D, ExprResult Init = InitExpr; if (!FD->getType()->isDependentType() && !InitExpr->isTypeDependent()) { - InitializedEntity Entity = InitializedEntity::InitializeMember(FD); + InitializedEntity Entity = + InitializedEntity::InitializeMemberFromDefaultMemberInitializer(FD); InitializationKind Kind = FD->getInClassInitStyle() == ICIS_ListInit ? InitializationKind::CreateDirectList(InitExpr->getLocStart(), @@ -4646,6 +4647,10 @@ static bool CollectFieldInitializer(Sema &SemaRef, BaseAndFieldInfo &Info, SemaRef.BuildCXXDefaultInitExpr(Info.Ctor->getLocation(), Field); if (DIE.isInvalid()) return true; + + auto Entity = InitializedEntity::InitializeMember(Field, nullptr, true); + SemaRef.checkInitializerLifetime(Entity, DIE.get()); + CXXCtorInitializer *Init; if (Indirect) Init = new (SemaRef.Context) diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index fbf572e398..a7ad8e322d 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -1864,13 +1864,6 @@ Sema::BuildCXXNew(SourceRange Range, bool UseGlobal, if (CheckAllocatedType(AllocType, TypeRange.getBegin(), TypeRange)) return ExprError(); - if (initStyle == CXXNewExpr::ListInit && - isStdInitializerList(AllocType, nullptr)) { - Diag(AllocTypeInfo->getTypeLoc().getBeginLoc(), - diag::warn_dangling_std_initializer_list) - << /*at end of FE*/0 << Inits[0]->getSourceRange(); - } - // In ARC, infer 'retaining' for the allocated if (getLangOpts().ObjCAutoRefCount && AllocType.getObjCLifetime() == Qualifiers::OCL_None && diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index 320d93a99a..f94eabd671 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -572,6 +572,7 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, hadError = true; return; } + SemaRef.checkInitializerLifetime(MemberEntity, DIE.get()); if (Init < NumInits) ILE->setInit(Init, DIE.get()); else { @@ -6197,17 +6198,43 @@ InitializedEntityOutlivesFullExpression(const InitializedEntity &Entity) { llvm_unreachable("unknown entity kind"); } +namespace { +enum LifetimeKind { + /// The lifetime of a temporary bound to this entity ends at the end of the + /// full-expression, and that's (probably) fine. + LK_FullExpression, + + /// The lifetime of a temporary bound to this entity is extended to the + /// lifeitme of the entity itself. + LK_Extended, + + /// The lifetime of a temporary bound to this entity probably ends too soon, + /// because the entity is allocated in a new-expression. + LK_New, + + /// The lifetime of a temporary bound to this entity ends too soon, because + /// the entity is a return object. + LK_Return, + + /// This is a mem-initializer: if it would extend a temporary (other than via + /// a default member initializer), the program is ill-formed. + LK_MemInitializer, +}; +using LifetimeResult = + llvm::PointerIntPair<const InitializedEntity *, 3, LifetimeKind>; +} + /// Determine the declaration which an initialized entity ultimately refers to, /// for the purpose of lifetime-extending a temporary bound to a reference in /// the initialization of \p Entity. -static const InitializedEntity *getEntityForTemporaryLifetimeExtension( +static LifetimeResult getEntityForTemporaryLifetimeExtension( const InitializedEntity *Entity, - const InitializedEntity *FallbackDecl = nullptr) { + const InitializedEntity *InitField = nullptr) { // C++11 [class.temporary]p5: switch (Entity->getKind()) { case InitializedEntity::EK_Variable: // The temporary [...] persists for the lifetime of the reference - return Entity; + return {Entity, LK_Extended}; case InitializedEntity::EK_Member: // For subobjects, we look at the complete object. @@ -6216,29 +6243,43 @@ static const InitializedEntity *getEntityForTemporaryLifetimeExtension( Entity); // except: - // -- A temporary bound to a reference member in a constructor's - // ctor-initializer persists until the constructor exits. - return Entity; + // C++17 [class.base.init]p8: + // A temporary expression bound to a reference member in a + // mem-initializer is ill-formed. + // C++17 [class.base.init]p11: + // A temporary expression bound to a reference member from a + // default member initializer is ill-formed. + // + // The context of p11 and its example suggest that it's only the use of a + // default member initializer from a constructor that makes the program + // ill-formed, not its mere existence, and that it can even be used by + // aggregate initialization. + return {Entity, Entity->isDefaultMemberInitializer() ? LK_Extended + : LK_MemInitializer}; case InitializedEntity::EK_Binding: // Per [dcl.decomp]p3, the binding is treated as a variable of reference // type. - return Entity; + return {Entity, LK_Extended}; case InitializedEntity::EK_Parameter: case InitializedEntity::EK_Parameter_CF_Audited: // -- A temporary bound to a reference parameter in a function call // persists until the completion of the full-expression containing // the call. + return {nullptr, LK_FullExpression}; + case InitializedEntity::EK_Result: // -- The lifetime of a temporary bound to the returned value in a // function return statement is not extended; the temporary is // destroyed at the end of the full-expression in the return statement. + return {nullptr, LK_Return}; + case InitializedEntity::EK_New: // -- A temporary bound to a reference in a new-initializer persists // until the completion of the full-expression containing the // new-initializer. - return nullptr; + return {nullptr, LK_New}; case InitializedEntity::EK_Temporary: case InitializedEntity::EK_CompoundLiteralInit: @@ -6246,25 +6287,26 @@ static const InitializedEntity *getEntityForTemporaryLifetimeExtension( // We don't yet know the storage duration of the surrounding temporary. // Assume it's got full-expression duration for now, it will patch up our // storage duration if that's not correct. - return nullptr; + return {nullptr, LK_FullExpression}; case InitializedEntity::EK_ArrayElement: // For subobjects, we look at the complete object. return getEntityForTemporaryLifetimeExtension(Entity->getParent(), - FallbackDecl); + InitField); case InitializedEntity::EK_Base: // For subobjects, we look at the complete object. if (Entity->getParent()) return getEntityForTemporaryLifetimeExtension(Entity->getParent(), - Entity); - LLVM_FALLTHROUGH; + InitField); + return {InitField, LK_MemInitializer}; + case InitializedEntity::EK_Delegating: // We can reach this case for aggregate initialization in a constructor: // struct A { int &&r; }; // struct B : A { B() : A{0} {} }; - // In this case, use the innermost field decl as the context. - return FallbackDecl; + // In this case, use the outermost field decl as the context. + return {InitField, LK_MemInitializer}; case InitializedEntity::EK_BlockElement: case InitializedEntity::EK_LambdaToBlockConversionBlockElement: @@ -6272,30 +6314,54 @@ static const InitializedEntity *getEntityForTemporaryLifetimeExtension( case InitializedEntity::EK_Exception: case InitializedEntity::EK_VectorElement: case InitializedEntity::EK_ComplexElement: - return nullptr; + return {nullptr, LK_FullExpression}; } llvm_unreachable("unknown entity kind"); } -static void performLifetimeExtension(Expr *Init, - const InitializedEntity *ExtendingEntity); +namespace { +enum ExtensionKind { + /// Lifetime would be extended by a reference binding to a temporary. + EK_ReferenceBinding, + /// Lifetime would be extended by a std::initializer_list object binding to + /// its backing array. + EK_StdInitializerList, +}; +using IndirectTemporaryPathEntry = + llvm::PointerUnion<CXXDefaultInitExpr *, ValueDecl *>; +using IndirectTemporaryPath = llvm::SmallVectorImpl<IndirectTemporaryPathEntry>; + +struct RevertToOldSizeRAII { + IndirectTemporaryPath &Path; + unsigned OldSize = Path.size(); + RevertToOldSizeRAII(IndirectTemporaryPath &Path) : Path(Path) {} + ~RevertToOldSizeRAII() { Path.resize(OldSize); } +}; +} + +template <typename TemporaryVisitor> +static void visitTemporariesExtendedByInitializer(IndirectTemporaryPath &Path, + Expr *Init, + TemporaryVisitor Visit); + +/// Visit the temporaries whose lifetimes would be extended by binding a +/// reference to the glvalue expression \c Init. +template <typename TemporaryVisitor> +static void +visitTemporariesExtendedByReferenceBinding(IndirectTemporaryPath &Path, + Expr *Init, ExtensionKind EK, + TemporaryVisitor Visit) { + RevertToOldSizeRAII RAII(Path); -/// Update a glvalue expression that is used as the initializer of a reference -/// to note that its lifetime is extended. -/// \return \c true if any temporary had its lifetime extended. -static bool -performReferenceExtension(Expr *Init, - const InitializedEntity *ExtendingEntity) { // Walk past any constructs which we can lifetime-extend across. Expr *Old; do { Old = Init; if (InitListExpr *ILE = dyn_cast<InitListExpr>(Init)) { - if (ILE->getNumInits() == 1 && ILE->isGLValue()) { - // This is just redundant braces around an initializer. Step over it. + // If this is just redundant braces around an initializer, step over it. + if (ILE->isTransparent()) Init = ILE->getInit(0); - } } // Step over any subobject adjustments; we may have a materialized @@ -6312,40 +6378,65 @@ performReferenceExtension(Expr *Init, // when performing lifetime extension. if (auto *ASE = dyn_cast<ArraySubscriptExpr>(Init)) Init = ASE->getBase(); + + // 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)) { + Path.push_back(DIE); + Init = DIE->getExpr(); + + if (auto *EWC = dyn_cast<ExprWithCleanups>(Init)) + Init = EWC->getSubExpr(); + } } while (Init != Old); - if (MaterializeTemporaryExpr *ME = dyn_cast<MaterializeTemporaryExpr>(Init)) { - // Update the storage duration of the materialized temporary. - // FIXME: Rebuild the expression instead of mutating it. - ME->setExtendingDecl(ExtendingEntity->getDecl(), - ExtendingEntity->allocateManglingNumber()); - performLifetimeExtension(ME->GetTemporaryExpr(), ExtendingEntity); - return true; + if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Init)) { + if (Visit(Path, MTE, EK)) + visitTemporariesExtendedByInitializer(Path, MTE->GetTemporaryExpr(), + Visit); } - - return false; } -/// Update a prvalue expression that is going to be materialized as a -/// lifetime-extended temporary. -static void performLifetimeExtension(Expr *Init, - const InitializedEntity *ExtendingEntity) { +/// Visit the temporaries whose lifetimes would be extended by +/// lifetime-extending the object initialized by the prvalue expression \c +/// Init. +template <typename TemporaryVisitor> +static void visitTemporariesExtendedByInitializer(IndirectTemporaryPath &Path, + Expr *Init, + TemporaryVisitor Visit) { + RevertToOldSizeRAII RAII(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)) { + Path.push_back(DIE); + Init = DIE->getExpr(); + + if (auto *EWC = dyn_cast<ExprWithCleanups>(Init)) + Init = EWC->getSubExpr(); + } + // Dig out the expression which constructs the extended temporary. Init = const_cast<Expr *>(Init->skipRValueSubobjectAdjustments()); if (CXXBindTemporaryExpr *BTE = dyn_cast<CXXBindTemporaryExpr>(Init)) Init = BTE->getSubExpr(); - if (CXXStdInitializerListExpr *ILE = - dyn_cast<CXXStdInitializerListExpr>(Init)) { - performReferenceExtension(ILE->getSubExpr(), ExtendingEntity); - return; - } + // C++17 [dcl.init.list]p6: + // initializing an initializer_list object from the array extends the + // lifetime of the array exactly like binding a reference to a temporary. + if (auto *ILE = dyn_cast<CXXStdInitializerListExpr>(Init)) + return visitTemporariesExtendedByReferenceBinding( + Path, ILE->getSubExpr(), EK_StdInitializerList, Visit); if (InitListExpr *ILE = dyn_cast<InitListExpr>(Init)) { + if (ILE->isTransparent()) + return visitTemporariesExtendedByInitializer(Path, ILE->getInit(0), + Visit); + if (ILE->getType()->isArrayType()) { for (unsigned I = 0, N = ILE->getNumInits(); I != N; ++I) - performLifetimeExtension(ILE->getInit(I), ExtendingEntity); + visitTemporariesExtendedByInitializer(Path, ILE->getInit(I), Visit); return; } @@ -6357,7 +6448,8 @@ static void performLifetimeExtension(Expr *Init, // bound to temporaries, those temporaries are also lifetime-extended. if (RD->isUnion() && ILE->getInitializedFieldInUnion() && ILE->getInitializedFieldInUnion()->getType()->isReferenceType()) - performReferenceExtension(ILE->getInit(0), ExtendingEntity); + visitTemporariesExtendedByReferenceBinding(Path, ILE->getInit(0), + EK_ReferenceBinding, Visit); else { unsigned Index = 0; for (const auto *I : RD->fields()) { @@ -6367,13 +6459,13 @@ static void performLifetimeExtension(Expr *Init, continue; Expr *SubInit = ILE->getInit(Index); if (I->getType()->isReferenceType()) - performReferenceExtension(SubInit, ExtendingEntity); - else if (isa<InitListExpr>(SubInit) || - isa<CXXStdInitializerListExpr>(SubInit)) - // This may be either aggregate-initialization of a member or - // initialization of a std::initializer_list object. Either way, + visitTemporariesExtendedByReferenceBinding( + Path, SubInit, EK_ReferenceBinding, Visit); + else + // This might be either aggregate-initialization of a member or + // initialization of a std::initializer_list object. Regardless, // we should recursively lifetime-extend that initializer. - performLifetimeExtension(SubInit, ExtendingEntity); + visitTemporariesExtendedByInitializer(Path, SubInit, Visit); ++Index; } } @@ -6381,37 +6473,123 @@ static void performLifetimeExtension(Expr *Init, } } -static void warnOnLifetimeExtension(Sema &S, const InitializedEntity &Entity, - const Expr *Init, bool IsInitializerList, - const ValueDecl *ExtendingDecl) { - // Warn if a field lifetime-extends a temporary. - if (isa<FieldDecl>(ExtendingDecl)) { - if (IsInitializerList) { - S.Diag(Init->getExprLoc(), diag::warn_dangling_std_initializer_list) - << /*at end of constructor*/true; - return; +/// Determine whether this is an indirect path to a temporary that we are +/// supposed to lifetime-extend along (but don't). +static bool shouldLifetimeExtendThroughPath(const IndirectTemporaryPath &Path) { + for (auto Elem : Path) { + if (!Elem.is<CXXDefaultInitExpr*>()) + return false; + } + return true; +} + +void Sema::checkInitializerLifetime(const InitializedEntity &Entity, + Expr *Init) { + LifetimeResult LR = getEntityForTemporaryLifetimeExtension(&Entity); + LifetimeKind LK = LR.getInt(); + const InitializedEntity *ExtendingEntity = LR.getPointer(); + + // If this entity doesn't have an interesting lifetime, don't bother looking + // for temporaries within its initializer. + if (LK == LK_FullExpression) + return; + + auto TemporaryVisitor = [&](IndirectTemporaryPath &Path, + MaterializeTemporaryExpr *MTE, + ExtensionKind EK) -> bool { + switch (LK) { + case LK_FullExpression: + llvm_unreachable("already handled this"); + + case LK_Extended: + // Lifetime-extend the temporary. + if (Path.empty()) { + // 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; + } + + if (shouldLifetimeExtendThroughPath(Path)) { + // 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(MTE->getExprLoc(), + EK == EK_ReferenceBinding + ? diag::warn_default_member_init_temporary_not_extended + : diag::warn_default_member_init_init_list_not_extended); + } else { + llvm_unreachable("unexpected indirect temporary path"); + } + break; + + case LK_MemInitializer: + // Under C++ DR1696, if a mem-initializer (or a default member + // initializer used by the absence of one) would lifetime-extend a + // temporary, the program is ill-formed. + if (auto *ExtendingDecl = ExtendingEntity->getDecl()) { + bool IsSubobjectMember = ExtendingEntity != &Entity; + Diag(MTE->getExprLoc(), diag::err_bind_ref_member_to_temporary) + << ExtendingDecl << Init->getSourceRange() << IsSubobjectMember + << EK; + // Don't bother adding a note pointing to the field if we're inside its + // default member initializer; our primary diagnostic points to the + // same place in that case. + if (Path.empty() || !Path.back().is<CXXDefaultInitExpr*>()) { + Diag(ExtendingDecl->getLocation(), + diag::note_lifetime_extending_member_declared_here) + << EK << IsSubobjectMember; + } + } else { + // We have a mem-initializer but no particular field within it; this + // is either a base class or a delegating initializer directly + // initializing the base-class from something that doesn't live long + // enough. Either way, that can't happen. + // FIXME: Move CheckForDanglingReferenceOrPointer checks here. + llvm_unreachable( + "temporary initializer for base class / delegating ctor"); + } + break; + + case LK_New: + if (EK == EK_ReferenceBinding) { + Diag(MTE->getExprLoc(), diag::warn_new_dangling_reference); + } else { + Diag(MTE->getExprLoc(), diag::warn_new_dangling_initializer_list) + << (ExtendingEntity != &Entity); + } + break; + + case LK_Return: + // FIXME: Move -Wreturn-stack-address checks here. + return false; } - bool IsSubobjectMember = false; - for (const InitializedEntity *Ent = Entity.getParent(); Ent; - Ent = Ent->getParent()) { - if (Ent->getKind() != InitializedEntity::EK_Base) { - IsSubobjectMember = true; - break; + // FIXME: Model these as CodeSynthesisContexts to fix the note emission + // order. + for (auto Elem : llvm::reverse(Path)) { + if (auto *DIE = Elem.dyn_cast<CXXDefaultInitExpr*>()) { + Diag(DIE->getExprLoc(), diag::note_in_default_member_initalizer_here) + << DIE->getField(); } } - S.Diag(Init->getExprLoc(), - diag::warn_bind_ref_member_to_temporary) - << ExtendingDecl << Init->getSourceRange() - << IsSubobjectMember << IsInitializerList; - if (IsSubobjectMember) - S.Diag(ExtendingDecl->getLocation(), - diag::note_ref_subobject_of_member_declared_here); - else - S.Diag(ExtendingDecl->getLocation(), - diag::note_ref_or_ptr_member_declared_here) - << /*is pointer*/false; - } + + // We didn't lifetime-extend, so don't go any further; we don't need more + // warnings or errors on inner temporaries within this one's initializer. + return false; + }; + + llvm::SmallVector<IndirectTemporaryPathEntry, 8> Path; + if (Init->isGLValue()) + visitTemporariesExtendedByReferenceBinding(Path, Init, EK_ReferenceBinding, + TemporaryVisitor); + else + visitTemporariesExtendedByInitializer(Path, Init, TemporaryVisitor); } static void DiagnoseNarrowingInInitList(Sema &S, @@ -6838,14 +7016,9 @@ InitializationSequence::Perform(Sema &S, } // Even though we didn't materialize a temporary, the binding may still - // extend the lifetime of a temporary. This happens if we bind a reference - // to the result of a cast to reference type. - if (const InitializedEntity *ExtendingEntity = - getEntityForTemporaryLifetimeExtension(&Entity)) - if (performReferenceExtension(CurInit.get(), ExtendingEntity)) - warnOnLifetimeExtension(S, Entity, CurInit.get(), - /*IsInitializerList=*/false, - ExtendingEntity->getDecl()); + // extend the lifetime of a temporary. This happens if we bind a + // reference to the result of a cast to reference type. + S.checkInitializerLifetime(Entity, CurInit.get()); CheckForNullPointerDereference(S, CurInit.get()); break; @@ -6861,23 +7034,17 @@ InitializationSequence::Perform(Sema &S, // Materialize the temporary into memory. MaterializeTemporaryExpr *MTE = S.CreateMaterializeTemporaryExpr( Step->Type, CurInit.get(), Entity.getType()->isLValueReferenceType()); + CurInit = MTE; // Maybe lifetime-extend the temporary's subobjects to match the // entity's lifetime. - if (const InitializedEntity *ExtendingEntity = - getEntityForTemporaryLifetimeExtension(&Entity)) - if (performReferenceExtension(MTE, ExtendingEntity)) - warnOnLifetimeExtension(S, Entity, CurInit.get(), - /*IsInitializerList=*/false, - ExtendingEntity->getDecl()); + S.checkInitializerLifetime(Entity, CurInit.get()); // If we're extending this temporary to automatic storage duration -- we // need to register its cleanup during the full-expression's cleanups. if (MTE->getStorageDuration() == SD_Automatic && MTE->getType().isDestructedType()) S.Cleanup.setExprNeedsCleanups(true); - - CurInit = MTE; break; } @@ -7320,18 +7487,13 @@ InitializationSequence::Perform(Sema &S, CurInit.get()->getType(), CurInit.get(), /*BoundToLvalueReference=*/false); - // Maybe lifetime-extend the array temporary's subobjects to match the - // entity's lifetime. - if (const InitializedEntity *ExtendingEntity = - getEntityForTemporaryLifetimeExtension(&Entity)) - if (performReferenceExtension(MTE, ExtendingEntity)) - warnOnLifetimeExtension(S, Entity, CurInit.get(), - /*IsInitializerList=*/true, - ExtendingEntity->getDecl()); - // Wrap it in a construction of a std::initializer_list<T>. CurInit = new (S.Context) CXXStdInitializerListExpr(Step->Type, MTE); + // Maybe lifetime-extend the array temporary's subobjects to match the + // entity's lifetime. + S.checkInitializerLifetime(Entity, CurInit.get()); + // Bind the result, in case the library has given initializer_list a // non-trivial destructor. if (shouldBindAsTemporary(Entity)) diff --git a/test/Analysis/initializer.cpp b/test/Analysis/initializer.cpp index b73a94f1db..0cb68c4a97 100644 --- a/test/Analysis/initializer.cpp +++ b/test/Analysis/initializer.cpp @@ -178,29 +178,6 @@ namespace ReferenceInitialization { const MyStruct &myStruct(OtherStruct(5)); myStruct.method(); // no-warning } - - struct HasMyStruct { - const MyStruct &ms; // expected-note {{reference member declared here}} - const MyStruct &msWithCleanups; // expected-note {{reference member declared here}} - - // clang's Sema issues a warning when binding a reference member to a - // temporary value. - HasMyStruct() : ms(5), msWithCleanups(OtherStruct(5)) { - // expected-warning@-1 {{binding reference member 'ms' to a temporary value}} - // expected-warning@-2 {{binding reference member 'msWithCleanups' to a temporary value}} - - // At this point the members are not garbage so we should not expect an - // analyzer warning here even though binding a reference member - // to a member is a terrible idea. - ms.method(); // no-warning - msWithCleanups.method(); // no-warning - } - }; - - void referenceInitializeField() { - HasMyStruct hms; - } - }; namespace PR31592 { diff --git a/test/CXX/drs/dr16xx.cpp b/test/CXX/drs/dr16xx.cpp index e0af95ac39..54648cfe96 100644 --- a/test/CXX/drs/dr16xx.cpp +++ b/test/CXX/drs/dr16xx.cpp @@ -9,6 +9,20 @@ #define static_assert(...) __extension__ _Static_assert(__VA_ARGS__) #endif +#if __cplusplus >= 201103L +namespace std { + typedef decltype(sizeof(int)) size_t; + + template<typename E> class initializer_list { + const E *begin; + size_t size; + + public: + initializer_list(); + }; +} // std +#endif + namespace dr1611 { // dr1611: dup 1658 struct A { A(int); }; struct B : virtual A { virtual void f() = 0; }; @@ -269,3 +283,83 @@ namespace dr1687 { // dr1687: 7 auto c = To<E1>() <=> To<E2>(); // expected-error {{invalid operands to binary expression ('To<dr1687::E1>' and 'To<dr1687::E2>')}} #endif } + +namespace dr1696 { // dr1696: 7 + namespace std_examples { +#if __cplusplus >= 201402L + extern struct A a; + struct A { + const A &x = { A{a, a} }; + const A &y = { A{} }; // expected-error {{default member initializer for 'y' needed within definition of enclosing class 'A' outside of member functions}} expected-note {{here}} + }; + A a{a, a}; +#endif + } + + struct A { A(); ~A(); }; +#if __cplusplus >= 201103L + struct B { + A &&a; // expected-note {{declared here}} + B() : a{} {} // expected-error {{reference member 'a' binds to a temporary object whose lifetime would be shorter than the constructed object}} + } b; +#endif + + struct C { + C(); + const A &a; // expected-note {{declared here}} + }; + C::C() : a(A()) {} // expected-error {{reference member 'a' binds to a temporary object whose lifetime would be shorter than the constructed object}} + +#if __cplusplus >= 201103L + // This is OK in C++14 onwards, per DR1815, though we don't support that yet: + // D1 d1 = {}; + // is equivalent to + // D1 d1 = {A()}; + // ... which lifetime-extends the A temporary. + struct D1 { + const A &a = A(); +#if __cplusplus < 201402L + // expected-error@-2 {{binds to a temporary}} + // expected-note@-4 {{here}} +#else + // expected-warning-re@-5 {{sorry, lifetime extension {{.*}} not supported}} +#endif + }; + D1 d1 = {}; // expected-note {{here}} + + struct D2 { + const A &a = A(); // expected-error {{binds to a temporary}} + D2() {} // expected-note {{used here}} + }; + + struct D3 { // expected-note {{used here}} + const A &a = A(); // expected-error {{binds to a temporary}} + }; + D3 d3; // expected-note {{first required here}} + + struct haslist1 { + std::initializer_list<int> il; // expected-note {{'std::initializer_list' member}} + haslist1(int i) : il{i, 2, 3} {} // expected-error {{backing array for 'std::initializer_list' member 'il' is a temporary object}} + }; + + struct haslist2 { + std::initializer_list<int> il; // expected-note {{'std::initializer_list' member}} + haslist2(); + }; + haslist2::haslist2() : il{1, 2} {} // expected-error {{backing array for 'std::initializer_list' member 'il' is a temporary object}} + + struct haslist3 { + std::initializer_list<int> il = {1, 2, 3}; + }; + + struct haslist4 { // expected-note {{in default member initializer}} + std::initializer_list<int> il = {1, 2, 3}; // expected-error {{backing array for 'std::initializer_list' member 'il' is a temporary object}} + }; + haslist4 hl4; // expected-note {{in implicit default constructor}} + + struct haslist5 { + std::initializer_list<int> il = {1, 2, 3}; // expected-error {{backing array for 'std::initializer_list' member 'il' is a temporary object}} + haslist5() {} // expected-note {{in default member initializer}} + }; +#endif +} diff --git a/test/CXX/drs/dr18xx.cpp b/test/CXX/drs/dr18xx.cpp index a0f470ed4a..f6a4676bc7 100644 --- a/test/CXX/drs/dr18xx.cpp +++ b/test/CXX/drs/dr18xx.cpp @@ -30,6 +30,17 @@ namespace dr1813 { // dr1813: 7 static_assert(!__is_standard_layout(U), ""); } +namespace dr1815 { // dr1815: no +#if __cplusplus >= 201402L + // FIXME: needs codegen test + struct A { int &&r = 0; }; // FIXME expected-warning {{not supported}} + A a = {}; // expected-note {{here}} + + struct B { int &&r = 0; }; // expected-error {{binds to a temporary}} expected-note {{here}} + B b; // expected-note {{here}} +#endif +} + namespace dr1881 { // dr1881: 7 struct A { int a : 4; }; struct B : A { int b : 3; }; diff --git a/test/CXX/special/class.copy/p11.0x.copy.cpp b/test/CXX/special/class.copy/p11.0x.copy.cpp index 1ca0143d09..a4d0cdcdc7 100644 --- a/test/CXX/special/class.copy/p11.0x.copy.cpp +++ b/test/CXX/special/class.copy/p11.0x.copy.cpp @@ -121,13 +121,22 @@ extern HasNoAccessDtorBase HNADBa; HasNoAccessDtorBase HNADBb(HNADBa); // expected-error{{implicitly-deleted copy constructor}} // -- a non-static data member of rvalue reference type +int some_int; struct RValue { - int && ri = 1; // expected-note{{copy constructor of 'RValue' is implicitly deleted because field 'ri' is of rvalue reference type 'int &&'}} - // expected-warning@-1{{binding reference member 'ri' to a temporary}} expected-note@-1 {{here}} + int && ri = static_cast<int&&>(some_int); // expected-note{{copy constructor of 'RValue' is implicitly deleted because field 'ri' is of rvalue reference type 'int &&'}} }; RValue RVa; RValue RVb(RVa); // expected-error{{call to implicitly-deleted copy constructor}} +// FIXME: The note on the class-name is attached to the location of the +// constructor. This is not especially clear. +struct RValueTmp { // expected-note {{used here}} + int && ri = 1; // expected-note{{copy constructor of 'RValueTmp' is implicitly deleted because field 'ri' is of rvalue reference type 'int &&'}} + // expected-error@-1 {{reference member 'ri' binds to a temporary}} +}; +RValueTmp RVTa; // expected-note {{implicit default constructor for 'RValueTmp' first required here}} +RValueTmp RVTb(RVTa); // expected-error{{call to implicitly-deleted copy constructor}} + namespace PR13381 { struct S { S(const S&); diff --git a/test/CXX/special/class.copy/p11.0x.move.cpp b/test/CXX/special/class.copy/p11.0x.move.cpp index ab4259548e..5b016836e9 100644 --- a/test/CXX/special/class.copy/p11.0x.move.cpp +++ b/test/CXX/special/class.copy/p11.0x.move.cpp @@ -145,7 +145,7 @@ HasNoAccessDtorBase HNADBb(HNADBa); // expected-error{{implicitly-deleted copy c // The restriction on rvalue reference members applies to only the copy // constructor. struct RValue { - int &&ri = 1; // expected-warning {{binding reference member 'ri' to a temporary}} expected-note {{here}} + int &&ri = 1; RValue(RValue&&); }; RValue::RValue(RValue&&) = default; diff --git a/test/CXX/special/class.ctor/p5-0x.cpp b/test/CXX/special/class.ctor/p5-0x.cpp index 2360345a48..5558313ce7 100644 --- a/test/CXX/special/class.ctor/p5-0x.cpp +++ b/test/CXX/special/class.ctor/p5-0x.cpp @@ -43,8 +43,12 @@ class NotDeleted2a { int &a = n; }; NotDeleted2a nd2a; class NotDeleted2b { int &a = error; }; // expected-error {{undeclared identifier}} NotDeleted2b nd2b; -class NotDeleted2c { int &&a = 0; }; // expected-warning {{binding reference member 'a' to a temporary}} expected-note {{here}} +class NotDeleted2c { int &&a = static_cast<int&&>(n); }; NotDeleted2c nd2c; +// Note: this one does not have a deleted default constructor even though the +// implicit default constructor is ill-formed! +class NotDeleted2d { int &&a = 0; }; // expected-error {{reference member 'a' binds to a temporary object}} expected-note {{here}} +NotDeleted2d nd2d; // expected-note {{first required here}} // - any non-variant non-static data member of const qualified type (or array // thereof) with no brace-or-equal-initializer does not have a user-provided diff --git a/test/CXX/temp/temp.param/p5.cpp b/test/CXX/temp/temp.param/p5.cpp index aa0d7e92b9..de902a5e4d 100644 --- a/test/CXX/temp/temp.param/p5.cpp +++ b/test/CXX/temp/temp.param/p5.cpp @@ -1,13 +1,13 @@ // RUN: %clang_cc1 -verify %s -std=c++14 -template<const int I> struct S { +template<const int I> struct S { // expected-note {{in default member initializer}} decltype(I) n; - int &&r = I; // expected-warning 2{{binding reference member 'r' to a temporary value}} expected-note 2{{declared here}} + int &&r = I; // expected-error {{reference member 'r' binds to a temporary object}} }; -S<5> s; // expected-note {{instantiation}} +S<5> s; // expected-note {{implicit default constructor}} -template<typename T, T v> struct U { +template<typename T, T v> struct U { // expected-note {{in default member initializer}} decltype(v) n; - int &&r = v; // expected-warning {{binding reference member 'r' to a temporary value}} expected-note {{declared here}} + int &&r = v; // expected-error {{reference member 'r' binds to a temporary object}} }; -U<const int, 6> u; // expected-note {{instantiation}} +U<const int, 6> u; // expected-note {{implicit default constructor}} diff --git a/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp b/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp index 3299763997..0184a1d6e7 100644 --- a/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp +++ b/test/CodeGenCXX/cxx0x-initializer-stdinitializerlist.cpp @@ -236,36 +236,6 @@ void fn9() { // CHECK: ret void } -struct haslist1 { - std::initializer_list<int> il; - haslist1(int i); -}; - -// CHECK-LABEL: define void @_ZN8haslist1C2Ei -haslist1::haslist1(int i) -// CHECK: alloca [3 x i32] -// CHECK: store i32 % -// CHECK: store i32 2 -// CHECK: store i32 3 - : il{i, 2, 3} -{ - destroyme2 dm2; -} - -struct haslist2 { - std::initializer_list<destroyme1> il; - haslist2(); -}; - -// CHECK-LABEL: define void @_ZN8haslist2C2Ev -haslist2::haslist2() - : il{destroyme1(), destroyme1()} -{ - destroyme2 dm2; - // CHECK: call void @_ZN10destroyme2D1Ev - // CHECK: call void @_ZN10destroyme1D1Ev -} - void fn10(int i) { // CHECK-LABEL: define void @_Z4fn10i // CHECK: alloca [3 x i32] diff --git a/test/CodeGenCXX/temporaries.cpp b/test/CodeGenCXX/temporaries.cpp index bad51ba353..21372effbc 100644 --- a/test/CodeGenCXX/temporaries.cpp +++ b/test/CodeGenCXX/temporaries.cpp @@ -198,20 +198,6 @@ B::B() f(); } -struct C { - C(); - - const B& b; -}; - -C::C() - // CHECK: call void @_ZN6PR50771BC1Ev - : b(B()) { - // CHECK: call void @_ZN6PR50771fEv - f(); - - // CHECK: call void @_ZN6PR50771BD1Ev -} } A f8() { @@ -816,16 +802,3 @@ namespace PR14130 { // CHECK: call void @_ZN7PR141301SC1Ei({{.*}} @_ZGRN7PR141301vE_, i32 0) // CHECK: store {{.*}} @_ZGRN7PR141301vE_, {{.*}} @_ZN7PR141301vE } - -namespace Ctor { - struct A { A(); ~A(); }; - void f(); - struct B { - A &&a; - B() : a{} { f(); } - } b; - // CHECK: define {{.*}}void @_ZN4Ctor1BC1Ev( - // CHECK: call void @_ZN4Ctor1AC1Ev( - // CHECK: call void @_ZN4Ctor1fEv( - // CHECK: call void @_ZN4Ctor1AD1Ev( -} diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp index 4b4bd43634..e921634b1b 100644 --- a/test/SemaCXX/constant-expression-cxx11.cpp +++ b/test/SemaCXX/constant-expression-cxx11.cpp @@ -1892,14 +1892,15 @@ namespace Lifetime { } constexpr int &get(int &&n) { return n; } + constexpr int &&get_rv(int &&n) { return static_cast<int&&>(n); } struct S { - int &&r; // expected-note 2{{declared here}} + int &&r; int &s; int t; - constexpr S() : r(0), s(get(0)), t(r) {} // expected-warning {{temporary}} - constexpr S(int) : r(0), s(get(0)), t(s) {} // expected-warning {{temporary}} expected-note {{read of object outside its lifetime}} + constexpr S() : r(get_rv(0)), s(get(0)), t(r) {} // expected-note {{read of object outside its lifetime}} + constexpr S(int) : r(get_rv(0)), s(get(0)), t(s) {} // expected-note {{read of object outside its lifetime}} }; - constexpr int k1 = S().t; // ok, int is lifetime-extended to end of constructor + constexpr int k1 = S().t; // expected-error {{constant expression}} expected-note {{in call}} constexpr int k2 = S(0).t; // expected-error {{constant expression}} expected-note {{in call}} } diff --git a/test/SemaCXX/constexpr-default-arg.cpp b/test/SemaCXX/constexpr-default-arg.cpp index 2fc873c3e5..165c31aab6 100644 --- a/test/SemaCXX/constexpr-default-arg.cpp +++ b/test/SemaCXX/constexpr-default-arg.cpp @@ -31,8 +31,8 @@ void test_default_arg2() { } // Check that multiple CXXDefaultInitExprs don't cause an assertion failure. -struct A { int &&r = 0; }; // expected-warning {{binding reference member}} // expected-note {{reference member declared here}} +struct A { int &&r = 0; }; // expected-warning 2{{not supported}} struct B { A x, y; }; -B b = {}; +B b = {}; // expected-note 2{{in default member initializer for field 'r' used here}} } diff --git a/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp b/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp index 860a4aa6c6..ece014d93a 100644 --- a/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp +++ b/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp @@ -153,13 +153,14 @@ void dangle() { } struct haslist1 { - std::initializer_list<int> il = {1, 2, 3}; // expected-warning{{at the end of the constructor}} - std::initializer_list<int> jl{1, 2, 3}; // expected-warning{{at the end of the constructor}} + std::initializer_list<int> il // expected-note {{declared here}} + = {1, 2, 3}; // ok, unused + std::initializer_list<int> jl{1, 2, 3}; // expected-error {{backing array for 'std::initializer_list' member 'jl' is a temporary object}} haslist1(); }; -haslist1::haslist1() -: il{1, 2, 3} // expected-warning{{at the end of the constructor}} +haslist1::haslist1() // expected-note {{used here}} +: il{1, 2, 3} // expected-error {{backing array for 'std::initializer_list' member 'il' is a temporary object}} {} namespace PR12119 { diff --git a/test/SemaCXX/eval-crashes.cpp b/test/SemaCXX/eval-crashes.cpp index 23946845d8..33bde75de6 100644 --- a/test/SemaCXX/eval-crashes.cpp +++ b/test/SemaCXX/eval-crashes.cpp @@ -26,10 +26,10 @@ namespace pr33140_0b { namespace pr33140_2 { // FIXME: The declaration of 'b' below should lifetime-extend two int - // temporaries, invalidating this warning to some extent. - struct A { int &&r = 0; }; // expected-warning {{binding reference member 'r' to a temporary}} expected-note {{here}} + // temporaries. + struct A { int &&r = 0; }; // expected-warning 2{{not supported}} struct B { A x, y; }; - B b = {}; + B b = {}; // expected-note 2{{used here}} } namespace pr33140_3 { diff --git a/test/SemaCXX/member-init.cpp b/test/SemaCXX/member-init.cpp index ad4a8f15b2..8a13eca2f1 100644 --- a/test/SemaCXX/member-init.cpp +++ b/test/SemaCXX/member-init.cpp @@ -86,9 +86,8 @@ namespace PR14838 { }; struct thing {}; struct another { - another() : r(thing()) {} + another() : r(thing()) {} // expected-error {{binds to a temporary object}} // expected-error@-1 {{temporary of type 'PR14838::function' has private destructor}} - // expected-warning@-2 {{binding reference member 'r' to a temporary value}} const function &r; // expected-note {{reference member declared here}} } af; } diff --git a/test/SemaCXX/warn-dangling-field.cpp b/test/SemaCXX/warn-dangling-field.cpp index eb65bd0669..97d4331c1f 100644 --- a/test/SemaCXX/warn-dangling-field.cpp +++ b/test/SemaCXX/warn-dangling-field.cpp @@ -20,7 +20,7 @@ struct S { struct S2 { const X &x; // expected-note {{reference member declared here}} - S2(int i) : x(i) {} // expected-warning {{binding reference member 'x' to a temporary}} + S2(int i) : x(i) {} // expected-error {{member 'x' binds to a temporary}} }; struct S3 { @@ -43,9 +43,9 @@ S5 s5 = { 0 }; // ok, lifetime-extended struct S6 { S5 s5; // expected-note {{here}} - S6() : s5 { 0 } {} // expected-warning {{binding reference subobject of member 's5' to a temporary}} + S6() : s5 { 0 } {} // expected-error {{reference subobject of member 's5' binds to a temporary}} }; struct S7 : S5 { - S7() : S5 { 0 } {} // expected-warning {{binding reference member 'x' to a temporary}} + S7() : S5 { 0 } {} // expected-error {{reference member 'x' binds to a temporary}} }; diff --git a/www/cxx_dr_status.html b/www/cxx_dr_status.html index 93d4ee93fb..04dd0eae1c 100644 --- a/www/cxx_dr_status.html +++ b/www/cxx_dr_status.html @@ -9991,7 +9991,7 @@ and <I>POD class</I></td> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1696">1696</a></td> <td>CD4</td> <td>Temporary lifetime and non-static data member initializers</td> - <td class="none" align="center">Unknown</td> + <td class="svn" align="center">SVN</td> </tr> <tr class="open" id="1697"> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1697">1697</a></td> @@ -10705,7 +10705,7 @@ and <I>POD class</I></td> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1815">1815</a></td> <td>CD4</td> <td>Lifetime extension in aggregate initialization</td> - <td class="none" align="center">Unknown</td> + <td class="none" align="center">No</td> </tr> <tr id="1816"> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1816">1816</a></td> |