1598 lines · cpp
1//===--- CheckExprLifetime.cpp --------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "CheckExprLifetime.h"10#include "clang/AST/Decl.h"11#include "clang/AST/Expr.h"12#include "clang/AST/Type.h"13#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"14#include "clang/Basic/DiagnosticSema.h"15#include "clang/Sema/Initialization.h"16#include "clang/Sema/Sema.h"17#include "llvm/ADT/PointerIntPair.h"18 19namespace clang::sema {20using lifetimes::isGslOwnerType;21using lifetimes::isGslPointerType;22 23namespace {24enum LifetimeKind {25 /// The lifetime of a temporary bound to this entity ends at the end of the26 /// full-expression, and that's (probably) fine.27 LK_FullExpression,28 29 /// The lifetime of a temporary bound to this entity is extended to the30 /// lifeitme of the entity itself.31 LK_Extended,32 33 /// The lifetime of a temporary bound to this entity probably ends too soon,34 /// because the entity is allocated in a new-expression.35 LK_New,36 37 /// The lifetime of a temporary bound to this entity ends too soon, because38 /// the entity is a return object.39 LK_Return,40 41 /// The lifetime of a temporary bound to this entity ends too soon, because42 /// the entity passed to a musttail function call.43 LK_MustTail,44 45 /// The lifetime of a temporary bound to this entity ends too soon, because46 /// the entity is the result of a statement expression.47 LK_StmtExprResult,48 49 /// This is a mem-initializer: if it would extend a temporary (other than via50 /// a default member initializer), the program is ill-formed.51 LK_MemInitializer,52 53 /// The lifetime of a temporary bound to this entity may end too soon,54 /// because the entity is a pointer and we assign the address of a temporary55 /// object to it.56 LK_Assignment,57 58 /// The lifetime of a temporary bound to this entity may end too soon,59 /// because the entity may capture the reference to a temporary object.60 LK_LifetimeCapture,61};62using LifetimeResult =63 llvm::PointerIntPair<const InitializedEntity *, 3, LifetimeKind>;64} // namespace65 66/// Determine the declaration which an initialized entity ultimately refers to,67/// for the purpose of lifetime-extending a temporary bound to a reference in68/// the initialization of \p Entity.69static LifetimeResult70getEntityLifetime(const InitializedEntity *Entity,71 const InitializedEntity *InitField = nullptr) {72 // C++11 [class.temporary]p5:73 switch (Entity->getKind()) {74 case InitializedEntity::EK_Variable:75 // The temporary [...] persists for the lifetime of the reference76 return {Entity, LK_Extended};77 78 case InitializedEntity::EK_Member:79 // For subobjects, we look at the complete object.80 if (Entity->getParent())81 return getEntityLifetime(Entity->getParent(), Entity);82 83 // except:84 // C++17 [class.base.init]p8:85 // A temporary expression bound to a reference member in a86 // mem-initializer is ill-formed.87 // C++17 [class.base.init]p11:88 // A temporary expression bound to a reference member from a89 // default member initializer is ill-formed.90 //91 // The context of p11 and its example suggest that it's only the use of a92 // default member initializer from a constructor that makes the program93 // ill-formed, not its mere existence, and that it can even be used by94 // aggregate initialization.95 return {Entity, Entity->isDefaultMemberInitializer() ? LK_Extended96 : LK_MemInitializer};97 98 case InitializedEntity::EK_Binding:99 // Per [dcl.decomp]p3, the binding is treated as a variable of reference100 // type.101 return {Entity, LK_Extended};102 103 case InitializedEntity::EK_Parameter:104 case InitializedEntity::EK_Parameter_CF_Audited:105 // -- A temporary bound to a reference parameter in a function call106 // persists until the completion of the full-expression containing107 // the call.108 return {nullptr, LK_FullExpression};109 110 case InitializedEntity::EK_TemplateParameter:111 // FIXME: This will always be ill-formed; should we eagerly diagnose it112 // here?113 return {nullptr, LK_FullExpression};114 115 case InitializedEntity::EK_Result:116 // -- The lifetime of a temporary bound to the returned value in a117 // function return statement is not extended; the temporary is118 // destroyed at the end of the full-expression in the return statement.119 return {nullptr, LK_Return};120 121 case InitializedEntity::EK_StmtExprResult:122 // FIXME: Should we lifetime-extend through the result of a statement123 // expression?124 return {nullptr, LK_StmtExprResult};125 126 case InitializedEntity::EK_New:127 // -- A temporary bound to a reference in a new-initializer persists128 // until the completion of the full-expression containing the129 // new-initializer.130 return {nullptr, LK_New};131 132 case InitializedEntity::EK_Temporary:133 case InitializedEntity::EK_CompoundLiteralInit:134 case InitializedEntity::EK_RelatedResult:135 // We don't yet know the storage duration of the surrounding temporary.136 // Assume it's got full-expression duration for now, it will patch up our137 // storage duration if that's not correct.138 return {nullptr, LK_FullExpression};139 140 case InitializedEntity::EK_ArrayElement:141 // For subobjects, we look at the complete object.142 return getEntityLifetime(Entity->getParent(), InitField);143 144 case InitializedEntity::EK_Base:145 // For subobjects, we look at the complete object.146 if (Entity->getParent())147 return getEntityLifetime(Entity->getParent(), InitField);148 return {InitField, LK_MemInitializer};149 150 case InitializedEntity::EK_Delegating:151 // We can reach this case for aggregate initialization in a constructor:152 // struct A { int &&r; };153 // struct B : A { B() : A{0} {} };154 // In this case, use the outermost field decl as the context.155 return {InitField, LK_MemInitializer};156 157 case InitializedEntity::EK_BlockElement:158 case InitializedEntity::EK_LambdaToBlockConversionBlockElement:159 case InitializedEntity::EK_LambdaCapture:160 case InitializedEntity::EK_VectorElement:161 case InitializedEntity::EK_MatrixElement:162 case InitializedEntity::EK_ComplexElement:163 return {nullptr, LK_FullExpression};164 165 case InitializedEntity::EK_Exception:166 // FIXME: Can we diagnose lifetime problems with exceptions?167 return {nullptr, LK_FullExpression};168 169 case InitializedEntity::EK_ParenAggInitMember:170 // -- A temporary object bound to a reference element of an aggregate of171 // class type initialized from a parenthesized expression-list172 // [dcl.init, 9.3] persists until the completion of the full-expression173 // containing the expression-list.174 return {nullptr, LK_FullExpression};175 }176 177 llvm_unreachable("unknown entity kind");178}179 180namespace {181enum ReferenceKind {182 /// Lifetime would be extended by a reference binding to a temporary.183 RK_ReferenceBinding,184 /// Lifetime would be extended by a std::initializer_list object binding to185 /// its backing array.186 RK_StdInitializerList,187};188 189/// A temporary or local variable. This will be one of:190/// * A MaterializeTemporaryExpr.191/// * A DeclRefExpr whose declaration is a local.192/// * An AddrLabelExpr.193/// * A BlockExpr for a block with captures.194using Local = Expr *;195 196/// Expressions we stepped over when looking for the local state. Any steps197/// that would inhibit lifetime extension or take us out of subexpressions of198/// the initializer are included.199struct IndirectLocalPathEntry {200 enum EntryKind {201 DefaultInit,202 AddressOf,203 VarInit,204 LValToRVal,205 LifetimeBoundCall,206 TemporaryCopy,207 LambdaCaptureInit,208 MemberExpr,209 GslReferenceInit,210 GslPointerInit,211 GslPointerAssignment,212 DefaultArg,213 ParenAggInit,214 } Kind;215 Expr *E;216 union {217 const Decl *D = nullptr;218 const LambdaCapture *Capture;219 };220 IndirectLocalPathEntry() {}221 IndirectLocalPathEntry(EntryKind K, Expr *E) : Kind(K), E(E) {}222 IndirectLocalPathEntry(EntryKind K, Expr *E, const Decl *D)223 : Kind(K), E(E), D(D) {}224 IndirectLocalPathEntry(EntryKind K, Expr *E, const LambdaCapture *Capture)225 : Kind(K), E(E), Capture(Capture) {}226};227 228using IndirectLocalPath = llvm::SmallVectorImpl<IndirectLocalPathEntry>;229 230struct RevertToOldSizeRAII {231 IndirectLocalPath &Path;232 unsigned OldSize = Path.size();233 RevertToOldSizeRAII(IndirectLocalPath &Path) : Path(Path) {}234 ~RevertToOldSizeRAII() { Path.resize(OldSize); }235};236 237using LocalVisitor = llvm::function_ref<bool(IndirectLocalPath &Path, Local L,238 ReferenceKind RK)>;239} // namespace240 241static bool isVarOnPath(const IndirectLocalPath &Path, VarDecl *VD) {242 for (auto E : Path)243 if (E.Kind == IndirectLocalPathEntry::VarInit && E.D == VD)244 return true;245 return false;246}247 248static bool pathContainsInit(const IndirectLocalPath &Path) {249 return llvm::any_of(Path, [=](IndirectLocalPathEntry E) {250 return E.Kind == IndirectLocalPathEntry::DefaultInit ||251 E.Kind == IndirectLocalPathEntry::VarInit;252 });253}254 255static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,256 Expr *Init, LocalVisitor Visit,257 bool RevisitSubinits);258 259static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,260 Expr *Init, ReferenceKind RK,261 LocalVisitor Visit);262 263static bool isPointerLikeType(QualType QT) {264 return isGslPointerType(QT) || QT->isPointerType() || QT->isNullPtrType();265}266 267// Decl::isInStdNamespace will return false for iterators in some STL268// implementations due to them being defined in a namespace outside of the std269// namespace.270static bool isInStlNamespace(const Decl *D) {271 const DeclContext *DC = D->getDeclContext();272 if (!DC)273 return false;274 if (const auto *ND = dyn_cast<NamespaceDecl>(DC))275 if (const IdentifierInfo *II = ND->getIdentifier()) {276 StringRef Name = II->getName();277 if (Name.size() >= 2 && Name.front() == '_' &&278 (Name[1] == '_' || isUppercase(Name[1])))279 return true;280 }281 282 return DC->isStdNamespace();283}284 285// Returns true if the given Record decl is a form of `GSLOwner<Pointer>`286// type, e.g. std::vector<string_view>, std::optional<string_view>.287static bool isContainerOfPointer(const RecordDecl *Container) {288 if (const auto *CTSD =289 dyn_cast_if_present<ClassTemplateSpecializationDecl>(Container)) {290 if (!CTSD->hasAttr<OwnerAttr>()) // Container must be a GSL owner type.291 return false;292 const auto &TAs = CTSD->getTemplateArgs();293 return TAs.size() > 0 && TAs[0].getKind() == TemplateArgument::Type &&294 isPointerLikeType(TAs[0].getAsType());295 }296 return false;297}298static bool isContainerOfOwner(const RecordDecl *Container) {299 const auto *CTSD =300 dyn_cast_if_present<ClassTemplateSpecializationDecl>(Container);301 if (!CTSD)302 return false;303 if (!CTSD->hasAttr<OwnerAttr>()) // Container must be a GSL owner type.304 return false;305 const auto &TAs = CTSD->getTemplateArgs();306 return TAs.size() > 0 && TAs[0].getKind() == TemplateArgument::Type &&307 isGslOwnerType(TAs[0].getAsType());308}309 310// Returns true if the given Record is `std::initializer_list<pointer>`.311static bool isStdInitializerListOfPointer(const RecordDecl *RD) {312 if (const auto *CTSD =313 dyn_cast_if_present<ClassTemplateSpecializationDecl>(RD)) {314 const auto &TAs = CTSD->getTemplateArgs();315 return isInStlNamespace(RD) && RD->getIdentifier() &&316 RD->getName() == "initializer_list" && TAs.size() > 0 &&317 TAs[0].getKind() == TemplateArgument::Type &&318 isPointerLikeType(TAs[0].getAsType());319 }320 return false;321}322 323static bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee) {324 if (auto *Conv = dyn_cast_or_null<CXXConversionDecl>(Callee))325 if (isGslPointerType(Conv->getConversionType()) &&326 Callee->getParent()->hasAttr<OwnerAttr>())327 return true;328 if (!isInStlNamespace(Callee->getParent()))329 return false;330 if (!isGslPointerType(Callee->getFunctionObjectParameterType()) &&331 !isGslOwnerType(Callee->getFunctionObjectParameterType()))332 return false;333 if (isPointerLikeType(Callee->getReturnType())) {334 if (!Callee->getIdentifier())335 return false;336 return llvm::StringSwitch<bool>(Callee->getName())337 .Cases({"begin", "rbegin", "cbegin", "crbegin"}, true)338 .Cases({"end", "rend", "cend", "crend"}, true)339 .Cases({"c_str", "data", "get"}, true)340 // Map and set types.341 .Cases({"find", "equal_range", "lower_bound", "upper_bound"}, true)342 .Default(false);343 }344 if (Callee->getReturnType()->isReferenceType()) {345 if (!Callee->getIdentifier()) {346 auto OO = Callee->getOverloadedOperator();347 if (!Callee->getParent()->hasAttr<OwnerAttr>())348 return false;349 return OO == OverloadedOperatorKind::OO_Subscript ||350 OO == OverloadedOperatorKind::OO_Star;351 }352 return llvm::StringSwitch<bool>(Callee->getName())353 .Cases({"front", "back", "at", "top", "value"}, true)354 .Default(false);355 }356 return false;357}358 359static bool shouldTrackFirstArgument(const FunctionDecl *FD) {360 if (!FD->getIdentifier() || FD->getNumParams() != 1)361 return false;362 const auto *RD = FD->getParamDecl(0)->getType()->getPointeeCXXRecordDecl();363 if (!FD->isInStdNamespace() || !RD || !RD->isInStdNamespace())364 return false;365 if (!RD->hasAttr<PointerAttr>() && !RD->hasAttr<OwnerAttr>())366 return false;367 if (FD->getReturnType()->isPointerType() ||368 isGslPointerType(FD->getReturnType())) {369 return llvm::StringSwitch<bool>(FD->getName())370 .Cases({"begin", "rbegin", "cbegin", "crbegin"}, true)371 .Cases({"end", "rend", "cend", "crend"}, true)372 .Case("data", true)373 .Default(false);374 }375 if (FD->getReturnType()->isReferenceType()) {376 return llvm::StringSwitch<bool>(FD->getName())377 .Cases({"get", "any_cast"}, true)378 .Default(false);379 }380 return false;381}382 383// Returns true if the given constructor is a copy-like constructor, such as384// `Ctor(Owner<U>&&)` or `Ctor(const Owner<U>&)`.385static bool isCopyLikeConstructor(const CXXConstructorDecl *Ctor) {386 if (!Ctor || Ctor->param_size() != 1)387 return false;388 const auto *ParamRefType =389 Ctor->getParamDecl(0)->getType()->getAs<ReferenceType>();390 if (!ParamRefType)391 return false;392 393 // Check if the first parameter type is "Owner<U>".394 if (const auto *TST =395 ParamRefType->getPointeeType()->getAs<TemplateSpecializationType>())396 return TST->getTemplateName()397 .getAsTemplateDecl()398 ->getTemplatedDecl()399 ->hasAttr<OwnerAttr>();400 return false;401}402 403// Returns true if we should perform the GSL analysis on the first argument for404// the given constructor.405static bool406shouldTrackFirstArgumentForConstructor(const CXXConstructExpr *Ctor) {407 const auto *LHSRecordDecl = Ctor->getConstructor()->getParent();408 409 // Case 1, construct a GSL pointer, e.g. std::string_view410 // Always inspect when LHS is a pointer.411 if (LHSRecordDecl->hasAttr<PointerAttr>())412 return true;413 414 if (Ctor->getConstructor()->param_empty() ||415 !isContainerOfPointer(LHSRecordDecl))416 return false;417 418 // Now, the LHS is an Owner<Pointer> type, e.g., std::vector<string_view>.419 //420 // At a high level, we cannot precisely determine what the nested pointer421 // owns. However, by analyzing the RHS owner type, we can use heuristics to422 // infer ownership information. These heuristics are designed to be423 // conservative, minimizing false positives while still providing meaningful424 // diagnostics.425 //426 // While this inference isn't perfect, it helps catch common use-after-free427 // patterns.428 auto RHSArgType = Ctor->getArg(0)->getType();429 const auto *RHSRD = RHSArgType->getAsRecordDecl();430 // LHS is constructed from an intializer_list.431 //432 // std::initializer_list is a proxy object that provides access to the backing433 // array. We perform analysis on it to determine if there are any dangling434 // temporaries in the backing array.435 // E.g. std::vector<string_view> abc = {string()};436 if (isStdInitializerListOfPointer(RHSRD))437 return true;438 439 // RHS must be an owner.440 if (!isGslOwnerType(RHSArgType))441 return false;442 443 // Bail out if the RHS is Owner<Pointer>.444 //445 // We cannot reliably determine what the LHS nested pointer owns -- it could446 // be the entire RHS or the nested pointer in RHS. To avoid false positives,447 // we skip this case, such as:448 // std::stack<std::string_view> s(std::deque<std::string_view>{});449 //450 // TODO: this also has a false negative, it doesn't catch the case like:451 // std::optional<span<int*>> os = std::vector<int*>{}452 if (isContainerOfPointer(RHSRD))453 return false;454 455 // Assume that the nested Pointer is constructed from the nested Owner.456 // E.g. std::optional<string_view> sv = std::optional<string>(s);457 if (isContainerOfOwner(RHSRD))458 return true;459 460 // Now, the LHS is an Owner<Pointer> and the RHS is an Owner<X>, where X is461 // neither an `Owner` nor a `Pointer`.462 //463 // Use the constructor's signature as a hint. If it is a copy-like constructor464 // `Owner1<Pointer>(Owner2<X>&&)`, we assume that the nested pointer is465 // constructed from X. In such cases, we do not diagnose, as `X` is not an466 // owner, e.g.467 // std::optional<string_view> sv = std::optional<Foo>();468 if (const auto *PrimaryCtorTemplate =469 Ctor->getConstructor()->getPrimaryTemplate();470 PrimaryCtorTemplate &&471 isCopyLikeConstructor(dyn_cast_if_present<CXXConstructorDecl>(472 PrimaryCtorTemplate->getTemplatedDecl()))) {473 return false;474 }475 // Assume that the nested pointer is constructed from the whole RHS.476 // E.g. optional<string_view> s = std::string();477 return true;478}479 480// Visit lifetimebound or gsl-pointer arguments.481static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call,482 LocalVisitor Visit) {483 const FunctionDecl *Callee;484 ArrayRef<Expr *> Args;485 486 if (auto *CE = dyn_cast<CallExpr>(Call)) {487 Callee = CE->getDirectCallee();488 Args = llvm::ArrayRef(CE->getArgs(), CE->getNumArgs());489 } else {490 auto *CCE = cast<CXXConstructExpr>(Call);491 Callee = CCE->getConstructor();492 Args = llvm::ArrayRef(CCE->getArgs(), CCE->getNumArgs());493 }494 if (!Callee)495 return;496 497 bool EnableGSLAnalysis = !Callee->getASTContext().getDiagnostics().isIgnored(498 diag::warn_dangling_lifetime_pointer, SourceLocation());499 Expr *ObjectArg = nullptr;500 if (isa<CXXOperatorCallExpr>(Call) && Callee->isCXXInstanceMember()) {501 ObjectArg = Args[0];502 Args = Args.slice(1);503 } else if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Call)) {504 ObjectArg = MCE->getImplicitObjectArgument();505 }506 507 auto VisitLifetimeBoundArg = [&](const Decl *D, Expr *Arg) {508 Path.push_back({IndirectLocalPathEntry::LifetimeBoundCall, Arg, D});509 if (Arg->isGLValue())510 visitLocalsRetainedByReferenceBinding(Path, Arg, RK_ReferenceBinding,511 Visit);512 else513 visitLocalsRetainedByInitializer(Path, Arg, Visit, true);514 Path.pop_back();515 };516 auto VisitGSLPointerArg = [&](const FunctionDecl *Callee, Expr *Arg) {517 auto ReturnType = Callee->getReturnType();518 519 // Once we initialized a value with a non gsl-owner reference, it can no520 // longer dangle.521 if (ReturnType->isReferenceType() &&522 !isGslOwnerType(ReturnType->getPointeeType())) {523 for (const IndirectLocalPathEntry &PE : llvm::reverse(Path)) {524 if (PE.Kind == IndirectLocalPathEntry::GslReferenceInit ||525 PE.Kind == IndirectLocalPathEntry::LifetimeBoundCall)526 continue;527 if (PE.Kind == IndirectLocalPathEntry::GslPointerInit ||528 PE.Kind == IndirectLocalPathEntry::GslPointerAssignment)529 return;530 break;531 }532 }533 Path.push_back({ReturnType->isReferenceType()534 ? IndirectLocalPathEntry::GslReferenceInit535 : IndirectLocalPathEntry::GslPointerInit,536 Arg, Callee});537 if (Arg->isGLValue())538 visitLocalsRetainedByReferenceBinding(Path, Arg, RK_ReferenceBinding,539 Visit);540 else541 visitLocalsRetainedByInitializer(Path, Arg, Visit, true);542 Path.pop_back();543 };544 545 bool CheckCoroCall = false;546 if (const auto *RD = Callee->getReturnType()->getAsRecordDecl()) {547 CheckCoroCall = RD->hasAttr<CoroLifetimeBoundAttr>() &&548 RD->hasAttr<CoroReturnTypeAttr>() &&549 !Callee->hasAttr<CoroDisableLifetimeBoundAttr>();550 }551 552 if (ObjectArg) {553 bool CheckCoroObjArg = CheckCoroCall;554 // Coroutine lambda objects with empty capture list are not lifetimebound.555 if (auto *LE = dyn_cast<LambdaExpr>(ObjectArg->IgnoreImplicit());556 LE && LE->captures().empty())557 CheckCoroObjArg = false;558 // Allow `get_return_object()` as the object param (__promise) is not559 // lifetimebound.560 if (Sema::CanBeGetReturnObject(Callee))561 CheckCoroObjArg = false;562 if (lifetimes::implicitObjectParamIsLifetimeBound(Callee) ||563 CheckCoroObjArg)564 VisitLifetimeBoundArg(Callee, ObjectArg);565 else if (EnableGSLAnalysis) {566 if (auto *CME = dyn_cast<CXXMethodDecl>(Callee);567 CME && shouldTrackImplicitObjectArg(CME))568 VisitGSLPointerArg(Callee, ObjectArg);569 }570 }571 572 const FunctionDecl *CanonCallee =573 lifetimes::getDeclWithMergedLifetimeBoundAttrs(Callee);574 unsigned NP = std::min(Callee->getNumParams(), CanonCallee->getNumParams());575 for (unsigned I = 0, N = std::min<unsigned>(NP, Args.size()); I != N; ++I) {576 Expr *Arg = Args[I];577 RevertToOldSizeRAII RAII(Path);578 if (auto *DAE = dyn_cast<CXXDefaultArgExpr>(Arg)) {579 Path.push_back(580 {IndirectLocalPathEntry::DefaultArg, DAE, DAE->getParam()});581 Arg = DAE->getExpr();582 }583 if (CheckCoroCall ||584 CanonCallee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>())585 VisitLifetimeBoundArg(CanonCallee->getParamDecl(I), Arg);586 else if (const auto *CaptureAttr =587 CanonCallee->getParamDecl(I)->getAttr<LifetimeCaptureByAttr>();588 CaptureAttr && isa<CXXConstructorDecl>(CanonCallee) &&589 llvm::any_of(CaptureAttr->params(), [](int ArgIdx) {590 return ArgIdx == LifetimeCaptureByAttr::This;591 }))592 // `lifetime_capture_by(this)` in a class constructor has the same593 // semantics as `lifetimebound`:594 //595 // struct Foo {596 // const int& a;597 // // Equivalent to Foo(const int& t [[clang::lifetimebound]])598 // Foo(const int& t [[clang::lifetime_capture_by(this)]]) : a(t) {}599 // };600 //601 // In the implementation, `lifetime_capture_by` is treated as an alias for602 // `lifetimebound` and shares the same code path. This implies the emitted603 // diagnostics will be emitted under `-Wdangling`, not604 // `-Wdangling-capture`.605 VisitLifetimeBoundArg(CanonCallee->getParamDecl(I), Arg);606 else if (EnableGSLAnalysis && I == 0) {607 // Perform GSL analysis for the first argument608 if (shouldTrackFirstArgument(CanonCallee)) {609 VisitGSLPointerArg(CanonCallee, Arg);610 } else if (auto *Ctor = dyn_cast<CXXConstructExpr>(Call);611 Ctor && shouldTrackFirstArgumentForConstructor(Ctor)) {612 VisitGSLPointerArg(Ctor->getConstructor(), Arg);613 }614 }615 }616}617 618/// Visit the locals that would be reachable through a reference bound to the619/// glvalue expression \c Init.620static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,621 Expr *Init, ReferenceKind RK,622 LocalVisitor Visit) {623 RevertToOldSizeRAII RAII(Path);624 625 // Walk past any constructs which we can lifetime-extend across.626 Expr *Old;627 do {628 Old = Init;629 630 if (auto *FE = dyn_cast<FullExpr>(Init))631 Init = FE->getSubExpr();632 633 if (InitListExpr *ILE = dyn_cast<InitListExpr>(Init)) {634 // If this is just redundant braces around an initializer, step over it.635 if (ILE->isTransparent())636 Init = ILE->getInit(0);637 }638 639 if (MemberExpr *ME = dyn_cast<MemberExpr>(Init->IgnoreImpCasts()))640 Path.push_back(641 {IndirectLocalPathEntry::MemberExpr, ME, ME->getMemberDecl()});642 // Step over any subobject adjustments; we may have a materialized643 // temporary inside them.644 Init = const_cast<Expr *>(Init->skipRValueSubobjectAdjustments());645 646 // Per current approach for DR1376, look through casts to reference type647 // when performing lifetime extension.648 if (CastExpr *CE = dyn_cast<CastExpr>(Init))649 if (CE->getSubExpr()->isGLValue())650 Init = CE->getSubExpr();651 652 // Per the current approach for DR1299, look through array element access653 // on array glvalues when performing lifetime extension.654 if (auto *ASE = dyn_cast<ArraySubscriptExpr>(Init)) {655 Init = ASE->getBase();656 auto *ICE = dyn_cast<ImplicitCastExpr>(Init);657 if (ICE && ICE->getCastKind() == CK_ArrayToPointerDecay)658 Init = ICE->getSubExpr();659 else660 // We can't lifetime extend through this but we might still find some661 // retained temporaries.662 return visitLocalsRetainedByInitializer(Path, Init, Visit, true);663 }664 665 // Step into CXXDefaultInitExprs so we can diagnose cases where a666 // constructor inherits one as an implicit mem-initializer.667 if (auto *DIE = dyn_cast<CXXDefaultInitExpr>(Init)) {668 Path.push_back(669 {IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()});670 Init = DIE->getExpr();671 }672 } while (Init != Old);673 674 if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Init)) {675 if (Visit(Path, Local(MTE), RK))676 visitLocalsRetainedByInitializer(Path, MTE->getSubExpr(), Visit, true);677 }678 679 if (auto *M = dyn_cast<MemberExpr>(Init)) {680 // Lifetime of a non-reference type field is same as base object.681 if (auto *F = dyn_cast<FieldDecl>(M->getMemberDecl());682 F && !F->getType()->isReferenceType())683 visitLocalsRetainedByInitializer(Path, M->getBase(), Visit, true);684 }685 686 if (isa<CallExpr>(Init))687 return visitFunctionCallArguments(Path, Init, Visit);688 689 switch (Init->getStmtClass()) {690 case Stmt::DeclRefExprClass: {691 // If we find the name of a local non-reference parameter, we could have a692 // lifetime problem.693 auto *DRE = cast<DeclRefExpr>(Init);694 auto *VD = dyn_cast<VarDecl>(DRE->getDecl());695 if (VD && VD->hasLocalStorage() &&696 !DRE->refersToEnclosingVariableOrCapture()) {697 if (!VD->getType()->isReferenceType()) {698 Visit(Path, Local(DRE), RK);699 } else if (isa<ParmVarDecl>(DRE->getDecl())) {700 // The lifetime of a reference parameter is unknown; assume it's OK701 // for now.702 break;703 } else if (VD->getInit() && !isVarOnPath(Path, VD)) {704 Path.push_back({IndirectLocalPathEntry::VarInit, DRE, VD});705 visitLocalsRetainedByReferenceBinding(Path, VD->getInit(),706 RK_ReferenceBinding, Visit);707 }708 }709 break;710 }711 712 case Stmt::UnaryOperatorClass: {713 // The only unary operator that make sense to handle here714 // is Deref. All others don't resolve to a "name." This includes715 // handling all sorts of rvalues passed to a unary operator.716 const UnaryOperator *U = cast<UnaryOperator>(Init);717 if (U->getOpcode() == UO_Deref)718 visitLocalsRetainedByInitializer(Path, U->getSubExpr(), Visit, true);719 break;720 }721 722 case Stmt::ArraySectionExprClass: {723 visitLocalsRetainedByInitializer(724 Path, cast<ArraySectionExpr>(Init)->getBase(), Visit, true);725 break;726 }727 728 case Stmt::ConditionalOperatorClass:729 case Stmt::BinaryConditionalOperatorClass: {730 auto *C = cast<AbstractConditionalOperator>(Init);731 if (!C->getTrueExpr()->getType()->isVoidType())732 visitLocalsRetainedByReferenceBinding(Path, C->getTrueExpr(), RK, Visit);733 if (!C->getFalseExpr()->getType()->isVoidType())734 visitLocalsRetainedByReferenceBinding(Path, C->getFalseExpr(), RK, Visit);735 break;736 }737 738 case Stmt::CompoundLiteralExprClass: {739 if (auto *CLE = dyn_cast<CompoundLiteralExpr>(Init)) {740 if (!CLE->isFileScope())741 Visit(Path, Local(CLE), RK);742 }743 break;744 }745 746 // FIXME: Visit the left-hand side of an -> or ->*.747 748 default:749 break;750 }751}752 753/// Visit the locals that would be reachable through an object initialized by754/// the prvalue expression \c Init.755static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,756 Expr *Init, LocalVisitor Visit,757 bool RevisitSubinits) {758 RevertToOldSizeRAII RAII(Path);759 760 Expr *Old;761 do {762 Old = Init;763 764 // Step into CXXDefaultInitExprs so we can diagnose cases where a765 // constructor inherits one as an implicit mem-initializer.766 if (auto *DIE = dyn_cast<CXXDefaultInitExpr>(Init)) {767 Path.push_back(768 {IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()});769 Init = DIE->getExpr();770 }771 772 if (auto *FE = dyn_cast<FullExpr>(Init))773 Init = FE->getSubExpr();774 775 // Dig out the expression which constructs the extended temporary.776 Init = const_cast<Expr *>(Init->skipRValueSubobjectAdjustments());777 778 if (CXXBindTemporaryExpr *BTE = dyn_cast<CXXBindTemporaryExpr>(Init))779 Init = BTE->getSubExpr();780 781 Init = Init->IgnoreParens();782 783 // Step over value-preserving rvalue casts.784 if (auto *CE = dyn_cast<CastExpr>(Init)) {785 switch (CE->getCastKind()) {786 case CK_LValueToRValue:787 // If we can match the lvalue to a const object, we can look at its788 // initializer.789 Path.push_back({IndirectLocalPathEntry::LValToRVal, CE});790 return visitLocalsRetainedByReferenceBinding(791 Path, Init, RK_ReferenceBinding,792 [&](IndirectLocalPath &Path, Local L, ReferenceKind RK) -> bool {793 if (auto *DRE = dyn_cast<DeclRefExpr>(L)) {794 auto *VD = dyn_cast<VarDecl>(DRE->getDecl());795 if (VD && VD->getType().isConstQualified() && VD->getInit() &&796 !isVarOnPath(Path, VD)) {797 Path.push_back({IndirectLocalPathEntry::VarInit, DRE, VD});798 visitLocalsRetainedByInitializer(Path, VD->getInit(), Visit,799 true);800 }801 } else if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L)) {802 if (MTE->getType().isConstQualified())803 visitLocalsRetainedByInitializer(Path, MTE->getSubExpr(),804 Visit, true);805 }806 return false;807 });808 809 // We assume that objects can be retained by pointers cast to integers,810 // but not if the integer is cast to floating-point type or to _Complex.811 // We assume that casts to 'bool' do not preserve enough information to812 // retain a local object.813 case CK_NoOp:814 case CK_BitCast:815 case CK_BaseToDerived:816 case CK_DerivedToBase:817 case CK_UncheckedDerivedToBase:818 case CK_Dynamic:819 case CK_ToUnion:820 case CK_UserDefinedConversion:821 case CK_ConstructorConversion:822 case CK_IntegralToPointer:823 case CK_PointerToIntegral:824 case CK_VectorSplat:825 case CK_IntegralCast:826 case CK_CPointerToObjCPointerCast:827 case CK_BlockPointerToObjCPointerCast:828 case CK_AnyPointerToBlockPointerCast:829 case CK_AddressSpaceConversion:830 break;831 832 case CK_ArrayToPointerDecay:833 // Model array-to-pointer decay as taking the address of the array834 // lvalue.835 Path.push_back({IndirectLocalPathEntry::AddressOf, CE});836 return visitLocalsRetainedByReferenceBinding(837 Path, CE->getSubExpr(), RK_ReferenceBinding, Visit);838 839 default:840 return;841 }842 843 Init = CE->getSubExpr();844 }845 } while (Old != Init);846 847 // C++17 [dcl.init.list]p6:848 // initializing an initializer_list object from the array extends the849 // lifetime of the array exactly like binding a reference to a temporary.850 if (auto *ILE = dyn_cast<CXXStdInitializerListExpr>(Init))851 return visitLocalsRetainedByReferenceBinding(Path, ILE->getSubExpr(),852 RK_StdInitializerList, Visit);853 854 if (InitListExpr *ILE = dyn_cast<InitListExpr>(Init)) {855 // We already visited the elements of this initializer list while856 // performing the initialization. Don't visit them again unless we've857 // changed the lifetime of the initialized entity.858 if (!RevisitSubinits)859 return;860 861 if (ILE->isTransparent())862 return visitLocalsRetainedByInitializer(Path, ILE->getInit(0), Visit,863 RevisitSubinits);864 865 if (ILE->getType()->isArrayType()) {866 for (unsigned I = 0, N = ILE->getNumInits(); I != N; ++I)867 visitLocalsRetainedByInitializer(Path, ILE->getInit(I), Visit,868 RevisitSubinits);869 return;870 }871 872 if (CXXRecordDecl *RD = ILE->getType()->getAsCXXRecordDecl()) {873 assert(RD->isAggregate() && "aggregate init on non-aggregate");874 875 // If we lifetime-extend a braced initializer which is initializing an876 // aggregate, and that aggregate contains reference members which are877 // bound to temporaries, those temporaries are also lifetime-extended.878 if (RD->isUnion() && ILE->getInitializedFieldInUnion() &&879 ILE->getInitializedFieldInUnion()->getType()->isReferenceType())880 visitLocalsRetainedByReferenceBinding(Path, ILE->getInit(0),881 RK_ReferenceBinding, Visit);882 else {883 unsigned Index = 0;884 for (; Index < RD->getNumBases() && Index < ILE->getNumInits(); ++Index)885 visitLocalsRetainedByInitializer(Path, ILE->getInit(Index), Visit,886 RevisitSubinits);887 for (const auto *I : RD->fields()) {888 if (Index >= ILE->getNumInits())889 break;890 if (I->isUnnamedBitField())891 continue;892 Expr *SubInit = ILE->getInit(Index);893 if (I->getType()->isReferenceType())894 visitLocalsRetainedByReferenceBinding(Path, SubInit,895 RK_ReferenceBinding, Visit);896 else897 // This might be either aggregate-initialization of a member or898 // initialization of a std::initializer_list object. Regardless,899 // we should recursively lifetime-extend that initializer.900 visitLocalsRetainedByInitializer(Path, SubInit, Visit,901 RevisitSubinits);902 ++Index;903 }904 }905 }906 return;907 }908 909 // The lifetime of an init-capture is that of the closure object constructed910 // by a lambda-expression.911 if (auto *LE = dyn_cast<LambdaExpr>(Init)) {912 LambdaExpr::capture_iterator CapI = LE->capture_begin();913 for (Expr *E : LE->capture_inits()) {914 assert(CapI != LE->capture_end());915 const LambdaCapture &Cap = *CapI++;916 if (!E)917 continue;918 if (Cap.capturesVariable())919 Path.push_back({IndirectLocalPathEntry::LambdaCaptureInit, E, &Cap});920 if (E->isGLValue())921 visitLocalsRetainedByReferenceBinding(Path, E, RK_ReferenceBinding,922 Visit);923 else924 visitLocalsRetainedByInitializer(Path, E, Visit, true);925 if (Cap.capturesVariable())926 Path.pop_back();927 }928 }929 930 // Assume that a copy or move from a temporary references the same objects931 // that the temporary does.932 if (auto *CCE = dyn_cast<CXXConstructExpr>(Init)) {933 if (CCE->getConstructor()->isCopyOrMoveConstructor()) {934 if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(CCE->getArg(0))) {935 Expr *Arg = MTE->getSubExpr();936 Path.push_back({IndirectLocalPathEntry::TemporaryCopy, Arg,937 CCE->getConstructor()});938 visitLocalsRetainedByInitializer(Path, Arg, Visit, true);939 Path.pop_back();940 }941 }942 }943 944 if (isa<CallExpr>(Init) || isa<CXXConstructExpr>(Init))945 return visitFunctionCallArguments(Path, Init, Visit);946 947 if (auto *CPE = dyn_cast<CXXParenListInitExpr>(Init)) {948 RevertToOldSizeRAII RAII(Path);949 Path.push_back({IndirectLocalPathEntry::ParenAggInit, CPE});950 for (auto *I : CPE->getInitExprs()) {951 if (I->isGLValue())952 visitLocalsRetainedByReferenceBinding(Path, I, RK_ReferenceBinding,953 Visit);954 else955 visitLocalsRetainedByInitializer(Path, I, Visit, true);956 }957 }958 switch (Init->getStmtClass()) {959 case Stmt::UnaryOperatorClass: {960 auto *UO = cast<UnaryOperator>(Init);961 // If the initializer is the address of a local, we could have a lifetime962 // problem.963 if (UO->getOpcode() == UO_AddrOf) {964 // If this is &rvalue, then it's ill-formed and we have already diagnosed965 // it. Don't produce a redundant warning about the lifetime of the966 // temporary.967 if (isa<MaterializeTemporaryExpr>(UO->getSubExpr()))968 return;969 970 Path.push_back({IndirectLocalPathEntry::AddressOf, UO});971 visitLocalsRetainedByReferenceBinding(Path, UO->getSubExpr(),972 RK_ReferenceBinding, Visit);973 }974 break;975 }976 977 case Stmt::BinaryOperatorClass: {978 // Handle pointer arithmetic.979 auto *BO = cast<BinaryOperator>(Init);980 BinaryOperatorKind BOK = BO->getOpcode();981 if (!BO->getType()->isPointerType() || (BOK != BO_Add && BOK != BO_Sub))982 break;983 984 if (BO->getLHS()->getType()->isPointerType())985 visitLocalsRetainedByInitializer(Path, BO->getLHS(), Visit, true);986 else if (BO->getRHS()->getType()->isPointerType())987 visitLocalsRetainedByInitializer(Path, BO->getRHS(), Visit, true);988 break;989 }990 991 case Stmt::ConditionalOperatorClass:992 case Stmt::BinaryConditionalOperatorClass: {993 auto *C = cast<AbstractConditionalOperator>(Init);994 // In C++, we can have a throw-expression operand, which has 'void' type995 // and isn't interesting from a lifetime perspective.996 if (!C->getTrueExpr()->getType()->isVoidType())997 visitLocalsRetainedByInitializer(Path, C->getTrueExpr(), Visit, true);998 if (!C->getFalseExpr()->getType()->isVoidType())999 visitLocalsRetainedByInitializer(Path, C->getFalseExpr(), Visit, true);1000 break;1001 }1002 1003 case Stmt::BlockExprClass:1004 if (cast<BlockExpr>(Init)->getBlockDecl()->hasCaptures()) {1005 // This is a local block, whose lifetime is that of the function.1006 Visit(Path, Local(cast<BlockExpr>(Init)), RK_ReferenceBinding);1007 }1008 break;1009 1010 case Stmt::AddrLabelExprClass:1011 // We want to warn if the address of a label would escape the function.1012 Visit(Path, Local(cast<AddrLabelExpr>(Init)), RK_ReferenceBinding);1013 break;1014 1015 default:1016 break;1017 }1018}1019 1020/// Whether a path to an object supports lifetime extension.1021enum PathLifetimeKind {1022 /// Lifetime-extend along this path.1023 Extend,1024 /// Do not lifetime extend along this path.1025 NoExtend1026};1027 1028/// Determine whether this is an indirect path to a temporary that we are1029/// supposed to lifetime-extend along.1030static PathLifetimeKind1031shouldLifetimeExtendThroughPath(const IndirectLocalPath &Path) {1032 for (auto Elem : Path) {1033 if (Elem.Kind == IndirectLocalPathEntry::MemberExpr ||1034 Elem.Kind == IndirectLocalPathEntry::LambdaCaptureInit)1035 continue;1036 return Elem.Kind == IndirectLocalPathEntry::DefaultInit1037 ? PathLifetimeKind::Extend1038 : PathLifetimeKind::NoExtend;1039 }1040 return PathLifetimeKind::Extend;1041}1042 1043/// Find the range for the first interesting entry in the path at or after I.1044static SourceRange nextPathEntryRange(const IndirectLocalPath &Path, unsigned I,1045 Expr *E) {1046 for (unsigned N = Path.size(); I != N; ++I) {1047 switch (Path[I].Kind) {1048 case IndirectLocalPathEntry::AddressOf:1049 case IndirectLocalPathEntry::LValToRVal:1050 case IndirectLocalPathEntry::LifetimeBoundCall:1051 case IndirectLocalPathEntry::TemporaryCopy:1052 case IndirectLocalPathEntry::GslReferenceInit:1053 case IndirectLocalPathEntry::GslPointerInit:1054 case IndirectLocalPathEntry::GslPointerAssignment:1055 case IndirectLocalPathEntry::ParenAggInit:1056 case IndirectLocalPathEntry::MemberExpr:1057 // These exist primarily to mark the path as not permitting or1058 // supporting lifetime extension.1059 break;1060 1061 case IndirectLocalPathEntry::VarInit:1062 if (cast<VarDecl>(Path[I].D)->isImplicit())1063 return SourceRange();1064 [[fallthrough]];1065 case IndirectLocalPathEntry::DefaultInit:1066 return Path[I].E->getSourceRange();1067 1068 case IndirectLocalPathEntry::LambdaCaptureInit:1069 if (!Path[I].Capture->capturesVariable())1070 continue;1071 return Path[I].E->getSourceRange();1072 1073 case IndirectLocalPathEntry::DefaultArg:1074 return cast<CXXDefaultArgExpr>(Path[I].E)->getUsedLocation();1075 }1076 }1077 return E->getSourceRange();1078}1079 1080static bool pathOnlyHandlesGslPointer(const IndirectLocalPath &Path) {1081 for (const auto &It : llvm::reverse(Path)) {1082 switch (It.Kind) {1083 case IndirectLocalPathEntry::VarInit:1084 case IndirectLocalPathEntry::AddressOf:1085 case IndirectLocalPathEntry::LifetimeBoundCall:1086 case IndirectLocalPathEntry::MemberExpr:1087 continue;1088 case IndirectLocalPathEntry::GslPointerInit:1089 case IndirectLocalPathEntry::GslReferenceInit:1090 case IndirectLocalPathEntry::GslPointerAssignment:1091 return true;1092 default:1093 return false;1094 }1095 }1096 return false;1097}1098// Result of analyzing the Path for GSLPointer.1099enum AnalysisResult {1100 // Path does not correspond to a GSLPointer.1101 NotGSLPointer,1102 1103 // A relevant case was identified.1104 Report,1105 // Stop the entire traversal.1106 Abandon,1107 // Skip this step and continue traversing inner AST nodes.1108 Skip,1109};1110// Analyze cases where a GSLPointer is initialized or assigned from a1111// temporary owner object.1112static AnalysisResult analyzePathForGSLPointer(const IndirectLocalPath &Path,1113 Local L, LifetimeKind LK) {1114 if (!pathOnlyHandlesGslPointer(Path))1115 return NotGSLPointer;1116 1117 // At this point, Path represents a series of operations involving a1118 // GSLPointer, either in the process of initialization or assignment.1119 1120 // Process temporary base objects for MemberExpr cases, e.g. Temp().field.1121 for (const auto &E : Path) {1122 if (E.Kind == IndirectLocalPathEntry::MemberExpr) {1123 // Avoid interfering with the local base object.1124 if (pathContainsInit(Path))1125 return Abandon;1126 1127 // We are not interested in the temporary base objects of gsl Pointers:1128 // auto p1 = Temp().ptr; // Here p1 might not dangle.1129 // However, we want to diagnose for gsl owner fields:1130 // auto p2 = Temp().owner; // Here p2 is dangling.1131 if (const auto *FD = llvm::dyn_cast_or_null<FieldDecl>(E.D);1132 FD && !FD->getType()->isReferenceType() &&1133 isGslOwnerType(FD->getType()) && LK != LK_MemInitializer) {1134 return Report;1135 }1136 return Abandon;1137 }1138 }1139 1140 // Note: A LifetimeBoundCall can appear interleaved in this sequence.1141 // For example:1142 // const std::string& Ref(const std::string& a [[clang::lifetimebound]]);1143 // string_view abc = Ref(std::string());1144 // The "Path" is [GSLPointerInit, LifetimeboundCall], where "L" is the1145 // temporary "std::string()" object. We need to check the return type of the1146 // function with the lifetimebound attribute.1147 if (Path.back().Kind == IndirectLocalPathEntry::LifetimeBoundCall) {1148 // The lifetimebound applies to the implicit object parameter of a method.1149 const FunctionDecl *FD =1150 llvm::dyn_cast_or_null<FunctionDecl>(Path.back().D);1151 // The lifetimebound applies to a function parameter.1152 if (const auto *PD = llvm::dyn_cast<ParmVarDecl>(Path.back().D))1153 FD = llvm::dyn_cast<FunctionDecl>(PD->getDeclContext());1154 1155 if (isa_and_present<CXXConstructorDecl>(FD)) {1156 // Constructor case: the parameter is annotated with lifetimebound1157 // e.g., GSLPointer(const S& s [[clang::lifetimebound]])1158 // We still respect this case even the type S is not an owner.1159 return Report;1160 }1161 // Check the return type, e.g.1162 // const GSLOwner& func(const Foo& foo [[clang::lifetimebound]])1163 // GSLOwner* func(cosnt Foo& foo [[clang::lifetimebound]])1164 // GSLPointer func(const Foo& foo [[clang::lifetimebound]])1165 if (FD && ((FD->getReturnType()->isPointerOrReferenceType() &&1166 isGslOwnerType(FD->getReturnType()->getPointeeType())) ||1167 isGslPointerType(FD->getReturnType())))1168 return Report;1169 1170 return Abandon;1171 }1172 1173 if (isa<DeclRefExpr>(L)) {1174 // We do not want to follow the references when returning a pointer1175 // originating from a local owner to avoid the following false positive:1176 // int &p = *localUniquePtr;1177 // someContainer.add(std::move(localUniquePtr));1178 // return p;1179 if (!pathContainsInit(Path) && isGslOwnerType(L->getType()))1180 return Report;1181 return Abandon;1182 }1183 1184 // The GSLPointer is from a temporary object.1185 auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L);1186 1187 bool IsGslPtrValueFromGslTempOwner =1188 MTE && !MTE->getExtendingDecl() && isGslOwnerType(MTE->getType());1189 // Skipping a chain of initializing gsl::Pointer annotated objects.1190 // We are looking only for the final source to find out if it was1191 // a local or temporary owner or the address of a local1192 // variable/param.1193 if (!IsGslPtrValueFromGslTempOwner)1194 return Skip;1195 return Report;1196}1197 1198static bool shouldRunGSLAssignmentAnalysis(const Sema &SemaRef,1199 const AssignedEntity &Entity) {1200 bool EnableGSLAssignmentWarnings = !SemaRef.getDiagnostics().isIgnored(1201 diag::warn_dangling_lifetime_pointer_assignment, SourceLocation());1202 return (EnableGSLAssignmentWarnings &&1203 (isGslPointerType(Entity.LHS->getType()) ||1204 lifetimes::isAssignmentOperatorLifetimeBound(1205 Entity.AssignmentOperator)));1206}1207 1208static void1209checkExprLifetimeImpl(Sema &SemaRef, const InitializedEntity *InitEntity,1210 const InitializedEntity *ExtendingEntity, LifetimeKind LK,1211 const AssignedEntity *AEntity,1212 const CapturingEntity *CapEntity, Expr *Init) {1213 assert(!AEntity || LK == LK_Assignment);1214 assert(!CapEntity || LK == LK_LifetimeCapture);1215 assert(!InitEntity || (LK != LK_Assignment && LK != LK_LifetimeCapture));1216 // If this entity doesn't have an interesting lifetime, don't bother looking1217 // for temporaries within its initializer.1218 if (LK == LK_FullExpression)1219 return;1220 1221 // FIXME: consider moving the TemporaryVisitor and visitLocalsRetained*1222 // functions to a dedicated class.1223 auto TemporaryVisitor = [&](const IndirectLocalPath &Path, Local L,1224 ReferenceKind RK) -> bool {1225 SourceRange DiagRange = nextPathEntryRange(Path, 0, L);1226 SourceLocation DiagLoc = DiagRange.getBegin();1227 1228 auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L);1229 1230 bool IsGslPtrValueFromGslTempOwner = true;1231 switch (analyzePathForGSLPointer(Path, L, LK)) {1232 case Abandon:1233 return false;1234 case Skip:1235 return true;1236 case NotGSLPointer:1237 IsGslPtrValueFromGslTempOwner = false;1238 [[fallthrough]];1239 case Report:1240 break;1241 }1242 1243 switch (LK) {1244 case LK_FullExpression:1245 llvm_unreachable("already handled this");1246 1247 case LK_Extended: {1248 if (!MTE) {1249 // The initialized entity has lifetime beyond the full-expression,1250 // and the local entity does too, so don't warn.1251 //1252 // FIXME: We should consider warning if a static / thread storage1253 // duration variable retains an automatic storage duration local.1254 return false;1255 }1256 1257 switch (shouldLifetimeExtendThroughPath(Path)) {1258 case PathLifetimeKind::Extend:1259 // Update the storage duration of the materialized temporary.1260 // FIXME: Rebuild the expression instead of mutating it.1261 MTE->setExtendingDecl(ExtendingEntity->getDecl(),1262 ExtendingEntity->allocateManglingNumber());1263 // Also visit the temporaries lifetime-extended by this initializer.1264 return true;1265 1266 case PathLifetimeKind::NoExtend:1267 if (SemaRef.getLangOpts().CPlusPlus23 && InitEntity) {1268 if (const VarDecl *VD =1269 dyn_cast_if_present<VarDecl>(InitEntity->getDecl());1270 VD && VD->isCXXForRangeImplicitVar()) {1271 return false;1272 }1273 }1274 1275 if (IsGslPtrValueFromGslTempOwner && DiagLoc.isValid()) {1276 SemaRef.Diag(DiagLoc, diag::warn_dangling_lifetime_pointer)1277 << DiagRange;1278 return false;1279 }1280 1281 // If the path goes through the initialization of a variable or field,1282 // it can't possibly reach a temporary created in this full-expression.1283 // We will have already diagnosed any problems with the initializer.1284 if (pathContainsInit(Path))1285 return false;1286 1287 SemaRef.Diag(DiagLoc, diag::warn_dangling_variable)1288 << RK << !InitEntity->getParent()1289 << ExtendingEntity->getDecl()->isImplicit()1290 << ExtendingEntity->getDecl() << Init->isGLValue() << DiagRange;1291 break;1292 }1293 break;1294 }1295 1296 case LK_LifetimeCapture: {1297 // The captured entity has lifetime beyond the full-expression,1298 // and the capturing entity does too, so don't warn.1299 if (!MTE)1300 return false;1301 if (CapEntity->Entity)1302 SemaRef.Diag(DiagLoc, diag::warn_dangling_reference_captured)1303 << CapEntity->Entity << DiagRange;1304 else1305 SemaRef.Diag(DiagLoc, diag::warn_dangling_reference_captured_by_unknown)1306 << DiagRange;1307 return false;1308 }1309 1310 case LK_Assignment: {1311 if (!MTE || pathContainsInit(Path))1312 return false;1313 if (IsGslPtrValueFromGslTempOwner)1314 SemaRef.Diag(DiagLoc, diag::warn_dangling_lifetime_pointer_assignment)1315 << AEntity->LHS << DiagRange;1316 else1317 SemaRef.Diag(DiagLoc, diag::warn_dangling_pointer_assignment)1318 << AEntity->LHS->getType()->isPointerType() << AEntity->LHS1319 << DiagRange;1320 return false;1321 }1322 case LK_MemInitializer: {1323 if (MTE) {1324 // Under C++ DR1696, if a mem-initializer (or a default member1325 // initializer used by the absence of one) would lifetime-extend a1326 // temporary, the program is ill-formed.1327 if (auto *ExtendingDecl =1328 ExtendingEntity ? ExtendingEntity->getDecl() : nullptr) {1329 if (IsGslPtrValueFromGslTempOwner) {1330 SemaRef.Diag(DiagLoc, diag::warn_dangling_lifetime_pointer_member)1331 << ExtendingDecl << DiagRange;1332 SemaRef.Diag(ExtendingDecl->getLocation(),1333 diag::note_ref_or_ptr_member_declared_here)1334 << true;1335 return false;1336 }1337 bool IsSubobjectMember = ExtendingEntity != InitEntity;1338 SemaRef.Diag(DiagLoc, shouldLifetimeExtendThroughPath(Path) !=1339 PathLifetimeKind::NoExtend1340 ? diag::err_dangling_member1341 : diag::warn_dangling_member)1342 << ExtendingDecl << IsSubobjectMember << RK << DiagRange;1343 // Don't bother adding a note pointing to the field if we're inside1344 // its default member initializer; our primary diagnostic points to1345 // the same place in that case.1346 if (Path.empty() ||1347 Path.back().Kind != IndirectLocalPathEntry::DefaultInit) {1348 SemaRef.Diag(ExtendingDecl->getLocation(),1349 diag::note_lifetime_extending_member_declared_here)1350 << RK << IsSubobjectMember;1351 }1352 } else {1353 // We have a mem-initializer but no particular field within it; this1354 // is either a base class or a delegating initializer directly1355 // initializing the base-class from something that doesn't live long1356 // enough.1357 //1358 // FIXME: Warn on this.1359 return false;1360 }1361 } else {1362 // Paths via a default initializer can only occur during error recovery1363 // (there's no other way that a default initializer can refer to a1364 // local). Don't produce a bogus warning on those cases.1365 if (pathContainsInit(Path))1366 return false;1367 1368 auto *DRE = dyn_cast<DeclRefExpr>(L);1369 // Suppress false positives for code like the one below:1370 // Ctor(unique_ptr<T> up) : pointer(up.get()), owner(move(up)) {}1371 // FIXME: move this logic to analyzePathForGSLPointer.1372 if (DRE && isGslOwnerType(DRE->getType()))1373 return false;1374 1375 auto *VD = DRE ? dyn_cast<VarDecl>(DRE->getDecl()) : nullptr;1376 if (!VD) {1377 // A member was initialized to a local block.1378 // FIXME: Warn on this.1379 return false;1380 }1381 1382 if (auto *Member =1383 ExtendingEntity ? ExtendingEntity->getDecl() : nullptr) {1384 bool IsPointer = !Member->getType()->isReferenceType();1385 SemaRef.Diag(DiagLoc,1386 IsPointer ? diag::warn_init_ptr_member_to_parameter_addr1387 : diag::warn_bind_ref_member_to_parameter)1388 << Member << VD << isa<ParmVarDecl>(VD) << DiagRange;1389 SemaRef.Diag(Member->getLocation(),1390 diag::note_ref_or_ptr_member_declared_here)1391 << (unsigned)IsPointer;1392 }1393 }1394 break;1395 }1396 1397 case LK_New:1398 if (isa<MaterializeTemporaryExpr>(L)) {1399 if (IsGslPtrValueFromGslTempOwner)1400 SemaRef.Diag(DiagLoc, diag::warn_dangling_lifetime_pointer)1401 << DiagRange;1402 else1403 SemaRef.Diag(DiagLoc, RK == RK_ReferenceBinding1404 ? diag::warn_new_dangling_reference1405 : diag::warn_new_dangling_initializer_list)1406 << !InitEntity->getParent() << DiagRange;1407 } else {1408 // We can't determine if the allocation outlives the local declaration.1409 return false;1410 }1411 break;1412 1413 case LK_Return:1414 case LK_MustTail:1415 case LK_StmtExprResult:1416 if (auto *DRE = dyn_cast<DeclRefExpr>(L)) {1417 // We can't determine if the local variable outlives the statement1418 // expression.1419 if (LK == LK_StmtExprResult)1420 return false;1421 SemaRef.Diag(DiagLoc, diag::warn_ret_stack_addr_ref)1422 << InitEntity->getType()->isReferenceType() << DRE->getDecl()1423 << isa<ParmVarDecl>(DRE->getDecl()) << (LK == LK_MustTail)1424 << DiagRange;1425 } else if (isa<BlockExpr>(L)) {1426 SemaRef.Diag(DiagLoc, diag::err_ret_local_block) << DiagRange;1427 } else if (isa<AddrLabelExpr>(L)) {1428 // Don't warn when returning a label from a statement expression.1429 // Leaving the scope doesn't end its lifetime.1430 if (LK == LK_StmtExprResult)1431 return false;1432 SemaRef.Diag(DiagLoc, diag::warn_ret_addr_label) << DiagRange;1433 } else if (auto *CLE = dyn_cast<CompoundLiteralExpr>(L)) {1434 SemaRef.Diag(DiagLoc, diag::warn_ret_stack_addr_ref)1435 << InitEntity->getType()->isReferenceType() << CLE->getInitializer()1436 << 2 << (LK == LK_MustTail) << DiagRange;1437 } else {1438 // P2748R5: Disallow Binding a Returned Glvalue to a Temporary.1439 // [stmt.return]/p6: In a function whose return type is a reference,1440 // other than an invented function for std::is_convertible ([meta.rel]),1441 // a return statement that binds the returned reference to a temporary1442 // expression ([class.temporary]) is ill-formed.1443 if (SemaRef.getLangOpts().CPlusPlus26 &&1444 InitEntity->getType()->isReferenceType())1445 SemaRef.Diag(DiagLoc, diag::err_ret_local_temp_ref)1446 << InitEntity->getType()->isReferenceType() << DiagRange;1447 else if (LK == LK_MustTail)1448 SemaRef.Diag(DiagLoc, diag::warn_musttail_local_temp_addr_ref)1449 << InitEntity->getType()->isReferenceType() << DiagRange;1450 else1451 SemaRef.Diag(DiagLoc, diag::warn_ret_local_temp_addr_ref)1452 << InitEntity->getType()->isReferenceType() << DiagRange;1453 }1454 break;1455 }1456 1457 for (unsigned I = 0; I != Path.size(); ++I) {1458 auto Elem = Path[I];1459 1460 switch (Elem.Kind) {1461 case IndirectLocalPathEntry::AddressOf:1462 case IndirectLocalPathEntry::LValToRVal:1463 case IndirectLocalPathEntry::ParenAggInit:1464 // These exist primarily to mark the path as not permitting or1465 // supporting lifetime extension.1466 break;1467 1468 case IndirectLocalPathEntry::LifetimeBoundCall:1469 case IndirectLocalPathEntry::TemporaryCopy:1470 case IndirectLocalPathEntry::MemberExpr:1471 case IndirectLocalPathEntry::GslPointerInit:1472 case IndirectLocalPathEntry::GslReferenceInit:1473 case IndirectLocalPathEntry::GslPointerAssignment:1474 // FIXME: Consider adding a note for these.1475 break;1476 1477 case IndirectLocalPathEntry::DefaultInit: {1478 auto *FD = cast<FieldDecl>(Elem.D);1479 SemaRef.Diag(FD->getLocation(),1480 diag::note_init_with_default_member_initializer)1481 << FD << nextPathEntryRange(Path, I + 1, L);1482 break;1483 }1484 1485 case IndirectLocalPathEntry::VarInit: {1486 const VarDecl *VD = cast<VarDecl>(Elem.D);1487 SemaRef.Diag(VD->getLocation(), diag::note_local_var_initializer)1488 << VD->getType()->isReferenceType() << VD->isImplicit()1489 << VD->getDeclName() << nextPathEntryRange(Path, I + 1, L);1490 break;1491 }1492 1493 case IndirectLocalPathEntry::LambdaCaptureInit: {1494 if (!Elem.Capture->capturesVariable())1495 break;1496 // FIXME: We can't easily tell apart an init-capture from a nested1497 // capture of an init-capture.1498 const ValueDecl *VD = Elem.Capture->getCapturedVar();1499 SemaRef.Diag(Elem.Capture->getLocation(),1500 diag::note_lambda_capture_initializer)1501 << VD << VD->isInitCapture() << Elem.Capture->isExplicit()1502 << (Elem.Capture->getCaptureKind() == LCK_ByRef) << VD1503 << nextPathEntryRange(Path, I + 1, L);1504 break;1505 }1506 1507 case IndirectLocalPathEntry::DefaultArg: {1508 const auto *DAE = cast<CXXDefaultArgExpr>(Elem.E);1509 const ParmVarDecl *Param = DAE->getParam();1510 SemaRef.Diag(Param->getDefaultArgRange().getBegin(),1511 diag::note_init_with_default_argument)1512 << Param << nextPathEntryRange(Path, I + 1, L);1513 break;1514 }1515 }1516 }1517 1518 // We didn't lifetime-extend, so don't go any further; we don't need more1519 // warnings or errors on inner temporaries within this one's initializer.1520 return false;1521 };1522 1523 llvm::SmallVector<IndirectLocalPathEntry, 8> Path;1524 switch (LK) {1525 case LK_Assignment: {1526 if (shouldRunGSLAssignmentAnalysis(SemaRef, *AEntity))1527 Path.push_back({lifetimes::isAssignmentOperatorLifetimeBound(1528 AEntity->AssignmentOperator)1529 ? IndirectLocalPathEntry::LifetimeBoundCall1530 : IndirectLocalPathEntry::GslPointerAssignment,1531 Init});1532 break;1533 }1534 case LK_LifetimeCapture: {1535 if (isPointerLikeType(Init->getType()))1536 Path.push_back({IndirectLocalPathEntry::GslPointerInit, Init});1537 break;1538 }1539 default:1540 break;1541 }1542 1543 if (Init->isGLValue())1544 visitLocalsRetainedByReferenceBinding(Path, Init, RK_ReferenceBinding,1545 TemporaryVisitor);1546 else1547 visitLocalsRetainedByInitializer(1548 Path, Init, TemporaryVisitor,1549 // Don't revisit the sub inits for the intialization case.1550 /*RevisitSubinits=*/!InitEntity);1551}1552 1553void checkInitLifetime(Sema &SemaRef, const InitializedEntity &Entity,1554 Expr *Init) {1555 auto LTResult = getEntityLifetime(&Entity);1556 LifetimeKind LK = LTResult.getInt();1557 const InitializedEntity *ExtendingEntity = LTResult.getPointer();1558 checkExprLifetimeImpl(SemaRef, &Entity, ExtendingEntity, LK,1559 /*AEntity=*/nullptr, /*CapEntity=*/nullptr, Init);1560}1561 1562void checkExprLifetimeMustTailArg(Sema &SemaRef,1563 const InitializedEntity &Entity, Expr *Init) {1564 checkExprLifetimeImpl(SemaRef, &Entity, nullptr, LK_MustTail,1565 /*AEntity=*/nullptr, /*CapEntity=*/nullptr, Init);1566}1567 1568void checkAssignmentLifetime(Sema &SemaRef, const AssignedEntity &Entity,1569 Expr *Init) {1570 bool EnableDanglingPointerAssignment = !SemaRef.getDiagnostics().isIgnored(1571 diag::warn_dangling_pointer_assignment, SourceLocation());1572 bool RunAnalysis = (EnableDanglingPointerAssignment &&1573 Entity.LHS->getType()->isPointerType()) ||1574 shouldRunGSLAssignmentAnalysis(SemaRef, Entity);1575 1576 if (!RunAnalysis)1577 return;1578 1579 checkExprLifetimeImpl(SemaRef, /*InitEntity=*/nullptr,1580 /*ExtendingEntity=*/nullptr, LK_Assignment, &Entity,1581 /*CapEntity=*/nullptr, Init);1582}1583 1584void checkCaptureByLifetime(Sema &SemaRef, const CapturingEntity &Entity,1585 Expr *Init) {1586 if (SemaRef.getDiagnostics().isIgnored(diag::warn_dangling_reference_captured,1587 SourceLocation()) &&1588 SemaRef.getDiagnostics().isIgnored(1589 diag::warn_dangling_reference_captured_by_unknown, SourceLocation()))1590 return;1591 return checkExprLifetimeImpl(SemaRef, /*InitEntity=*/nullptr,1592 /*ExtendingEntity=*/nullptr, LK_LifetimeCapture,1593 /*AEntity=*/nullptr,1594 /*CapEntity=*/&Entity, Init);1595}1596 1597} // namespace clang::sema1598