diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2018-07-23 21:21:22 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2018-07-23 21:21:22 +0000 |
commit | afe48f9d68e446829bed6ce72b319c915873809a (patch) | |
tree | cf4d1852819f96f709701e9e7c9b6088d77d74c5 /clang/lib/Sema/SemaInit.cpp | |
parent | f9f50f634d4d6cae8d844b5a7b764e1159dce8e4 (diff) | |
download | bcm5719-llvm-afe48f9d68e446829bed6ce72b319c915873809a.tar.gz bcm5719-llvm-afe48f9d68e446829bed6ce72b319c915873809a.zip |
Fold -Wreturn-stack-address into general initialization lifetime
checking.
llvm-svn: 337743
Diffstat (limited to 'clang/lib/Sema/SemaInit.cpp')
-rw-r--r-- | clang/lib/Sema/SemaInit.cpp | 436 |
1 files changed, 318 insertions, 118 deletions
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index e457de4dcc7..5c891bebea8 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -15,6 +15,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" +#include "clang/AST/ExprOpenMP.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/TargetInfo.h" #include "clang/Sema/Designator.h" @@ -6348,18 +6349,28 @@ enum ReferenceKind { RK_StdInitializerList, }; -/// A temporary or local variable. -using Local = llvm::PointerUnion<MaterializeTemporaryExpr*, ValueDecl*>; +/// A temporary or local variable. This will be one of: +/// * A MaterializeTemporaryExpr. +/// * A DeclRefExpr whose declaration is a local. +/// * An AddrLabelExpr. +/// * A BlockExpr for a block with captures. +using Local = Expr*; /// 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 { + enum EntryKind { DefaultInit, AddressOf, + VarInit, + LValToRVal, } Kind; Expr *E; + 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) {} }; using IndirectLocalPath = llvm::SmallVectorImpl<IndirectLocalPathEntry>; @@ -6370,16 +6381,24 @@ struct RevertToOldSizeRAII { RevertToOldSizeRAII(IndirectLocalPath &Path) : Path(Path) {} ~RevertToOldSizeRAII() { Path.resize(OldSize); } }; + +using LocalVisitor = llvm::function_ref<bool(IndirectLocalPath &Path, Local L, + ReferenceKind RK)>; +} + +static bool isVarOnPath(IndirectLocalPath &Path, VarDecl *VD) { + for (auto E : Path) + if (E.Kind == IndirectLocalPathEntry::VarInit && E.D == VD) + return true; + return false; } -template <typename LocalVisitor> static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, Expr *Init, LocalVisitor Visit, bool RevisitSubinits); /// 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) { @@ -6390,6 +6409,9 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, do { Old = Init; + if (auto *EWC = dyn_cast<ExprWithCleanups>(Init)) + Init = EWC->getSubExpr(); + if (InitListExpr *ILE = dyn_cast<InitListExpr>(Init)) { // If this is just redundant braces around an initializer, step over it. if (ILE->isTransparent()) @@ -6409,20 +6431,22 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, // Per the current approach for DR1299, look through array element access // on array glvalues when performing lifetime extension. if (auto *ASE = dyn_cast<ArraySubscriptExpr>(Init)) { - auto *ICE = dyn_cast<ImplicitCastExpr>(ASE->getBase()); - if (!ICE || ICE->getCastKind() != CK_ArrayToPointerDecay) - return; - Init = ICE->getSubExpr(); + Init = ASE->getBase(); + auto *ICE = dyn_cast<ImplicitCastExpr>(Init); + if (ICE && ICE->getCastKind() == CK_ArrayToPointerDecay) + Init = ICE->getSubExpr(); + else + // We can't lifetime extend through this but we might still find some + // retained temporaries. + return visitLocalsRetainedByInitializer(Path, Init, Visit, true); } // 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}); + Path.push_back( + {IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()}); Init = DIE->getExpr(); - - if (auto *EWC = dyn_cast<ExprWithCleanups>(Init)) - Init = EWC->getSubExpr(); } } while (Init != Old); @@ -6432,22 +6456,64 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, 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())) { + switch (Init->getStmtClass()) { + case Stmt::DeclRefExprClass: { + // If we find the name of a local non-reference parameter, we could have a + // lifetime problem. + auto *DRE = cast<DeclRefExpr>(Init); 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); + if (!VD->getType()->isReferenceType()) { + Visit(Path, Local(DRE), RK); + } else if (isa<ParmVarDecl>(DRE->getDecl())) { + // The lifetime of a reference parameter is unknown; assume it's OK + // for now. + break; + } else if (VD->getInit() && !isVarOnPath(Path, VD)) { + Path.push_back({IndirectLocalPathEntry::VarInit, DRE, VD}); + visitLocalsRetainedByReferenceBinding(Path, VD->getInit(), + RK_ReferenceBinding, Visit); + } } + break; + } + + case Stmt::UnaryOperatorClass: { + // The only unary operator that make sense to handle here + // is Deref. All others don't resolve to a "name." This includes + // handling all sorts of rvalues passed to a unary operator. + const UnaryOperator *U = cast<UnaryOperator>(Init); + if (U->getOpcode() == UO_Deref) + visitLocalsRetainedByInitializer(Path, U->getSubExpr(), Visit, true); + break; + } + + case Stmt::OMPArraySectionExprClass: { + visitLocalsRetainedByInitializer( + Path, cast<OMPArraySectionExpr>(Init)->getBase(), Visit, true); + break; + } + + case Stmt::ConditionalOperatorClass: + case Stmt::BinaryConditionalOperatorClass: { + auto *C = cast<AbstractConditionalOperator>(Init); + if (!C->getTrueExpr()->getType()->isVoidType()) + visitLocalsRetainedByReferenceBinding(Path, C->getTrueExpr(), RK, Visit); + if (!C->getFalseExpr()->getType()->isVoidType()) + visitLocalsRetainedByReferenceBinding(Path, C->getFalseExpr(), RK, Visit); + break; + } + + // FIXME: Visit the left-hand side of an -> or ->*. + + default: + break; } } /// 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) { @@ -6456,13 +6522,13 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &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}); + Path.push_back({IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()}); Init = DIE->getExpr(); - - if (auto *EWC = dyn_cast<ExprWithCleanups>(Init)) - Init = EWC->getSubExpr(); } + 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()); @@ -6528,15 +6594,119 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, 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, + // 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()) { + 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(); + } + + Init = Init->IgnoreParens(); + switch (Init->getStmtClass()) { + case Stmt::UnaryOperatorClass: { + auto *UO = cast<UnaryOperator>(Init); + // If the initializer is the address of a local, we could have a lifetime + // problem. + if (UO->getOpcode() == UO_AddrOf) { + Path.push_back({IndirectLocalPathEntry::AddressOf, UO}); + visitLocalsRetainedByReferenceBinding(Path, UO->getSubExpr(), + RK_ReferenceBinding, Visit); + } + break; + } + + case Stmt::BinaryOperatorClass: { + // Handle pointer arithmetic. + auto *BO = cast<BinaryOperator>(Init); + BinaryOperatorKind BOK = BO->getOpcode(); + if (!BO->getType()->isPointerType() || (BOK != BO_Add && BOK != BO_Sub)) + break; + + if (BO->getLHS()->getType()->isPointerType()) + visitLocalsRetainedByInitializer(Path, BO->getLHS(), Visit, true); + else if (BO->getRHS()->getType()->isPointerType()) + visitLocalsRetainedByInitializer(Path, BO->getRHS(), Visit, true); + break; + } + + case Stmt::ConditionalOperatorClass: + case Stmt::BinaryConditionalOperatorClass: { + auto *C = cast<AbstractConditionalOperator>(Init); + // In C++, we can have a throw-expression operand, which has 'void' type + // and isn't interesting from a lifetime perspective. + if (!C->getTrueExpr()->getType()->isVoidType()) + visitLocalsRetainedByInitializer(Path, C->getTrueExpr(), Visit, true); + if (!C->getFalseExpr()->getType()->isVoidType()) + visitLocalsRetainedByInitializer(Path, C->getFalseExpr(), Visit, true); + break; + } + + case Stmt::BlockExprClass: + if (cast<BlockExpr>(Init)->getBlockDecl()->hasCaptures()) { + // This is a local block, whose lifetime is that of the function. + Visit(Path, Local(cast<BlockExpr>(Init)), RK_ReferenceBinding); } + break; + + case Stmt::AddrLabelExprClass: + // We want to warn if the address of a label would escape the function. + Visit(Path, Local(cast<AddrLabelExpr>(Init)), RK_ReferenceBinding); + break; + + default: + break; } } @@ -6563,58 +6733,20 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity, 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: - case LK_StmtExprResult: - // FIXME: Move -Wreturn-stack-address checks here. - return false; - } - return false; - } + Expr *First = Path.empty() ? L : Path.front().E; + SourceLocation DiagLoc = First->getLocStart(); + SourceRange DiagRange = First->getSourceRange(); - auto *MTE = L.get<MaterializeTemporaryExpr*>(); switch (LK) { case LK_FullExpression: llvm_unreachable("already handled this"); - case LK_Extended: + case LK_Extended: { + auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L); + if (!MTE) + // FIXME: Warn on this. + return false; + // Lifetime-extend the temporary. if (Path.empty()) { // Update the storage duration of the materialized temporary. @@ -6632,70 +6764,138 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity, // 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(), + Diag(DiagLoc, RK == RK_ReferenceBinding ? diag::warn_default_member_init_temporary_not_extended - : diag::warn_default_member_init_init_list_not_extended); + : diag::warn_default_member_init_init_list_not_extended) + << DiagRange; } else { // FIXME: Warn on this. + return false; } 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 ? ExtendingEntity->getDecl() : nullptr) { - bool IsSubobjectMember = ExtendingEntity != &Entity; - Diag(MTE->getExprLoc(), diag::err_bind_ref_member_to_temporary) - << ExtendingDecl << Init->getSourceRange() << IsSubobjectMember - << 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().Kind != IndirectLocalPathEntry::DefaultInit) { - Diag(ExtendingDecl->getLocation(), - diag::note_lifetime_extending_member_declared_here) - << RK << IsSubobjectMember; + case LK_MemInitializer: { + if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L)) { + // 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 ? ExtendingEntity->getDecl() : nullptr) { + bool IsSubobjectMember = ExtendingEntity != &Entity; + Diag(DiagLoc, diag::err_bind_ref_member_to_temporary) + << ExtendingDecl << IsSubobjectMember << RK << DiagRange; + // 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().Kind != IndirectLocalPathEntry::DefaultInit) { + Diag(ExtendingDecl->getLocation(), + diag::note_lifetime_extending_member_declared_here) + << RK << 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. + // + // FIXME: Warn on this. + return false; } } 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"); + // 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; + })) + return false; + + auto *DRE = dyn_cast<DeclRefExpr>(L); + auto *VD = DRE ? dyn_cast<VarDecl>(DRE->getDecl()) : nullptr; + if (!VD) { + // A member was initialized to a local block. + // FIXME: Warn on this. + return false; + } + + if (auto *Member = + ExtendingEntity ? ExtendingEntity->getDecl() : nullptr) { + bool IsPointer = Member->getType()->isAnyPointerType(); + Diag(DiagLoc, IsPointer ? diag::warn_init_ptr_member_to_parameter_addr + : diag::warn_bind_ref_member_to_parameter) + << Member << VD << isa<ParmVarDecl>(VD) << DiagRange; + Diag(Member->getLocation(), + diag::note_ref_or_ptr_member_declared_here) + << (unsigned)IsPointer; + } } break; + } case LK_New: - if (RK == RK_ReferenceBinding) { - Diag(MTE->getExprLoc(), diag::warn_new_dangling_reference); + if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L)) { + Diag(DiagLoc, RK == RK_ReferenceBinding + ? diag::warn_new_dangling_reference + : diag::warn_new_dangling_initializer_list) + << (ExtendingEntity != &Entity) << DiagRange; } else { - Diag(MTE->getExprLoc(), diag::warn_new_dangling_initializer_list) - << (ExtendingEntity != &Entity); + // We can't determine if the allocation outlives the local declaration. + return false; } break; case LK_Return: case LK_StmtExprResult: - // FIXME: Move -Wreturn-stack-address checks here. - return false; + if (auto *DRE = dyn_cast<DeclRefExpr>(L)) { + // We can't determine if the local variable outlives the statement + // expression. + if (LK == LK_StmtExprResult) + return false; + Diag(DiagLoc, diag::warn_ret_stack_addr_ref) + << Entity.getType()->isReferenceType() << DRE->getDecl() + << isa<ParmVarDecl>(DRE->getDecl()) << DiagRange; + } else if (isa<BlockExpr>(L)) { + Diag(DiagLoc, diag::err_ret_local_block) << DiagRange; + } else if (isa<AddrLabelExpr>(L)) { + Diag(DiagLoc, diag::warn_ret_addr_label) << DiagRange; + } else { + Diag(DiagLoc, diag::warn_ret_local_temp_addr_ref) + << Entity.getType()->isReferenceType() << DiagRange; + } + break; } - // FIXME: Model these as CodeSynthesisContexts to fix the note emission - // order. - for (auto Elem : llvm::reverse(Path)) { + for (unsigned I = 0; I != Path.size(); ++I) { + auto Elem = Path[I]; + + // Highlight the range of the next step within this path element. + SourceRange Range; + if (I < Path.size() - 1) + Range = Path[I + 1].E->getSourceRange(); + else + Range = L->getSourceRange(); + switch (Elem.Kind) { - case IndirectLocalPathEntry::DefaultInit: - Diag(Elem.E->getExprLoc(), diag::note_in_default_member_initalizer_here) - << cast<CXXDefaultInitExpr>(Elem.E)->getField(); + case IndirectLocalPathEntry::AddressOf: + case IndirectLocalPathEntry::LValToRVal: + // These exist primarily to mark the path as not permitting lifetime + // extension. break; - case IndirectLocalPathEntry::AddressOf: + case IndirectLocalPathEntry::DefaultInit: { + auto *FD = cast<FieldDecl>(Elem.D); + Diag(FD->getLocation(), diag::note_init_with_default_member_initalizer) + << FD << Range; + break; + } + + case IndirectLocalPathEntry::VarInit: + const VarDecl *VD = cast<VarDecl>(Elem.D); + Diag(VD->getLocation(), diag::note_local_var_initializer) + << VD->getType()->isReferenceType() << VD->getDeclName() << Range; break; } } |