forked from OSchip/llvm-project
Fold dangling-field warning into general initialization lifetime checks.
llvm-svn: 337627
This commit is contained in:
parent
a48d108353
commit
8aacc2cce6
|
@ -7870,11 +7870,11 @@ def note_ref_var_local_bind : Note<
|
|||
// Check for initializing a member variable with the address or a reference to
|
||||
// a constructor parameter.
|
||||
def warn_bind_ref_member_to_parameter : Warning<
|
||||
"binding reference member %0 to stack allocated parameter %1">,
|
||||
InGroup<DanglingField>;
|
||||
"binding reference member %0 to stack allocated "
|
||||
"%select{variable|parameter}2 %1">, InGroup<DanglingField>;
|
||||
def warn_init_ptr_member_to_parameter_addr : Warning<
|
||||
"initializing pointer member %0 with the stack address of parameter %1">,
|
||||
InGroup<DanglingField>;
|
||||
"initializing pointer member %0 with the stack address of "
|
||||
"%select{variable|parameter}2 %1">, InGroup<DanglingField>;
|
||||
def note_ref_or_ptr_member_declared_here : Note<
|
||||
"%select{reference|pointer}0 member declared here">;
|
||||
|
||||
|
|
|
@ -3946,53 +3946,6 @@ Sema::BuildMemInitializer(Decl *ConstructorD,
|
|||
return BuildBaseInitializer(BaseType, TInfo, Init, ClassDecl, EllipsisLoc);
|
||||
}
|
||||
|
||||
/// Checks a member initializer expression for cases where reference (or
|
||||
/// pointer) members are bound to by-value parameters (or their addresses).
|
||||
static void CheckForDanglingReferenceOrPointer(Sema &S, ValueDecl *Member,
|
||||
Expr *Init,
|
||||
SourceLocation IdLoc) {
|
||||
QualType MemberTy = Member->getType();
|
||||
|
||||
// We only handle pointers and references currently.
|
||||
// FIXME: Would this be relevant for ObjC object pointers? Or block pointers?
|
||||
if (!MemberTy->isReferenceType() && !MemberTy->isPointerType())
|
||||
return;
|
||||
|
||||
const bool IsPointer = MemberTy->isPointerType();
|
||||
if (IsPointer) {
|
||||
if (const UnaryOperator *Op
|
||||
= dyn_cast<UnaryOperator>(Init->IgnoreParenImpCasts())) {
|
||||
// The only case we're worried about with pointers requires taking the
|
||||
// address.
|
||||
if (Op->getOpcode() != UO_AddrOf)
|
||||
return;
|
||||
|
||||
Init = Op->getSubExpr();
|
||||
} else {
|
||||
// We only handle address-of expression initializers for pointers.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Init->IgnoreParens())) {
|
||||
// We only warn when referring to a non-reference parameter declaration.
|
||||
const ParmVarDecl *Parameter = dyn_cast<ParmVarDecl>(DRE->getDecl());
|
||||
if (!Parameter || Parameter->getType()->isReferenceType())
|
||||
return;
|
||||
|
||||
S.Diag(Init->getExprLoc(),
|
||||
IsPointer ? diag::warn_init_ptr_member_to_parameter_addr
|
||||
: diag::warn_bind_ref_member_to_parameter)
|
||||
<< Member << Parameter << Init->getSourceRange();
|
||||
} else {
|
||||
// Other initializers are fine.
|
||||
return;
|
||||
}
|
||||
|
||||
S.Diag(Member->getLocation(), diag::note_ref_or_ptr_member_declared_here)
|
||||
<< (unsigned)IsPointer;
|
||||
}
|
||||
|
||||
MemInitResult
|
||||
Sema::BuildMemberInitializer(ValueDecl *Member, Expr *Init,
|
||||
SourceLocation IdLoc) {
|
||||
|
@ -4047,8 +4000,6 @@ Sema::BuildMemberInitializer(ValueDecl *Member, Expr *Init,
|
|||
if (MemberInit.isInvalid())
|
||||
return true;
|
||||
|
||||
CheckForDanglingReferenceOrPointer(*this, Member, MemberInit.get(), IdLoc);
|
||||
|
||||
// C++11 [class.base.init]p7:
|
||||
// The initialization of each base and member constitutes a
|
||||
// full-expression.
|
||||
|
|
|
@ -6227,7 +6227,7 @@ using LifetimeResult =
|
|||
/// 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 LifetimeResult getEntityForTemporaryLifetimeExtension(
|
||||
static LifetimeResult getEntityLifetime(
|
||||
const InitializedEntity *Entity,
|
||||
const InitializedEntity *InitField = nullptr) {
|
||||
// C++11 [class.temporary]p5:
|
||||
|
@ -6239,8 +6239,7 @@ static LifetimeResult getEntityForTemporaryLifetimeExtension(
|
|||
case InitializedEntity::EK_Member:
|
||||
// For subobjects, we look at the complete object.
|
||||
if (Entity->getParent())
|
||||
return getEntityForTemporaryLifetimeExtension(Entity->getParent(),
|
||||
Entity);
|
||||
return getEntityLifetime(Entity->getParent(), Entity);
|
||||
|
||||
// except:
|
||||
// C++17 [class.base.init]p8:
|
||||
|
@ -6291,14 +6290,12 @@ static LifetimeResult getEntityForTemporaryLifetimeExtension(
|
|||
|
||||
case InitializedEntity::EK_ArrayElement:
|
||||
// For subobjects, we look at the complete object.
|
||||
return getEntityForTemporaryLifetimeExtension(Entity->getParent(),
|
||||
InitField);
|
||||
return getEntityLifetime(Entity->getParent(), InitField);
|
||||
|
||||
case InitializedEntity::EK_Base:
|
||||
// For subobjects, we look at the complete object.
|
||||
if (Entity->getParent())
|
||||
return getEntityForTemporaryLifetimeExtension(Entity->getParent(),
|
||||
InitField);
|
||||
return getEntityLifetime(Entity->getParent(), InitField);
|
||||
return {InitField, LK_MemInitializer};
|
||||
|
||||
case InitializedEntity::EK_Delegating:
|
||||
|
@ -6311,46 +6308,61 @@ static LifetimeResult getEntityForTemporaryLifetimeExtension(
|
|||
case InitializedEntity::EK_BlockElement:
|
||||
case InitializedEntity::EK_LambdaToBlockConversionBlockElement:
|
||||
case InitializedEntity::EK_LambdaCapture:
|
||||
case InitializedEntity::EK_Exception:
|
||||
case InitializedEntity::EK_VectorElement:
|
||||
case InitializedEntity::EK_ComplexElement:
|
||||
return {nullptr, LK_FullExpression};
|
||||
|
||||
case InitializedEntity::EK_Exception:
|
||||
// FIXME: Can we diagnose lifetime problems with exceptions?
|
||||
return {nullptr, LK_FullExpression};
|
||||
}
|
||||
llvm_unreachable("unknown entity kind");
|
||||
}
|
||||
|
||||
namespace {
|
||||
enum ExtensionKind {
|
||||
enum ReferenceKind {
|
||||
/// Lifetime would be extended by a reference binding to a temporary.
|
||||
EK_ReferenceBinding,
|
||||
RK_ReferenceBinding,
|
||||
/// Lifetime would be extended by a std::initializer_list object binding to
|
||||
/// its backing array.
|
||||
EK_StdInitializerList,
|
||||
RK_StdInitializerList,
|
||||
};
|
||||
using IndirectTemporaryPathEntry =
|
||||
llvm::PointerUnion<CXXDefaultInitExpr *, ValueDecl *>;
|
||||
using IndirectTemporaryPath = llvm::SmallVectorImpl<IndirectTemporaryPathEntry>;
|
||||
|
||||
/// A temporary or local variable.
|
||||
using Local = llvm::PointerUnion<MaterializeTemporaryExpr*, ValueDecl*>;
|
||||
|
||||
/// Expressions we stepped over when looking for the local state. Any steps
|
||||
/// that would inhibit lifetime extension or take us out of subexpressions of
|
||||
/// the initializer are included.
|
||||
struct IndirectLocalPathEntry {
|
||||
enum {
|
||||
DefaultInit,
|
||||
AddressOf,
|
||||
} Kind;
|
||||
Expr *E;
|
||||
};
|
||||
|
||||
using IndirectLocalPath = llvm::SmallVectorImpl<IndirectLocalPathEntry>;
|
||||
|
||||
struct RevertToOldSizeRAII {
|
||||
IndirectTemporaryPath &Path;
|
||||
IndirectLocalPath &Path;
|
||||
unsigned OldSize = Path.size();
|
||||
RevertToOldSizeRAII(IndirectTemporaryPath &Path) : Path(Path) {}
|
||||
RevertToOldSizeRAII(IndirectLocalPath &Path) : Path(Path) {}
|
||||
~RevertToOldSizeRAII() { Path.resize(OldSize); }
|
||||
};
|
||||
}
|
||||
|
||||
template <typename TemporaryVisitor>
|
||||
static void visitTemporariesExtendedByInitializer(IndirectTemporaryPath &Path,
|
||||
Expr *Init,
|
||||
TemporaryVisitor Visit);
|
||||
template <typename LocalVisitor>
|
||||
static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
|
||||
Expr *Init, LocalVisitor Visit,
|
||||
bool RevisitSubinits);
|
||||
|
||||
/// 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) {
|
||||
/// Visit the locals that would be reachable through a reference bound to the
|
||||
/// glvalue expression \c Init.
|
||||
template <typename LocalVisitor>
|
||||
static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,
|
||||
Expr *Init, ReferenceKind RK,
|
||||
LocalVisitor Visit) {
|
||||
RevertToOldSizeRAII RAII(Path);
|
||||
|
||||
// Walk past any constructs which we can lifetime-extend across.
|
||||
|
@ -6382,7 +6394,7 @@ visitTemporariesExtendedByReferenceBinding(IndirectTemporaryPath &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);
|
||||
Path.push_back({IndirectLocalPathEntry::DefaultInit, DIE});
|
||||
Init = DIE->getExpr();
|
||||
|
||||
if (auto *EWC = dyn_cast<ExprWithCleanups>(Init))
|
||||
|
@ -6391,25 +6403,36 @@ visitTemporariesExtendedByReferenceBinding(IndirectTemporaryPath &Path,
|
|||
} while (Init != Old);
|
||||
|
||||
if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Init)) {
|
||||
if (Visit(Path, MTE, EK))
|
||||
visitTemporariesExtendedByInitializer(Path, MTE->GetTemporaryExpr(),
|
||||
Visit);
|
||||
if (Visit(Path, Local(MTE), RK))
|
||||
visitLocalsRetainedByInitializer(Path, MTE->GetTemporaryExpr(), Visit,
|
||||
true);
|
||||
}
|
||||
|
||||
// If we find the name of a local non-reference parameter, we could have a
|
||||
// lifetime problem.
|
||||
if (auto *DRE = dyn_cast<DeclRefExpr>(Init->IgnoreParens())) {
|
||||
auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
|
||||
if (VD && VD->hasLocalStorage() &&
|
||||
!DRE->refersToEnclosingVariableOrCapture()) {
|
||||
// FIXME: Recurse to the initializer of a local reference.
|
||||
if (!VD->getType()->isReferenceType())
|
||||
Visit(Path, Local(VD), RK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
/// Visit the locals that would be reachable through an object initialized by
|
||||
/// the prvalue expression \c Init.
|
||||
template <typename LocalVisitor>
|
||||
static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
|
||||
Expr *Init, LocalVisitor Visit,
|
||||
bool RevisitSubinits) {
|
||||
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);
|
||||
Path.push_back({IndirectLocalPathEntry::DefaultInit, DIE});
|
||||
Init = DIE->getExpr();
|
||||
|
||||
if (auto *EWC = dyn_cast<ExprWithCleanups>(Init))
|
||||
|
@ -6426,17 +6449,24 @@ static void visitTemporariesExtendedByInitializer(IndirectTemporaryPath &Path,
|
|||
// 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);
|
||||
return visitLocalsRetainedByReferenceBinding(Path, ILE->getSubExpr(),
|
||||
RK_StdInitializerList, Visit);
|
||||
|
||||
if (InitListExpr *ILE = dyn_cast<InitListExpr>(Init)) {
|
||||
// We already visited the elements of this initializer list while
|
||||
// performing the initialization. Don't visit them again unless we've
|
||||
// changed the lifetime of the initialized entity.
|
||||
if (!RevisitSubinits)
|
||||
return;
|
||||
|
||||
if (ILE->isTransparent())
|
||||
return visitTemporariesExtendedByInitializer(Path, ILE->getInit(0),
|
||||
Visit);
|
||||
return visitLocalsRetainedByInitializer(Path, ILE->getInit(0), Visit,
|
||||
RevisitSubinits);
|
||||
|
||||
if (ILE->getType()->isArrayType()) {
|
||||
for (unsigned I = 0, N = ILE->getNumInits(); I != N; ++I)
|
||||
visitTemporariesExtendedByInitializer(Path, ILE->getInit(I), Visit);
|
||||
visitLocalsRetainedByInitializer(Path, ILE->getInit(I), Visit,
|
||||
RevisitSubinits);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -6448,8 +6478,8 @@ static void visitTemporariesExtendedByInitializer(IndirectTemporaryPath &Path,
|
|||
// bound to temporaries, those temporaries are also lifetime-extended.
|
||||
if (RD->isUnion() && ILE->getInitializedFieldInUnion() &&
|
||||
ILE->getInitializedFieldInUnion()->getType()->isReferenceType())
|
||||
visitTemporariesExtendedByReferenceBinding(Path, ILE->getInit(0),
|
||||
EK_ReferenceBinding, Visit);
|
||||
visitLocalsRetainedByReferenceBinding(Path, ILE->getInit(0),
|
||||
RK_ReferenceBinding, Visit);
|
||||
else {
|
||||
unsigned Index = 0;
|
||||
for (const auto *I : RD->fields()) {
|
||||
|
@ -6459,25 +6489,38 @@ static void visitTemporariesExtendedByInitializer(IndirectTemporaryPath &Path,
|
|||
continue;
|
||||
Expr *SubInit = ILE->getInit(Index);
|
||||
if (I->getType()->isReferenceType())
|
||||
visitTemporariesExtendedByReferenceBinding(
|
||||
Path, SubInit, EK_ReferenceBinding, Visit);
|
||||
visitLocalsRetainedByReferenceBinding(Path, SubInit,
|
||||
RK_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.
|
||||
visitTemporariesExtendedByInitializer(Path, SubInit, Visit);
|
||||
visitLocalsRetainedByInitializer(Path, SubInit, Visit,
|
||||
RevisitSubinits);
|
||||
++Index;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If the initializer is the address of a local, we could have a lifetime
|
||||
// problem.
|
||||
if (auto *Op = dyn_cast<UnaryOperator>(Init->IgnoreParenImpCasts())) {
|
||||
if (Op->getOpcode() == UO_AddrOf) {
|
||||
Path.push_back({IndirectLocalPathEntry::AddressOf, Op});
|
||||
Init = Op->getSubExpr();
|
||||
return visitLocalsRetainedByReferenceBinding(Path, Init,
|
||||
RK_ReferenceBinding, Visit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
static bool shouldLifetimeExtendThroughPath(const IndirectLocalPath &Path) {
|
||||
for (auto Elem : Path) {
|
||||
if (!Elem.is<CXXDefaultInitExpr*>())
|
||||
if (Elem.Kind != IndirectLocalPathEntry::DefaultInit)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -6485,7 +6528,7 @@ static bool shouldLifetimeExtendThroughPath(const IndirectTemporaryPath &Path) {
|
|||
|
||||
void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
|
||||
Expr *Init) {
|
||||
LifetimeResult LR = getEntityForTemporaryLifetimeExtension(&Entity);
|
||||
LifetimeResult LR = getEntityLifetime(&Entity);
|
||||
LifetimeKind LK = LR.getInt();
|
||||
const InitializedEntity *ExtendingEntity = LR.getPointer();
|
||||
|
||||
|
@ -6494,9 +6537,54 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
|
|||
if (LK == LK_FullExpression)
|
||||
return;
|
||||
|
||||
auto TemporaryVisitor = [&](IndirectTemporaryPath &Path,
|
||||
MaterializeTemporaryExpr *MTE,
|
||||
ExtensionKind EK) -> bool {
|
||||
auto TemporaryVisitor = [&](IndirectLocalPath &Path, Local L,
|
||||
ReferenceKind RK) -> bool {
|
||||
// If we found a path to a local variable or similar, check whether the
|
||||
// initialized object will outlive it.
|
||||
if (auto *VD = L.dyn_cast<ValueDecl*>()) {
|
||||
switch (LK) {
|
||||
case LK_FullExpression:
|
||||
llvm_unreachable("already handled this");
|
||||
|
||||
case LK_Extended:
|
||||
break;
|
||||
|
||||
case LK_MemInitializer: {
|
||||
// Paths via a default initializer can only occur during error recovery
|
||||
// (there's no other way that a default initializer can refer to a
|
||||
// local). Don't produce a bogus warning on those cases.
|
||||
if (std::any_of(Path.begin(), Path.end(), [](IndirectLocalPathEntry E) {
|
||||
return E.Kind == IndirectLocalPathEntry::DefaultInit;
|
||||
}))
|
||||
break;
|
||||
|
||||
if (auto *Member =
|
||||
ExtendingEntity ? ExtendingEntity->getDecl() : nullptr) {
|
||||
bool AddressTaken =
|
||||
!Path.empty() &&
|
||||
Path.back().Kind == IndirectLocalPathEntry::AddressOf;
|
||||
Diag(Init->getExprLoc(),
|
||||
AddressTaken ? diag::warn_init_ptr_member_to_parameter_addr
|
||||
: diag::warn_bind_ref_member_to_parameter)
|
||||
<< Member << VD << isa<ParmVarDecl>(VD) << Init->getSourceRange();
|
||||
Diag(Member->getLocation(),
|
||||
diag::note_ref_or_ptr_member_declared_here)
|
||||
<< (unsigned)AddressTaken;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case LK_New:
|
||||
break;
|
||||
|
||||
case LK_Return:
|
||||
// FIXME: Move -Wreturn-stack-address checks here.
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto *MTE = L.get<MaterializeTemporaryExpr*>();
|
||||
switch (LK) {
|
||||
case LK_FullExpression:
|
||||
llvm_unreachable("already handled this");
|
||||
|
@ -6520,11 +6608,11 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
|
|||
// would be to clone the initializer expression on each use that would
|
||||
// lifetime extend its temporaries.
|
||||
Diag(MTE->getExprLoc(),
|
||||
EK == EK_ReferenceBinding
|
||||
RK == RK_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");
|
||||
// FIXME: Warn on this.
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -6532,18 +6620,20 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
|
|||
// 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()) {
|
||||
if (auto *ExtendingDecl =
|
||||
ExtendingEntity ? ExtendingEntity->getDecl() : nullptr) {
|
||||
bool IsSubobjectMember = ExtendingEntity != &Entity;
|
||||
Diag(MTE->getExprLoc(), diag::err_bind_ref_member_to_temporary)
|
||||
<< ExtendingDecl << Init->getSourceRange() << IsSubobjectMember
|
||||
<< EK;
|
||||
<< RK;
|
||||
// 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*>()) {
|
||||
if (Path.empty() ||
|
||||
Path.back().Kind != IndirectLocalPathEntry::DefaultInit) {
|
||||
Diag(ExtendingDecl->getLocation(),
|
||||
diag::note_lifetime_extending_member_declared_here)
|
||||
<< EK << IsSubobjectMember;
|
||||
<< RK << IsSubobjectMember;
|
||||
}
|
||||
} else {
|
||||
// We have a mem-initializer but no particular field within it; this
|
||||
|
@ -6557,7 +6647,7 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
|
|||
break;
|
||||
|
||||
case LK_New:
|
||||
if (EK == EK_ReferenceBinding) {
|
||||
if (RK == RK_ReferenceBinding) {
|
||||
Diag(MTE->getExprLoc(), diag::warn_new_dangling_reference);
|
||||
} else {
|
||||
Diag(MTE->getExprLoc(), diag::warn_new_dangling_initializer_list)
|
||||
|
@ -6573,9 +6663,14 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
|
|||
// 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();
|
||||
switch (Elem.Kind) {
|
||||
case IndirectLocalPathEntry::DefaultInit:
|
||||
Diag(Elem.E->getExprLoc(), diag::note_in_default_member_initalizer_here)
|
||||
<< cast<CXXDefaultInitExpr>(Elem.E)->getField();
|
||||
break;
|
||||
|
||||
case IndirectLocalPathEntry::AddressOf:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6584,12 +6679,12 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
|
|||
return false;
|
||||
};
|
||||
|
||||
llvm::SmallVector<IndirectTemporaryPathEntry, 8> Path;
|
||||
llvm::SmallVector<IndirectLocalPathEntry, 8> Path;
|
||||
if (Init->isGLValue())
|
||||
visitTemporariesExtendedByReferenceBinding(Path, Init, EK_ReferenceBinding,
|
||||
TemporaryVisitor);
|
||||
visitLocalsRetainedByReferenceBinding(Path, Init, RK_ReferenceBinding,
|
||||
TemporaryVisitor);
|
||||
else
|
||||
visitTemporariesExtendedByInitializer(Path, Init, TemporaryVisitor);
|
||||
visitLocalsRetainedByInitializer(Path, Init, TemporaryVisitor, false);
|
||||
}
|
||||
|
||||
static void DiagnoseNarrowingInInitList(Sema &S,
|
||||
|
@ -6853,6 +6948,7 @@ InitializationSequence::Perform(Sema &S,
|
|||
|
||||
// Diagnose cases where we initialize a pointer to an array temporary, and the
|
||||
// pointer obviously outlives the temporary.
|
||||
// FIXME: Fold this into checkInitializerLifetime.
|
||||
if (Args.size() == 1 && Args[0]->getType()->isArrayType() &&
|
||||
Entity.getType()->isPointerType() &&
|
||||
InitializedEntityOutlivesFullExpression(Entity)) {
|
||||
|
@ -7015,11 +7111,6 @@ 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.
|
||||
S.checkInitializerLifetime(Entity, CurInit.get());
|
||||
|
||||
CheckForNullPointerDereference(S, CurInit.get());
|
||||
break;
|
||||
|
||||
|
@ -7036,10 +7127,6 @@ InitializationSequence::Perform(Sema &S,
|
|||
Step->Type, CurInit.get(), Entity.getType()->isLValueReferenceType());
|
||||
CurInit = MTE;
|
||||
|
||||
// Maybe lifetime-extend the temporary's subobjects to match the
|
||||
// entity's lifetime.
|
||||
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 &&
|
||||
|
@ -7490,10 +7577,6 @@ InitializationSequence::Perform(Sema &S,
|
|||
// 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))
|
||||
|
@ -7612,6 +7695,11 @@ InitializationSequence::Perform(Sema &S,
|
|||
}
|
||||
}
|
||||
|
||||
// Check whether the initializer has a shorter lifetime than the initialized
|
||||
// entity, and if not, either lifetime-extend or warn as appropriate.
|
||||
if (auto *Init = CurInit.get())
|
||||
S.checkInitializerLifetime(Entity, Init);
|
||||
|
||||
// Diagnose non-fatal problems with the completed initialization.
|
||||
if (Entity.getKind() == InitializedEntity::EK_Member &&
|
||||
cast<FieldDecl>(Entity.getDecl())->isBitField())
|
||||
|
|
|
@ -235,10 +235,10 @@ void fVoidPointerTest2() {
|
|||
}
|
||||
|
||||
class VoidPointerRRefTest1 {
|
||||
void *&&vptrrref;
|
||||
void *&&vptrrref; // expected-note {{here}}
|
||||
|
||||
public:
|
||||
VoidPointerRRefTest1(void *vptr, char) : vptrrref(static_cast<void *&&>(vptr)) {
|
||||
VoidPointerRRefTest1(void *vptr, char) : vptrrref(static_cast<void *&&>(vptr)) { // expected-warning {{binding reference member 'vptrrref' to stack allocated parameter 'vptr'}}
|
||||
// All good!
|
||||
}
|
||||
};
|
||||
|
@ -249,10 +249,10 @@ void fVoidPointerRRefTest1() {
|
|||
}
|
||||
|
||||
class VoidPointerRRefTest2 {
|
||||
void **&&vpptrrref;
|
||||
void **&&vpptrrref; // expected-note {{here}}
|
||||
|
||||
public:
|
||||
VoidPointerRRefTest2(void **vptr, char) : vpptrrref(static_cast<void **&&>(vptr)) {
|
||||
VoidPointerRRefTest2(void **vptr, char) : vpptrrref(static_cast<void **&&>(vptr)) { // expected-warning {{binding reference member 'vpptrrref' to stack allocated parameter 'vptr'}}
|
||||
// All good!
|
||||
}
|
||||
};
|
||||
|
@ -263,10 +263,10 @@ void fVoidPointerRRefTest2() {
|
|||
}
|
||||
|
||||
class VoidPointerLRefTest {
|
||||
void *&vptrrref;
|
||||
void *&vptrrref; // expected-note {{here}}
|
||||
|
||||
public:
|
||||
VoidPointerLRefTest(void *vptr, char) : vptrrref(static_cast<void *&>(vptr)) {
|
||||
VoidPointerLRefTest(void *vptr, char) : vptrrref(static_cast<void *&>(vptr)) { // expected-warning {{binding reference member 'vptrrref' to stack allocated parameter 'vptr'}}
|
||||
// All good!
|
||||
}
|
||||
};
|
||||
|
|
|
@ -72,18 +72,18 @@ std::initializer_list<int> thread_local x = {1, 2, 3, 4};
|
|||
|
||||
// X86: @_ZN15partly_constant1kE = global i32 0, align 4
|
||||
// X86: @_ZN15partly_constant2ilE = global {{.*}} null, align 8
|
||||
// X86: @[[PARTLY_CONSTANT_OUTER:_ZGRN15partly_constant2ilE.*]] = internal global {{.*}} zeroinitializer, align 8
|
||||
// X86: @[[PARTLY_CONSTANT_INNER:_ZGRN15partly_constant2ilE.*]] = internal global [3 x {{.*}}] zeroinitializer, align 8
|
||||
// X86: @[[PARTLY_CONSTANT_FIRST:_ZGRN15partly_constant2ilE.*]] = internal constant [3 x i32] [i32 1, i32 2, i32 3], align 4
|
||||
// X86: @[[PARTLY_CONSTANT_SECOND:_ZGRN15partly_constant2ilE.*]] = internal global [2 x i32] zeroinitializer, align 4
|
||||
// X86: @[[PARTLY_CONSTANT_THIRD:_ZGRN15partly_constant2ilE.*]] = internal constant [4 x i32] [i32 5, i32 6, i32 7, i32 8], align 4
|
||||
// X86: @[[PARTLY_CONSTANT_OUTER:_ZGRN15partly_constant2ilE_]] = internal global {{.*}} zeroinitializer, align 8
|
||||
// X86: @[[PARTLY_CONSTANT_INNER:_ZGRN15partly_constant2ilE0_]] = internal global [3 x {{.*}}] zeroinitializer, align 8
|
||||
// X86: @[[PARTLY_CONSTANT_FIRST:_ZGRN15partly_constant2ilE1_]] = internal constant [3 x i32] [i32 1, i32 2, i32 3], align 4
|
||||
// X86: @[[PARTLY_CONSTANT_SECOND:_ZGRN15partly_constant2ilE2_]] = internal global [2 x i32] zeroinitializer, align 4
|
||||
// X86: @[[PARTLY_CONSTANT_THIRD:_ZGRN15partly_constant2ilE3_]] = internal constant [4 x i32] [i32 5, i32 6, i32 7, i32 8], align 4
|
||||
// AMDGCN: @_ZN15partly_constant1kE = addrspace(1) global i32 0, align 4
|
||||
// AMDGCN: @_ZN15partly_constant2ilE = addrspace(4) global {{.*}} null, align 8
|
||||
// AMDGCN: @[[PARTLY_CONSTANT_OUTER:_ZGRN15partly_constant2ilE.*]] = internal addrspace(4) global {{.*}} zeroinitializer, align 8
|
||||
// AMDGCN: @[[PARTLY_CONSTANT_INNER:_ZGRN15partly_constant2ilE.*]] = internal addrspace(4) global [3 x {{.*}}] zeroinitializer, align 8
|
||||
// AMDGCN: @[[PARTLY_CONSTANT_FIRST:_ZGRN15partly_constant2ilE.*]] = internal addrspace(4) constant [3 x i32] [i32 1, i32 2, i32 3], align 4
|
||||
// AMDGCN: @[[PARTLY_CONSTANT_SECOND:_ZGRN15partly_constant2ilE.*]] = internal addrspace(4) global [2 x i32] zeroinitializer, align 4
|
||||
// AMDGCN: @[[PARTLY_CONSTANT_THIRD:_ZGRN15partly_constant2ilE.*]] = internal addrspace(4) constant [4 x i32] [i32 5, i32 6, i32 7, i32 8], align 4
|
||||
// AMDGCN: @[[PARTLY_CONSTANT_OUTER:_ZGRN15partly_constant2ilE_]] = internal addrspace(4) global {{.*}} zeroinitializer, align 8
|
||||
// AMDGCN: @[[PARTLY_CONSTANT_INNER:_ZGRN15partly_constant2ilE0_]] = internal addrspace(4) global [3 x {{.*}}] zeroinitializer, align 8
|
||||
// AMDGCN: @[[PARTLY_CONSTANT_FIRST:_ZGRN15partly_constant2ilE1_]] = internal addrspace(4) constant [3 x i32] [i32 1, i32 2, i32 3], align 4
|
||||
// AMDGCN: @[[PARTLY_CONSTANT_SECOND:_ZGRN15partly_constant2ilE2_]] = internal addrspace(4) global [2 x i32] zeroinitializer, align 4
|
||||
// AMDGCN: @[[PARTLY_CONSTANT_THIRD:_ZGRN15partly_constant2ilE3_]] = internal addrspace(4) constant [4 x i32] [i32 5, i32 6, i32 7, i32 8], align 4
|
||||
|
||||
// X86: @[[REFTMP1:.*]] = private constant [2 x i32] [i32 42, i32 43], align 4
|
||||
// X86: @[[REFTMP2:.*]] = private constant [3 x %{{.*}}] [%{{.*}} { i32 1 }, %{{.*}} { i32 2 }, %{{.*}} { i32 3 }], align 4
|
||||
|
@ -375,7 +375,7 @@ namespace partly_constant {
|
|||
// CHECK-NOT: @[[PARTLY_CONSTANT_THIRD]],
|
||||
// CHECK: store i32* getelementptr inbounds ({{.*}}, {{.*}}* {{.*}}@[[PARTLY_CONSTANT_THIRD]]{{.*}}, i64 0, i64 0),
|
||||
// CHECK: i32** getelementptr inbounds ({{.*}}, {{.*}}* {{.*}}@[[PARTLY_CONSTANT_INNER]]{{.*}}, i64 0, i64 2, i32 0)
|
||||
// CHECK: store i64 4, i64* getelementptr inbounds ({{.*}}, {{.*}}* {{.*}}@_ZGRN15partly_constant2ilE4_{{.*}}, i64 0, i64 2, i32 1)
|
||||
// CHECK: store i64 4, i64* getelementptr inbounds ({{.*}}, {{.*}}* {{.*}}@[[PARTLY_CONSTANT_INNER]]{{.*}}, i64 0, i64 2, i32 1)
|
||||
// CHECK-NOT: @[[PARTLY_CONSTANT_THIRD]],
|
||||
//
|
||||
// Outer init list.
|
||||
|
|
Loading…
Reference in New Issue