Expand Up
@@ -6322,12 +6322,14 @@ struct IndirectLocalPathEntry {
AddressOf,
VarInit,
LValToRVal,
LifetimeBoundCall,
} Kind;
Expr *E;
Decl *D = nullptr ;
const Decl *D = nullptr ;
IndirectLocalPathEntry () {}
IndirectLocalPathEntry (EntryKind K, Expr *E) : Kind(K), E(E) {}
IndirectLocalPathEntry (EntryKind K, Expr *E, Decl *D) : Kind(K), E(E), D(D) {}
IndirectLocalPathEntry (EntryKind K, Expr *E, const Decl *D)
: Kind(K), E(E), D(D) {}
};
using IndirectLocalPath = llvm::SmallVectorImpl<IndirectLocalPathEntry>;
Expand Down
Expand Up
@@ -6361,6 +6363,68 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
Expr *Init, LocalVisitor Visit,
bool RevisitSubinits);
static void visitLocalsRetainedByReferenceBinding (IndirectLocalPath &Path,
Expr *Init, ReferenceKind RK,
LocalVisitor Visit);
static bool implicitObjectParamIsLifetimeBound (const FunctionDecl *FD) {
const TypeSourceInfo *TSI = FD->getTypeSourceInfo ();
if (!TSI)
return false ;
for (TypeLoc TL = TSI->getTypeLoc ();
auto ATL = TL.getAsAdjusted <AttributedTypeLoc>();
TL = ATL.getModifiedLoc ()) {
if (ATL.getAttrKind () == AttributedType::attr_lifetimebound)
return true ;
}
return false ;
}
static void visitLifetimeBoundArguments (IndirectLocalPath &Path, Expr *Call,
LocalVisitor Visit) {
const FunctionDecl *Callee;
ArrayRef<Expr*> Args;
if (auto *CE = dyn_cast<CallExpr>(Call)) {
Callee = CE->getDirectCallee ();
Args = llvm::makeArrayRef (CE->getArgs (), CE->getNumArgs ());
} else {
auto *CCE = cast<CXXConstructExpr>(Call);
Callee = CCE->getConstructor ();
Args = llvm::makeArrayRef (CCE->getArgs (), CCE->getNumArgs ());
}
if (!Callee)
return ;
Expr *ObjectArg = nullptr ;
if (isa<CXXOperatorCallExpr>(Call) && Callee->isCXXInstanceMember ()) {
ObjectArg = Args[0 ];
Args = Args.slice (1 );
} else if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Call)) {
ObjectArg = MCE->getImplicitObjectArgument ();
}
auto VisitLifetimeBoundArg = [&](const Decl *D, Expr *Arg) {
Path.push_back ({IndirectLocalPathEntry::LifetimeBoundCall, Arg, D});
if (Arg->isGLValue ())
visitLocalsRetainedByReferenceBinding (Path, Arg, RK_ReferenceBinding,
Visit);
else
visitLocalsRetainedByInitializer (Path, Arg, Visit, true );
Path.pop_back ();
};
if (ObjectArg && implicitObjectParamIsLifetimeBound (Callee))
VisitLifetimeBoundArg (Callee, ObjectArg);
for (unsigned I = 0 ,
N = std::min<unsigned >(Callee->getNumParams (), Args.size ());
I != N; ++I) {
if (Callee->getParamDecl (I)->hasAttr <LifetimeBoundAttr>())
VisitLifetimeBoundArg (Callee->getParamDecl (I), Args[I]);
}
}
// / Visit the locals that would be reachable through a reference bound to the
// / glvalue expression \c Init.
static void visitLocalsRetainedByReferenceBinding (IndirectLocalPath &Path,
Expand Down
Expand Up
@@ -6420,6 +6484,9 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,
true );
}
if (isa<CallExpr>(Init))
return visitLifetimeBoundArguments (Path, Init, Visit);
switch (Init->getStmtClass ()) {
case Stmt::DeclRefExprClass: {
// If we find the name of a local non-reference parameter, we could have a
Expand Down
Expand Up
@@ -6483,21 +6550,90 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
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 ({IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField ()});
Init = DIE->getExpr ();
}
Expr *Old;
do {
Old = Init;
if (auto *EWC = dyn_cast<ExprWithCleanups>(Init))
Init = EWC->getSubExpr ();
// 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 ({IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField ()});
Init = DIE->getExpr ();
}
// Dig out the expression which constructs the extended temporary.
Init = const_cast <Expr *>(Init->skipRValueSubobjectAdjustments ());
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 ();
Init = Init->IgnoreParens ();
// Step over value-preserving rvalue casts.
if (auto *CE = dyn_cast<CastExpr>(Init)) {
switch (CE->getCastKind ()) {
case CK_LValueToRValue:
// If we can match the lvalue to a const object, we can look at its
// initializer.
Path.push_back ({IndirectLocalPathEntry::LValToRVal, CE});
return visitLocalsRetainedByReferenceBinding (
Path, Init, RK_ReferenceBinding,
[&](IndirectLocalPath &Path, Local L, ReferenceKind RK) -> bool {
if (auto *DRE = dyn_cast<DeclRefExpr>(L)) {
auto *VD = dyn_cast<VarDecl>(DRE->getDecl ());
if (VD && VD->getType ().isConstQualified () && VD->getInit () &&
!isVarOnPath (Path, VD)) {
Path.push_back ({IndirectLocalPathEntry::VarInit, DRE, VD});
visitLocalsRetainedByInitializer (Path, VD->getInit (), Visit, true );
}
} else if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L)) {
if (MTE->getType ().isConstQualified ())
visitLocalsRetainedByInitializer (Path, MTE->GetTemporaryExpr (),
Visit, true );
}
return false ;
});
// We assume that objects can be retained by pointers cast to integers,
// but not if the integer is cast to floating-point type or to _Complex.
// We assume that casts to 'bool' do not preserve enough information to
// retain a local object.
case CK_NoOp:
case CK_BitCast:
case CK_BaseToDerived:
case CK_DerivedToBase:
case CK_UncheckedDerivedToBase:
case CK_Dynamic:
case CK_ToUnion:
case CK_UserDefinedConversion:
case CK_ConstructorConversion:
case CK_IntegralToPointer:
case CK_PointerToIntegral:
case CK_VectorSplat:
case CK_IntegralCast:
case CK_CPointerToObjCPointerCast:
case CK_BlockPointerToObjCPointerCast:
case CK_AnyPointerToBlockPointerCast:
case CK_AddressSpaceConversion:
break ;
case CK_ArrayToPointerDecay:
// Model array-to-pointer decay as taking the address of the array
// lvalue.
Path.push_back ({IndirectLocalPathEntry::AddressOf, CE});
return visitLocalsRetainedByReferenceBinding (Path, CE->getSubExpr (),
RK_ReferenceBinding, Visit);
default :
return ;
}
if (CXXBindTemporaryExpr *BTE = dyn_cast<CXXBindTemporaryExpr>(Init))
Init = BTE->getSubExpr ();
Init = CE->getSubExpr ();
}
} while (Old != Init);
// C++17 [dcl.init.list]p6:
// initializing an initializer_list object from the array extends the
Expand Down
Expand Up
@@ -6558,67 +6694,9 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
return ;
}
// Step over value-preserving rvalue casts.
while (auto *CE = dyn_cast<CastExpr>(Init)) {
switch (CE->getCastKind ()) {
case CK_LValueToRValue:
// If we can match the lvalue to a const object, we can look at its
// initializer.
Path.push_back ({IndirectLocalPathEntry::LValToRVal, CE});
return visitLocalsRetainedByReferenceBinding (
Path, Init, RK_ReferenceBinding,
[&](IndirectLocalPath &Path, Local L, ReferenceKind RK) -> bool {
if (auto *DRE = dyn_cast<DeclRefExpr>(L)) {
auto *VD = dyn_cast<VarDecl>(DRE->getDecl ());
if (VD && VD->getType ().isConstQualified () && VD->getInit () &&
!isVarOnPath (Path, VD)) {
Path.push_back ({IndirectLocalPathEntry::VarInit, DRE, VD});
visitLocalsRetainedByInitializer (Path, VD->getInit (), Visit, true );
}
} else if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L)) {
if (MTE->getType ().isConstQualified ())
visitLocalsRetainedByInitializer (Path, MTE->GetTemporaryExpr (),
Visit, true );
}
return false ;
});
// We assume that objects can be retained by pointers cast to integers,
// but not if the integer is cast to floating-point type or to _Complex.
// We assume that casts to 'bool' do not preserve enough information to
// retain a local object.
case CK_NoOp:
case CK_BitCast:
case CK_BaseToDerived:
case CK_DerivedToBase:
case CK_UncheckedDerivedToBase:
case CK_Dynamic:
case CK_ToUnion:
case CK_IntegralToPointer:
case CK_PointerToIntegral:
case CK_VectorSplat:
case CK_IntegralCast:
case CK_CPointerToObjCPointerCast:
case CK_BlockPointerToObjCPointerCast:
case CK_AnyPointerToBlockPointerCast:
case CK_AddressSpaceConversion:
break ;
case CK_ArrayToPointerDecay:
// Model array-to-pointer decay as taking the address of the array
// lvalue.
Path.push_back ({IndirectLocalPathEntry::AddressOf, CE});
return visitLocalsRetainedByReferenceBinding (Path, CE->getSubExpr (),
RK_ReferenceBinding, Visit);
default :
return ;
}
Init = CE->getSubExpr ();
}
if (isa<CallExpr>(Init) || isa<CXXConstructExpr>(Init))
return visitLifetimeBoundArguments (Path, Init, Visit);
Init = Init->IgnoreParens ();
switch (Init->getStmtClass ()) {
case Stmt::UnaryOperatorClass: {
auto *UO = cast<UnaryOperator>(Init);
Expand Down
Expand Up
@@ -6698,6 +6776,7 @@ static SourceRange nextPathEntryRange(const IndirectLocalPath &Path, unsigned I,
switch (Path[I].Kind ) {
case IndirectLocalPathEntry::AddressOf:
case IndirectLocalPathEntry::LValToRVal:
case IndirectLocalPathEntry::LifetimeBoundCall:
// These exist primarily to mark the path as not permitting or
// supporting lifetime extension.
break ;
Expand Down
Expand Up
@@ -6876,6 +6955,10 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
// supporting lifetime extension.
break ;
case IndirectLocalPathEntry::LifetimeBoundCall:
// FIXME: Consider adding a note for this.
break ;
case IndirectLocalPathEntry::DefaultInit: {
auto *FD = cast<FieldDecl>(Elem.D );
Diag (FD->getLocation (), diag::note_init_with_default_member_initalizer)
Expand Down