From a4bb99cd0055ba0e1f3107890e5b6cbe31e6d1cc Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 12 Jun 2013 21:51:50 +0000 Subject: [PATCH] Move detection of reference members binding to temporaries from building of CXXCtorInitializers to the point where we perform the questionable lifetime extension. This exposed a selection of false negatives in the warning. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@183869 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 4 ++- lib/Sema/SemaDeclCXX.cpp | 10 +------- lib/Sema/SemaInit.cpp | 28 ++++++++++++++++++++- test/CXX/special/class.copy/p11.0x.copy.cpp | 1 + test/CXX/special/class.copy/p11.0x.move.cpp | 2 +- test/CXX/special/class.ctor/p5-0x.cpp | 2 +- test/CXX/temp/temp.param/p5.cpp | 9 +++---- test/SemaCXX/warn-dangling-field.cpp | 16 +++++++++++- 8 files changed, 53 insertions(+), 19 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 302ec8b08c..ac76e4d400 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -5947,10 +5947,12 @@ def warn_init_ptr_member_to_parameter_addr : Warning< "initializing pointer member %0 with the stack address of parameter %1">, InGroup; def warn_bind_ref_member_to_temporary : Warning< - "binding reference member %0 to a temporary value">, + "binding reference %select{|subobject of }1member %0 to a temporary value">, InGroup; 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">; // 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/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 008bb73dc8..5d6eab0f17 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -2479,15 +2479,7 @@ static void CheckForDanglingReferenceOrPointer(Sema &S, ValueDecl *Member, } } - if (isa(Init->IgnoreParens())) { - // Taking the address of a temporary will be diagnosed as a hard error. - if (IsPointer) - return; - - S.Diag(Init->getExprLoc(), diag::warn_bind_ref_member_to_temporary) - << Member << Init->getSourceRange(); - } else if (const DeclRefExpr *DRE - = dyn_cast(Init->IgnoreParens())) { + if (const DeclRefExpr *DRE = dyn_cast(Init->IgnoreParens())) { // We only warn when referring to a non-reference parameter declaration. const ParmVarDecl *Parameter = dyn_cast(DRE->getDecl()); if (!Parameter || Parameter->getType()->isReferenceType()) diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index b678f91bf7..a4bccacb2d 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -2461,6 +2461,7 @@ InitializedEntity InitializedEntity::InitializeBase(ASTContext &Context, { InitializedEntity Result; Result.Kind = EK_Base; + Result.Parent = 0; Result.Base = reinterpret_cast(Base); if (IsInheritedVirtualBase) Result.Base |= 0x01; @@ -2553,6 +2554,7 @@ bool InitializedEntity::allowsNRVO() const { } unsigned InitializedEntity::dumpImpl(raw_ostream &OS) const { + assert(getParent() != this); unsigned Depth = getParent() ? getParent()->dumpImpl(OS) : 0; for (unsigned I = 0; I != Depth; ++I) OS << "`-"; @@ -5566,9 +5568,33 @@ InitializationSequence::Perform(Sema &S, // entity's lifetime. const ValueDecl *ExtendingDecl = getDeclForTemporaryLifetimeExtension(Entity); - if (ExtendingDecl) + if (ExtendingDecl) { performLifetimeExtension(CurInit.get(), ExtendingDecl); + // Warn if a field lifetime-extends a temporary. + if (isa(ExtendingDecl)) { + bool IsSubobjectMember = false; + for (const InitializedEntity *Ent = Entity.getParent(); Ent; + Ent = Ent->getParent()) { + if (Ent->getKind() != InitializedEntity::EK_Base) { + IsSubobjectMember = true; + break; + } + } + S.Diag(CurInit.get()->getExprLoc(), + diag::warn_bind_ref_member_to_temporary) + << ExtendingDecl << CurInit.get()->getSourceRange() + << IsSubobjectMember; + 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) + << /*IsPointer*/false; + } + } + // Materialize the temporary into memory. MaterializeTemporaryExpr *MTE = new (S.Context) MaterializeTemporaryExpr( Entity.getType().getNonReferenceType(), CurInit.get(), diff --git a/test/CXX/special/class.copy/p11.0x.copy.cpp b/test/CXX/special/class.copy/p11.0x.copy.cpp index fab3b9dd7b..011c1e9a87 100644 --- a/test/CXX/special/class.copy/p11.0x.copy.cpp +++ b/test/CXX/special/class.copy/p11.0x.copy.cpp @@ -116,6 +116,7 @@ HasNoAccessDtorBase HNADBb(HNADBa); // expected-error{{implicitly-deleted copy c // -- a non-static data member of rvalue reference type 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}} }; RValue RVa; RValue RVb(RVa); // expected-error{{call to implicitly-deleted copy constructor}} diff --git a/test/CXX/special/class.copy/p11.0x.move.cpp b/test/CXX/special/class.copy/p11.0x.move.cpp index ff9478be8b..ebcce491c4 100644 --- a/test/CXX/special/class.copy/p11.0x.move.cpp +++ b/test/CXX/special/class.copy/p11.0x.move.cpp @@ -108,7 +108,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; + int &&ri = 1; // expected-warning {{binding reference member 'ri' to a temporary}} expected-note {{here}} 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 0f4add8c97..2360345a48 100644 --- a/test/CXX/special/class.ctor/p5-0x.cpp +++ b/test/CXX/special/class.ctor/p5-0x.cpp @@ -43,7 +43,7 @@ class NotDeleted2a { int &a = n; }; NotDeleted2a nd2a; class NotDeleted2b { int &a = error; }; // expected-error {{undeclared identifier}} NotDeleted2b nd2b; -class NotDeleted2c { int &&a = 0; }; +class NotDeleted2c { int &&a = 0; }; // expected-warning {{binding reference member 'a' to a temporary}} expected-note {{here}} NotDeleted2c nd2c; // - any non-variant non-static data member of const qualified type (or array diff --git a/test/CXX/temp/temp.param/p5.cpp b/test/CXX/temp/temp.param/p5.cpp index 67efc4e481..c25868267e 100644 --- a/test/CXX/temp/temp.param/p5.cpp +++ b/test/CXX/temp/temp.param/p5.cpp @@ -1,14 +1,13 @@ // RUN: %clang_cc1 -verify %s -std=c++11 -// expected-no-diagnostics template struct S { decltype(I) n; - int &&r = I; + int &&r = I; // expected-warning 2{{binding reference member 'r' to a temporary value}} expected-note 2{{declared here}} }; -S<5> s; +S<5> s; // expected-note {{instantiation}} template struct U { decltype(v) n; - int &&r = v; + int &&r = v; // expected-warning {{binding reference member 'r' to a temporary value}} expected-note {{declared here}} }; -U u; +U u; // expected-note {{instantiation}} diff --git a/test/SemaCXX/warn-dangling-field.cpp b/test/SemaCXX/warn-dangling-field.cpp index 95f8c61ebb..eb65bd0669 100644 --- a/test/SemaCXX/warn-dangling-field.cpp +++ b/test/SemaCXX/warn-dangling-field.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -Wdangling-field -verify %s +// RUN: %clang_cc1 -fsyntax-only -Wdangling-field -verify -std=c++11 %s struct X { X(int); @@ -35,3 +35,17 @@ template struct S4 { template struct S4; // no warning from this instantiation template struct S4; // expected-note {{in instantiation}} + +struct S5 { + const X &x; // expected-note {{here}} +}; +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}} +}; + +struct S7 : S5 { + S7() : S5 { 0 } {} // expected-warning {{binding reference member 'x' to a temporary}} +}; -- 2.40.0