checking.
llvm-svn: 337743
// CHECK: returning address/reference of stack memory
def warn_ret_stack_addr_ref : Warning<
- "%select{address of|reference to}0 stack memory associated with local "
- "variable %1 returned">,
+ "%select{address of|reference to}0 stack memory associated with "
+ "%select{local variable|parameter}2 %1 returned">,
InGroup<ReturnStackAddress>;
def warn_ret_local_temp_addr_ref : Warning<
"returning %select{address of|reference to}0 local temporary object">,
InGroup<ReturnStackAddress>;
def err_ret_local_block : Error<
"returning block that lives on the local stack">;
-def note_ref_var_local_bind : Note<
- "binding reference variable %0 here">;
+def note_local_var_initializer : Note<
+ "%select{via initialization of|binding reference}0 variable %1 here">;
+def note_init_with_default_member_initalizer : Note<
+ "initializing field %0 with default member initializer">;
// Check for initializing a member variable with the address or a reference to
// a constructor parameter.
"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.
<< FixItHint::CreateReplacement(SR, OS.str());
}
-//===--- CHECK: Return Address of Stack Variable --------------------------===//
-
-static const Expr *EvalVal(const Expr *E,
- SmallVectorImpl<const DeclRefExpr *> &refVars,
- const Decl *ParentDecl);
-static const Expr *EvalAddr(const Expr *E,
- SmallVectorImpl<const DeclRefExpr *> &refVars,
- const Decl *ParentDecl);
-
-/// CheckReturnStackAddr - Check if a return statement returns the address
-/// of a stack variable.
-static void
-CheckReturnStackAddr(Sema &S, Expr *RetValExp, QualType lhsType,
- SourceLocation ReturnLoc) {
- const Expr *stackE = nullptr;
- SmallVector<const DeclRefExpr *, 8> refVars;
-
- // Perform checking for returned stack addresses, local blocks,
- // label addresses or references to temporaries.
- if (lhsType->isPointerType() ||
- (!S.getLangOpts().ObjCAutoRefCount && lhsType->isBlockPointerType())) {
- stackE = EvalAddr(RetValExp, refVars, /*ParentDecl=*/nullptr);
- } else if (lhsType->isReferenceType()) {
- stackE = EvalVal(RetValExp, refVars, /*ParentDecl=*/nullptr);
- }
-
- if (!stackE)
- return; // Nothing suspicious was found.
-
- // Parameters are initialized in the calling scope, so taking the address
- // of a parameter reference doesn't need a warning.
- for (auto *DRE : refVars)
- if (isa<ParmVarDecl>(DRE->getDecl()))
- return;
-
- SourceLocation diagLoc;
- SourceRange diagRange;
- if (refVars.empty()) {
- diagLoc = stackE->getLocStart();
- diagRange = stackE->getSourceRange();
- } else {
- // We followed through a reference variable. 'stackE' contains the
- // problematic expression but we will warn at the return statement pointing
- // at the reference variable. We will later display the "trail" of
- // reference variables using notes.
- diagLoc = refVars[0]->getLocStart();
- diagRange = refVars[0]->getSourceRange();
- }
-
- if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(stackE)) {
- // address of local var
- S.Diag(diagLoc, diag::warn_ret_stack_addr_ref) << lhsType->isReferenceType()
- << DR->getDecl()->getDeclName() << diagRange;
- } else if (isa<BlockExpr>(stackE)) { // local block.
- S.Diag(diagLoc, diag::err_ret_local_block) << diagRange;
- } else if (isa<AddrLabelExpr>(stackE)) { // address of label.
- S.Diag(diagLoc, diag::warn_ret_addr_label) << diagRange;
- } else { // local temporary.
- // If there is an LValue->RValue conversion, then the value of the
- // reference type is used, not the reference.
- if (auto *ICE = dyn_cast<ImplicitCastExpr>(RetValExp)) {
- if (ICE->getCastKind() == CK_LValueToRValue) {
- return;
- }
- }
- S.Diag(diagLoc, diag::warn_ret_local_temp_addr_ref)
- << lhsType->isReferenceType() << diagRange;
- }
-
- // Display the "trail" of reference variables that we followed until we
- // found the problematic expression using notes.
- for (unsigned i = 0, e = refVars.size(); i != e; ++i) {
- const VarDecl *VD = cast<VarDecl>(refVars[i]->getDecl());
- // If this var binds to another reference var, show the range of the next
- // var, otherwise the var binds to the problematic expression, in which case
- // show the range of the expression.
- SourceRange range = (i < e - 1) ? refVars[i + 1]->getSourceRange()
- : stackE->getSourceRange();
- S.Diag(VD->getLocation(), diag::note_ref_var_local_bind)
- << VD->getDeclName() << range;
- }
-}
-
-/// EvalAddr - EvalAddr and EvalVal are mutually recursive functions that
-/// check if the expression in a return statement evaluates to an address
-/// to a location on the stack, a local block, an address of a label, or a
-/// reference to local temporary. The recursion is used to traverse the
-/// AST of the return expression, with recursion backtracking when we
-/// encounter a subexpression that (1) clearly does not lead to one of the
-/// above problematic expressions (2) is something we cannot determine leads to
-/// a problematic expression based on such local checking.
-///
-/// Both EvalAddr and EvalVal follow through reference variables to evaluate
-/// the expression that they point to. Such variables are added to the
-/// 'refVars' vector so that we know what the reference variable "trail" was.
-///
-/// EvalAddr processes expressions that are pointers that are used as
-/// references (and not L-values). EvalVal handles all other values.
-/// At the base case of the recursion is a check for the above problematic
-/// expressions.
-///
-/// This implementation handles:
-///
-/// * pointer-to-pointer casts
-/// * implicit conversions from array references to pointers
-/// * taking the address of fields
-/// * arbitrary interplay between "&" and "*" operators
-/// * pointer arithmetic from an address of a stack variable
-/// * taking the address of an array element where the array is on the stack
-static const Expr *EvalAddr(const Expr *E,
- SmallVectorImpl<const DeclRefExpr *> &refVars,
- const Decl *ParentDecl) {
- if (E->isTypeDependent())
- return nullptr;
-
- // We should only be called for evaluating pointer expressions.
- assert((E->getType()->isAnyPointerType() ||
- E->getType()->isBlockPointerType() ||
- E->getType()->isObjCQualifiedIdType()) &&
- "EvalAddr only works on pointers");
-
- E = E->IgnoreParens();
-
- // Our "symbolic interpreter" is just a dispatch off the currently
- // viewed AST node. We then recursively traverse the AST by calling
- // EvalAddr and EvalVal appropriately.
- switch (E->getStmtClass()) {
- case Stmt::DeclRefExprClass: {
- const DeclRefExpr *DR = cast<DeclRefExpr>(E);
-
- // If we leave the immediate function, the lifetime isn't about to end.
- if (DR->refersToEnclosingVariableOrCapture())
- return nullptr;
-
- if (const VarDecl *V = dyn_cast<VarDecl>(DR->getDecl()))
- // If this is a reference variable, follow through to the expression that
- // it points to.
- if (V->hasLocalStorage() &&
- V->getType()->isReferenceType() && V->hasInit()) {
- // Add the reference variable to the "trail".
- refVars.push_back(DR);
- return EvalAddr(V->getInit(), refVars, ParentDecl);
- }
-
- return nullptr;
- }
-
- case Stmt::UnaryOperatorClass: {
- // The only unary operator that make sense to handle here
- // is AddrOf. All others don't make sense as pointers.
- const UnaryOperator *U = cast<UnaryOperator>(E);
-
- if (U->getOpcode() == UO_AddrOf)
- return EvalVal(U->getSubExpr(), refVars, ParentDecl);
- return nullptr;
- }
-
- case Stmt::BinaryOperatorClass: {
- // Handle pointer arithmetic. All other binary operators are not valid
- // in this context.
- const BinaryOperator *B = cast<BinaryOperator>(E);
- BinaryOperatorKind op = B->getOpcode();
-
- if (op != BO_Add && op != BO_Sub)
- return nullptr;
-
- const Expr *Base = B->getLHS();
-
- // Determine which argument is the real pointer base. It could be
- // the RHS argument instead of the LHS.
- if (!Base->getType()->isPointerType())
- Base = B->getRHS();
-
- assert(Base->getType()->isPointerType());
- return EvalAddr(Base, refVars, ParentDecl);
- }
-
- // For conditional operators we need to see if either the LHS or RHS are
- // valid DeclRefExpr*s. If one of them is valid, we return it.
- case Stmt::ConditionalOperatorClass: {
- const ConditionalOperator *C = cast<ConditionalOperator>(E);
-
- // Handle the GNU extension for missing LHS.
- // FIXME: That isn't a ConditionalOperator, so doesn't get here.
- if (const Expr *LHSExpr = C->getLHS()) {
- // In C++, we can have a throw-expression, which has 'void' type.
- if (!LHSExpr->getType()->isVoidType())
- if (const Expr *LHS = EvalAddr(LHSExpr, refVars, ParentDecl))
- return LHS;
- }
-
- // In C++, we can have a throw-expression, which has 'void' type.
- if (C->getRHS()->getType()->isVoidType())
- return nullptr;
-
- return EvalAddr(C->getRHS(), refVars, ParentDecl);
- }
-
- case Stmt::BlockExprClass:
- if (cast<BlockExpr>(E)->getBlockDecl()->hasCaptures())
- return E; // local block.
- return nullptr;
-
- case Stmt::AddrLabelExprClass:
- return E; // address of label.
-
- case Stmt::ExprWithCleanupsClass:
- return EvalAddr(cast<ExprWithCleanups>(E)->getSubExpr(), refVars,
- ParentDecl);
-
- // For casts, we need to handle conversions from arrays to
- // pointer values, and pointer-to-pointer conversions.
- case Stmt::ImplicitCastExprClass:
- case Stmt::CStyleCastExprClass:
- case Stmt::CXXFunctionalCastExprClass:
- case Stmt::ObjCBridgedCastExprClass:
- case Stmt::CXXStaticCastExprClass:
- case Stmt::CXXDynamicCastExprClass:
- case Stmt::CXXConstCastExprClass:
- case Stmt::CXXReinterpretCastExprClass: {
- const Expr* SubExpr = cast<CastExpr>(E)->getSubExpr();
- switch (cast<CastExpr>(E)->getCastKind()) {
- case CK_LValueToRValue:
- case CK_NoOp:
- case CK_BaseToDerived:
- case CK_DerivedToBase:
- case CK_UncheckedDerivedToBase:
- case CK_Dynamic:
- case CK_CPointerToObjCPointerCast:
- case CK_BlockPointerToObjCPointerCast:
- case CK_AnyPointerToBlockPointerCast:
- return EvalAddr(SubExpr, refVars, ParentDecl);
-
- case CK_ArrayToPointerDecay:
- return EvalVal(SubExpr, refVars, ParentDecl);
-
- case CK_BitCast:
- if (SubExpr->getType()->isAnyPointerType() ||
- SubExpr->getType()->isBlockPointerType() ||
- SubExpr->getType()->isObjCQualifiedIdType())
- return EvalAddr(SubExpr, refVars, ParentDecl);
- else
- return nullptr;
-
- default:
- return nullptr;
- }
- }
-
- case Stmt::MaterializeTemporaryExprClass:
- if (const Expr *Result =
- EvalAddr(cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr(),
- refVars, ParentDecl))
- return Result;
- return E;
-
- // Everything else: we simply don't reason about them.
- default:
- return nullptr;
- }
-}
-
-/// EvalVal - This function is complements EvalAddr in the mutual recursion.
-/// See the comments for EvalAddr for more details.
-static const Expr *EvalVal(const Expr *E,
- SmallVectorImpl<const DeclRefExpr *> &refVars,
- const Decl *ParentDecl) {
- do {
- // We should only be called for evaluating non-pointer expressions, or
- // expressions with a pointer type that are not used as references but
- // instead
- // are l-values (e.g., DeclRefExpr with a pointer type).
-
- // Our "symbolic interpreter" is just a dispatch off the currently
- // viewed AST node. We then recursively traverse the AST by calling
- // EvalAddr and EvalVal appropriately.
-
- E = E->IgnoreParens();
- switch (E->getStmtClass()) {
- case Stmt::ImplicitCastExprClass: {
- const ImplicitCastExpr *IE = cast<ImplicitCastExpr>(E);
- if (IE->getValueKind() == VK_LValue) {
- E = IE->getSubExpr();
- continue;
- }
- return nullptr;
- }
-
- case Stmt::ExprWithCleanupsClass:
- return EvalVal(cast<ExprWithCleanups>(E)->getSubExpr(), refVars,
- ParentDecl);
-
- case Stmt::DeclRefExprClass: {
- // When we hit a DeclRefExpr we are looking at code that refers to a
- // variable's name. If it's not a reference variable we check if it has
- // local storage within the function, and if so, return the expression.
- const DeclRefExpr *DR = cast<DeclRefExpr>(E);
-
- // If we leave the immediate function, the lifetime isn't about to end.
- if (DR->refersToEnclosingVariableOrCapture())
- return nullptr;
-
- if (const VarDecl *V = dyn_cast<VarDecl>(DR->getDecl())) {
- // Check if it refers to itself, e.g. "int& i = i;".
- if (V == ParentDecl)
- return DR;
-
- if (V->hasLocalStorage()) {
- if (!V->getType()->isReferenceType())
- return DR;
-
- // Reference variable, follow through to the expression that
- // it points to.
- if (V->hasInit()) {
- // Add the reference variable to the "trail".
- refVars.push_back(DR);
- return EvalVal(V->getInit(), refVars, V);
- }
- }
- }
-
- return nullptr;
- }
-
- 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>(E);
-
- if (U->getOpcode() == UO_Deref)
- return EvalAddr(U->getSubExpr(), refVars, ParentDecl);
-
- return nullptr;
- }
-
- case Stmt::ArraySubscriptExprClass: {
- // Array subscripts are potential references to data on the stack. We
- // retrieve the DeclRefExpr* for the array variable if it indeed
- // has local storage.
- const auto *ASE = cast<ArraySubscriptExpr>(E);
- if (ASE->isTypeDependent())
- return nullptr;
- return EvalAddr(ASE->getBase(), refVars, ParentDecl);
- }
-
- case Stmt::OMPArraySectionExprClass: {
- return EvalAddr(cast<OMPArraySectionExpr>(E)->getBase(), refVars,
- ParentDecl);
- }
-
- case Stmt::ConditionalOperatorClass: {
- // For conditional operators we need to see if either the LHS or RHS are
- // non-NULL Expr's. If one is non-NULL, we return it.
- const ConditionalOperator *C = cast<ConditionalOperator>(E);
-
- // Handle the GNU extension for missing LHS.
- if (const Expr *LHSExpr = C->getLHS()) {
- // In C++, we can have a throw-expression, which has 'void' type.
- if (!LHSExpr->getType()->isVoidType())
- if (const Expr *LHS = EvalVal(LHSExpr, refVars, ParentDecl))
- return LHS;
- }
-
- // In C++, we can have a throw-expression, which has 'void' type.
- if (C->getRHS()->getType()->isVoidType())
- return nullptr;
-
- return EvalVal(C->getRHS(), refVars, ParentDecl);
- }
-
- // Accesses to members are potential references to data on the stack.
- case Stmt::MemberExprClass: {
- const MemberExpr *M = cast<MemberExpr>(E);
-
- // Check for indirect access. We only want direct field accesses.
- if (M->isArrow())
- return nullptr;
-
- // Check whether the member type is itself a reference, in which case
- // we're not going to refer to the member, but to what the member refers
- // to.
- if (M->getMemberDecl()->getType()->isReferenceType())
- return nullptr;
-
- return EvalVal(M->getBase(), refVars, ParentDecl);
- }
-
- case Stmt::MaterializeTemporaryExprClass:
- if (const Expr *Result =
- EvalVal(cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr(),
- refVars, ParentDecl))
- return Result;
- return E;
-
- default:
- // Check that we don't return or take the address of a reference to a
- // temporary. This is only useful in C++.
- if (!E->isTypeDependent() && E->isRValue())
- return E;
-
- // Everything else: we simply don't reason about them.
- return nullptr;
- }
- } while (true);
-}
-
void
Sema::CheckReturnValExpr(Expr *RetValExp, QualType lhsType,
SourceLocation ReturnLoc,
bool isObjCMethod,
const AttrVec *Attrs,
const FunctionDecl *FD) {
- CheckReturnStackAddr(*this, RetValExp, lhsType, ReturnLoc);
-
// Check if the return value is null but should not be.
if (((Attrs && hasSpecificAttr<ReturnsNonNullAttr>(*Attrs)) ||
(!isObjCMethod && isNonNullType(Context, lhsType))) &&
#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"
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>;
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) {
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())
// 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);
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) {
// 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());
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;
}
}
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.
// 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;
}
}
}
int* f2(int y) {
- return &y; // expected-warning{{Address of stack memory associated with local variable 'y' returned}} expected-warning{{address of stack memory associated with local variable 'y' returned}}
+ return &y; // expected-warning{{Address of stack memory associated with local variable 'y' returned}} expected-warning{{address of stack memory associated with parameter 'y' returned}}
}
int* f3(int x, int *y) {
// rdar://11345441
int* f5() {
- int& i = i; // expected-warning {{Assigned value is garbage or undefined}} expected-note {{binding reference variable 'i' here}} expected-warning{{reference 'i' is not yet bound to a value when used within its own initialization}}
- return &i; // expected-warning {{address of stack memory associated with local variable 'i' returned}}
+ int& i = i; // expected-warning {{Assigned value is garbage or undefined}} expected-warning{{reference 'i' is not yet bound to a value when used within its own initialization}}
+ return &i;
}
void *radar13226577() {
intptr_t returnAsNonLoc() {
int x;
- return (intptr_t)&x; // expected-warning{{Address of stack memory associated with local variable 'x' returned to caller}}
+ return (intptr_t)&x; // expected-warning{{Address of stack memory associated with local variable 'x' returned to caller}} expected-warning{{address of stack memory associated with local variable 'x' returned}}
}
bool returnAsBool() {
// 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
+ const A &a = A(); // expected-note {{default member init}}
};
- D1 d1 = {}; // expected-note {{here}}
+ D1 d1 = {};
+#if __cplusplus < 201402L
+ // expected-note@-2 {{first required here}}
+#else
+ // expected-warning-re@-4 {{sorry, lifetime extension {{.*}} not supported}}
+#endif
struct D2 {
- const A &a = A(); // expected-error {{binds to a temporary}}
- D2() {} // expected-note {{used here}}
+ const A &a = A(); // expected-note {{default member init}}
+ D2() {} // expected-error {{binds to a temporary}}
};
- struct D3 { // expected-note {{used here}}
- const A &a = A(); // expected-error {{binds to a temporary}}
+ struct D3 { // expected-error {{binds to a temporary}}
+ const A &a = A(); // expected-note {{default member init}}
};
D3 d3; // expected-note {{first required here}}
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}}
+ struct haslist4 { // expected-error {{backing array for 'std::initializer_list' member 'il' is a temporary object}}
+ std::initializer_list<int> il = {1, 2, 3}; // expected-note {{default member initializer}}
};
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}}
+ std::initializer_list<int> il = {1, 2, 3}; // expected-note {{default member initializer}}
+ haslist5() {} // expected-error {{backing array for 'std::initializer_list' member 'il' is a temporary object}}
};
#endif
}
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 A { int &&r = 0; }; // expected-note {{default member init}}
+ A a = {}; // FIXME expected-warning {{not supported}}
- struct B { int &&r = 0; }; // expected-error {{binds to a temporary}} expected-note {{here}}
+ struct B { int &&r = 0; }; // expected-error {{binds to a temporary}} expected-note {{default member init}}
B b; // expected-note {{here}}
#endif
}
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
+// FIXME: The error 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}}
+struct RValueTmp { // expected-error {{reference member 'ri' binds to a temporary}}
+ int && ri = 1; // expected-note{{copy constructor of 'RValueTmp' is implicitly deleted because field 'ri' is of rvalue reference type 'int &&'}} // expected-note {{default member init}}
};
RValueTmp RVTa; // expected-note {{implicit default constructor for 'RValueTmp' first required here}}
RValueTmp RVTb(RVTa); // expected-error{{call to implicitly-deleted copy constructor}}
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}}
+class NotDeleted2d { int &&a = 0; }; // expected-error {{reference member 'a' binds to a temporary object}} expected-note {{default member init}}
NotDeleted2d nd2d; // expected-note {{first required here}}
// - any non-variant non-static data member of const qualified type (or array
// RUN: %clang_cc1 -verify %s -std=c++14
-template<const int I> struct S { // expected-note {{in default member initializer}}
+template<const int I> struct S { // expected-error {{reference member 'r' binds to a temporary object}}
decltype(I) n;
- int &&r = I; // expected-error {{reference member 'r' binds to a temporary object}}
+ int &&r = I; // expected-note {{default member initializer}}
};
S<5> s; // expected-note {{implicit default constructor}}
-template<typename T, T v> struct U { // expected-note {{in default member initializer}}
+template<typename T, T v> struct U { // expected-error {{reference member 'r' binds to a temporary object}}
decltype(v) n;
- int &&r = v; // expected-error {{reference member 'r' binds to a temporary object}}
+ int &&r = v; // expected-note {{default member initializer}}
};
U<const int, 6> u; // expected-note {{implicit default constructor}}
}
// Check that multiple CXXDefaultInitExprs don't cause an assertion failure.
-struct A { int &&r = 0; }; // expected-warning 2{{not supported}}
+struct A { int &&r = 0; }; // expected-note 2{{default member initializer}}
struct B { A x, y; };
-B b = {}; // expected-note 2{{in default member initializer for field 'r' used here}}
+B b = {}; // expected-warning 2{{not supported}}
}
struct haslist1 {
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}}
+ std::initializer_list<int> jl{1, 2, 3}; // expected-note {{default member init}}
haslist1();
};
-haslist1::haslist1() // expected-note {{used here}}
+haslist1::haslist1() // expected-error {{backing array for 'std::initializer_list' member 'jl' is a temporary object}}
: il{1, 2, 3} // expected-error {{backing array for 'std::initializer_list' member 'il' is a temporary object}}
{}
namespace pr33140_2 {
// FIXME: The declaration of 'b' below should lifetime-extend two int
// temporaries.
- struct A { int &&r = 0; }; // expected-warning 2{{not supported}}
+ struct A { int &&r = 0; }; // expected-note 2{{initializing field 'r' with default member initializer}}
struct B { A x, y; };
- B b = {}; // expected-note 2{{used here}}
+ B b = {}; // expected-warning 2{{not supported}}
}
namespace pr33140_3 {
const int *int7(int x) {
const int &x2 = x; // expected-note{{binding reference variable 'x2' here}}
- return &x2; // expected-warning{{address of stack memory associated with local variable 'x' returned}}
+ return &x2; // expected-warning{{address of stack memory associated with parameter 'x' returned}}
}
const int *int8(const int &x = 5) {
int* ret_conditional(bool cond) {
int x = 1;
int y = 2;
- return cond ? &x : &y; // expected-warning {{address of stack memory}}
+ return cond ? &x // expected-warning {{address of stack memory associated with local variable 'x' returned}}
+ : &y; // expected-warning {{address of stack memory associated with local variable 'y' returned}}
}
int* ret_conditional_rhs(int *x, bool cond) {
}
int&& should_warn(int i) {
- // FIXME: The stack address return test doesn't reason about casts.
- return static_cast<int&&>(i); // xpected-warning {{returning reference to temporary}}
+ return static_cast<int&&>(i); // expected-warning {{reference to stack memory associated with parameter 'i' returned}}
}
-int&& should_not_warn(int&& i) { // But GCC 4.4 does
+int&& should_not_warn(int&& i) {
return static_cast<int&&>(i);
}