mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-12-15 04:00:56 +00:00
Fold -Wreturn-stack-address into general initialization lifetime
checking. llvm-svn: 337743
This commit is contained in:
parent
f9f50f634d
commit
afe48f9d68
@ -7849,8 +7849,8 @@ def warn_null_ret : Warning<
|
||||
|
||||
// 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">,
|
||||
@ -7860,8 +7860,10 @@ def warn_ret_addr_label : Warning<
|
||||
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.
|
||||
@ -7902,8 +7904,6 @@ def warn_default_member_init_init_list_not_extended : Warning<
|
||||
"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.
|
||||
|
@ -9232,421 +9232,12 @@ void Sema::CheckStrncatArguments(const CallExpr *CE,
|
||||
<< 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))) &&
|
||||
|
@ -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");
|
||||
Expr *First = Path.empty() ? L : Path.front().E;
|
||||
SourceLocation DiagLoc = First->getLocStart();
|
||||
SourceRange DiagRange = First->getSourceRange();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ int* f1() {
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -91,8 +91,8 @@ struct TS {
|
||||
|
||||
// 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() {
|
||||
|
@ -37,7 +37,7 @@ void test_multi_return() {
|
||||
|
||||
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() {
|
||||
|
@ -317,23 +317,25 @@ namespace dr1696 { // dr1696: 7
|
||||
// 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}}
|
||||
|
||||
@ -352,14 +354,14 @@ namespace dr1696 { // dr1696: 7
|
||||
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
|
||||
}
|
||||
|
@ -33,10 +33,10 @@ namespace dr1813 { // dr1813: 7
|
||||
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
|
||||
}
|
||||
|
@ -128,11 +128,10 @@ struct RValue {
|
||||
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}}
|
||||
|
@ -47,7 +47,7 @@ class NotDeleted2c { int &&a = static_cast<int&&>(n); };
|
||||
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
|
||||
|
@ -1,13 +1,13 @@
|
||||
// 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}}
|
||||
|
@ -31,8 +31,8 @@ void test_default_arg2() {
|
||||
}
|
||||
|
||||
// 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}}
|
||||
|
||||
}
|
||||
|
@ -155,11 +155,11 @@ void dangle() {
|
||||
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}}
|
||||
{}
|
||||
|
||||
|
@ -27,9 +27,9 @@ namespace pr33140_0b {
|
||||
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 {
|
||||
|
@ -65,7 +65,7 @@ const int *int6() {
|
||||
|
||||
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) {
|
||||
|
@ -63,7 +63,8 @@ int& ret_local_field_ref() {
|
||||
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) {
|
||||
|
@ -65,10 +65,9 @@ void f() {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user