From 354438d88cbb857858ffcbde72d4723b8548ce0a Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 23 Jul 2018 21:21:22 +0000 Subject: [PATCH] Fold -Wreturn-stack-address into general initialization lifetime checking. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@337743 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 12 +- lib/Sema/SemaChecking.cpp | 409 ---------------- lib/Sema/SemaInit.cpp | 436 +++++++++++++----- test/Analysis/stack-addr-ps.c | 2 +- test/Analysis/stack-addr-ps.cpp | 4 +- test/Analysis/stackaddrleak.c | 2 +- test/CXX/drs/dr16xx.cpp | 28 +- test/CXX/drs/dr18xx.cpp | 6 +- test/CXX/special/class.copy/p11.0x.copy.cpp | 7 +- test/CXX/special/class.ctor/p5-0x.cpp | 2 +- test/CXX/temp/temp.param/p5.cpp | 8 +- test/SemaCXX/constexpr-default-arg.cpp | 4 +- .../cxx0x-initializer-stdinitializerlist.cpp | 4 +- test/SemaCXX/eval-crashes.cpp | 4 +- test/SemaCXX/return-stack-addr-2.cpp | 2 +- test/SemaCXX/return-stack-addr.cpp | 3 +- test/SemaCXX/rval-references.cpp | 5 +- 17 files changed, 365 insertions(+), 573 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index c6f263dc8c..0d000e72a5 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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; 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; 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; -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. diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index 6492346f6a..8d953b6ef3 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -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 &refVars, - const Decl *ParentDecl); -static const Expr *EvalAddr(const Expr *E, - SmallVectorImpl &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 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(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(stackE)) { - // address of local var - S.Diag(diagLoc, diag::warn_ret_stack_addr_ref) << lhsType->isReferenceType() - << DR->getDecl()->getDeclName() << diagRange; - } else if (isa(stackE)) { // local block. - S.Diag(diagLoc, diag::err_ret_local_block) << diagRange; - } else if (isa(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(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(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 &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(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(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(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(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(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(E)->getBlockDecl()->hasCaptures()) - return E; // local block. - return nullptr; - - case Stmt::AddrLabelExprClass: - return E; // address of label. - - case Stmt::ExprWithCleanupsClass: - return EvalAddr(cast(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(E)->getSubExpr(); - switch (cast(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(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 &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(E); - if (IE->getValueKind() == VK_LValue) { - E = IE->getSubExpr(); - continue; - } - return nullptr; - } - - case Stmt::ExprWithCleanupsClass: - return EvalVal(cast(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(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(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(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(E); - if (ASE->isTypeDependent()) - return nullptr; - return EvalAddr(ASE->getBase(), refVars, ParentDecl); - } - - case Stmt::OMPArraySectionExprClass: { - return EvalAddr(cast(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(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(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(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(*Attrs)) || (!isObjCMethod && isNonNullType(Context, lhsType))) && diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index e457de4dcc..5c891bebea 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -15,6 +15,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" +#include "clang/AST/ExprOpenMP.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/TargetInfo.h" #include "clang/Sema/Designator.h" @@ -6348,18 +6349,28 @@ enum ReferenceKind { RK_StdInitializerList, }; -/// A temporary or local variable. -using Local = llvm::PointerUnion; +/// 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; @@ -6370,16 +6381,24 @@ struct RevertToOldSizeRAII { RevertToOldSizeRAII(IndirectLocalPath &Path) : Path(Path) {} ~RevertToOldSizeRAII() { Path.resize(OldSize); } }; + +using LocalVisitor = llvm::function_ref; +} + +static bool isVarOnPath(IndirectLocalPath &Path, VarDecl *VD) { + for (auto E : Path) + if (E.Kind == IndirectLocalPathEntry::VarInit && E.D == VD) + return true; + return false; } -template 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 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(Init)) + Init = EWC->getSubExpr(); + if (InitListExpr *ILE = dyn_cast(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(Init)) { - auto *ICE = dyn_cast(ASE->getBase()); - if (!ICE || ICE->getCastKind() != CK_ArrayToPointerDecay) - return; - Init = ICE->getSubExpr(); + Init = ASE->getBase(); + auto *ICE = dyn_cast(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(Init)) { - Path.push_back({IndirectLocalPathEntry::DefaultInit, DIE}); + Path.push_back( + {IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()}); Init = DIE->getExpr(); - - if (auto *EWC = dyn_cast(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(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(Init); auto *VD = dyn_cast(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(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(Init); + if (U->getOpcode() == UO_Deref) + visitLocalsRetainedByInitializer(Path, U->getSubExpr(), Visit, true); + break; + } + + case Stmt::OMPArraySectionExprClass: { + visitLocalsRetainedByInitializer( + Path, cast(Init)->getBase(), Visit, true); + break; + } + + case Stmt::ConditionalOperatorClass: + case Stmt::BinaryConditionalOperatorClass: { + auto *C = cast(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 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(Init)) { - Path.push_back({IndirectLocalPathEntry::DefaultInit, DIE}); + Path.push_back({IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()}); Init = DIE->getExpr(); - - if (auto *EWC = dyn_cast(Init)) - Init = EWC->getSubExpr(); } + if (auto *EWC = dyn_cast(Init)) + Init = EWC->getSubExpr(); + // Dig out the expression which constructs the extended temporary. Init = const_cast(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(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(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(L)) { + auto *VD = dyn_cast(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(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(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(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(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(Init)->getBlockDecl()->hasCaptures()) { + // This is a local block, whose lifetime is that of the function. + Visit(Path, Local(cast(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(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()) { - 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(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(); switch (LK) { case LK_FullExpression: llvm_unreachable("already handled this"); - case LK_Extended: + case LK_Extended: { + auto *MTE = dyn_cast(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(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(L); + auto *VD = DRE ? dyn_cast(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(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(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(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(DRE->getDecl()) << DiagRange; + } else if (isa(L)) { + Diag(DiagLoc, diag::err_ret_local_block) << DiagRange; + } else if (isa(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(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(Elem.D); + Diag(FD->getLocation(), diag::note_init_with_default_member_initalizer) + << FD << Range; + break; + } + + case IndirectLocalPathEntry::VarInit: + const VarDecl *VD = cast(Elem.D); + Diag(VD->getLocation(), diag::note_local_var_initializer) + << VD->getType()->isReferenceType() << VD->getDeclName() << Range; break; } } diff --git a/test/Analysis/stack-addr-ps.c b/test/Analysis/stack-addr-ps.c index 4026cee90b..721051ffaf 100644 --- a/test/Analysis/stack-addr-ps.c +++ b/test/Analysis/stack-addr-ps.c @@ -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) { diff --git a/test/Analysis/stack-addr-ps.cpp b/test/Analysis/stack-addr-ps.cpp index 79afd18e18..e1f06835c7 100644 --- a/test/Analysis/stack-addr-ps.cpp +++ b/test/Analysis/stack-addr-ps.cpp @@ -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() { diff --git a/test/Analysis/stackaddrleak.c b/test/Analysis/stackaddrleak.c index a037d12fe4..8a1519e787 100644 --- a/test/Analysis/stackaddrleak.c +++ b/test/Analysis/stackaddrleak.c @@ -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() { diff --git a/test/CXX/drs/dr16xx.cpp b/test/CXX/drs/dr16xx.cpp index 54648cfe96..d9d404c0c2 100644 --- a/test/CXX/drs/dr16xx.cpp +++ b/test/CXX/drs/dr16xx.cpp @@ -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 il = {1, 2, 3}; }; - struct haslist4 { // expected-note {{in default member initializer}} - std::initializer_list 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 il = {1, 2, 3}; // expected-note {{default member initializer}} }; haslist4 hl4; // expected-note {{in implicit default constructor}} struct haslist5 { - std::initializer_list 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 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 } diff --git a/test/CXX/drs/dr18xx.cpp b/test/CXX/drs/dr18xx.cpp index f6a4676bc7..16f967b656 100644 --- a/test/CXX/drs/dr18xx.cpp +++ b/test/CXX/drs/dr18xx.cpp @@ -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 } diff --git a/test/CXX/special/class.copy/p11.0x.copy.cpp b/test/CXX/special/class.copy/p11.0x.copy.cpp index a4d0cdcdc7..e7256bcf94 100644 --- a/test/CXX/special/class.copy/p11.0x.copy.cpp +++ b/test/CXX/special/class.copy/p11.0x.copy.cpp @@ -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}} diff --git a/test/CXX/special/class.ctor/p5-0x.cpp b/test/CXX/special/class.ctor/p5-0x.cpp index 5558313ce7..061a3d1f07 100644 --- a/test/CXX/special/class.ctor/p5-0x.cpp +++ b/test/CXX/special/class.ctor/p5-0x.cpp @@ -47,7 +47,7 @@ class NotDeleted2c { int &&a = static_cast(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 diff --git a/test/CXX/temp/temp.param/p5.cpp b/test/CXX/temp/temp.param/p5.cpp index de902a5e4d..4cb2d4343a 100644 --- a/test/CXX/temp/temp.param/p5.cpp +++ b/test/CXX/temp/temp.param/p5.cpp @@ -1,13 +1,13 @@ // RUN: %clang_cc1 -verify %s -std=c++14 -template struct S { // expected-note {{in default member initializer}} +template 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 struct U { // expected-note {{in default member initializer}} +template 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 u; // expected-note {{implicit default constructor}} diff --git a/test/SemaCXX/constexpr-default-arg.cpp b/test/SemaCXX/constexpr-default-arg.cpp index 165c31aab6..0cef4aa578 100644 --- a/test/SemaCXX/constexpr-default-arg.cpp +++ b/test/SemaCXX/constexpr-default-arg.cpp @@ -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}} } diff --git a/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp b/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp index ece014d93a..f371891e54 100644 --- a/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp +++ b/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp @@ -155,11 +155,11 @@ void dangle() { struct haslist1 { std::initializer_list il // expected-note {{declared here}} = {1, 2, 3}; // ok, unused - std::initializer_list jl{1, 2, 3}; // expected-error {{backing array for 'std::initializer_list' member 'jl' is a temporary object}} + std::initializer_list 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}} {} diff --git a/test/SemaCXX/eval-crashes.cpp b/test/SemaCXX/eval-crashes.cpp index 33bde75de6..60c2deed4d 100644 --- a/test/SemaCXX/eval-crashes.cpp +++ b/test/SemaCXX/eval-crashes.cpp @@ -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 { diff --git a/test/SemaCXX/return-stack-addr-2.cpp b/test/SemaCXX/return-stack-addr-2.cpp index 47b45957e9..e848189bde 100644 --- a/test/SemaCXX/return-stack-addr-2.cpp +++ b/test/SemaCXX/return-stack-addr-2.cpp @@ -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) { diff --git a/test/SemaCXX/return-stack-addr.cpp b/test/SemaCXX/return-stack-addr.cpp index 7670798ecb..a5f84adf0b 100644 --- a/test/SemaCXX/return-stack-addr.cpp +++ b/test/SemaCXX/return-stack-addr.cpp @@ -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) { diff --git a/test/SemaCXX/rval-references.cpp b/test/SemaCXX/rval-references.cpp index 94b09ce2cb..838bf484fe 100644 --- a/test/SemaCXX/rval-references.cpp +++ b/test/SemaCXX/rval-references.cpp @@ -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(i); // xpected-warning {{returning reference to temporary}} + return static_cast(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(i); } -- 2.40.0