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-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "CheckExprLifetime.h" |
10 | #include "clang/AST/Decl.h" |
11 | #include "clang/AST/Expr.h" |
12 | #include "clang/Basic/DiagnosticSema.h" |
13 | #include "clang/Sema/Initialization.h" |
14 | #include "clang/Sema/Sema.h" |
15 | #include "llvm/ADT/PointerIntPair.h" |
16 | |
17 | namespace clang::sema { |
18 | namespace { |
19 | enum LifetimeKind { |
20 | /// The lifetime of a temporary bound to this entity ends at the end of the |
21 | /// full-expression, and that's (probably) fine. |
22 | LK_FullExpression, |
23 | |
24 | /// The lifetime of a temporary bound to this entity is extended to the |
25 | /// lifeitme of the entity itself. |
26 | LK_Extended, |
27 | |
28 | /// The lifetime of a temporary bound to this entity probably ends too soon, |
29 | /// because the entity is allocated in a new-expression. |
30 | LK_New, |
31 | |
32 | /// The lifetime of a temporary bound to this entity ends too soon, because |
33 | /// the entity is a return object. |
34 | LK_Return, |
35 | |
36 | /// The lifetime of a temporary bound to this entity ends too soon, because |
37 | /// the entity is the result of a statement expression. |
38 | LK_StmtExprResult, |
39 | |
40 | /// This is a mem-initializer: if it would extend a temporary (other than via |
41 | /// a default member initializer), the program is ill-formed. |
42 | LK_MemInitializer, |
43 | |
44 | /// The lifetime of a temporary bound to this entity probably ends too soon, |
45 | /// because the entity is a pointer and we assign the address of a temporary |
46 | /// object to it. |
47 | LK_Assignment, |
48 | }; |
49 | using LifetimeResult = |
50 | llvm::PointerIntPair<const InitializedEntity *, 3, LifetimeKind>; |
51 | } // namespace |
52 | |
53 | /// Determine the declaration which an initialized entity ultimately refers to, |
54 | /// for the purpose of lifetime-extending a temporary bound to a reference in |
55 | /// the initialization of \p Entity. |
56 | static LifetimeResult |
57 | getEntityLifetime(const InitializedEntity *Entity, |
58 | const InitializedEntity *InitField = nullptr) { |
59 | // C++11 [class.temporary]p5: |
60 | switch (Entity->getKind()) { |
61 | case InitializedEntity::EK_Variable: |
62 | // The temporary [...] persists for the lifetime of the reference |
63 | return {Entity, LK_Extended}; |
64 | |
65 | case InitializedEntity::EK_Member: |
66 | // For subobjects, we look at the complete object. |
67 | if (Entity->getParent()) |
68 | return getEntityLifetime(Entity: Entity->getParent(), InitField: Entity); |
69 | |
70 | // except: |
71 | // C++17 [class.base.init]p8: |
72 | // A temporary expression bound to a reference member in a |
73 | // mem-initializer is ill-formed. |
74 | // C++17 [class.base.init]p11: |
75 | // A temporary expression bound to a reference member from a |
76 | // default member initializer is ill-formed. |
77 | // |
78 | // The context of p11 and its example suggest that it's only the use of a |
79 | // default member initializer from a constructor that makes the program |
80 | // ill-formed, not its mere existence, and that it can even be used by |
81 | // aggregate initialization. |
82 | return {Entity, Entity->isDefaultMemberInitializer() ? LK_Extended |
83 | : LK_MemInitializer}; |
84 | |
85 | case InitializedEntity::EK_Binding: |
86 | // Per [dcl.decomp]p3, the binding is treated as a variable of reference |
87 | // type. |
88 | return {Entity, LK_Extended}; |
89 | |
90 | case InitializedEntity::EK_Parameter: |
91 | case InitializedEntity::EK_Parameter_CF_Audited: |
92 | // -- A temporary bound to a reference parameter in a function call |
93 | // persists until the completion of the full-expression containing |
94 | // the call. |
95 | return {nullptr, LK_FullExpression}; |
96 | |
97 | case InitializedEntity::EK_TemplateParameter: |
98 | // FIXME: This will always be ill-formed; should we eagerly diagnose it |
99 | // here? |
100 | return {nullptr, LK_FullExpression}; |
101 | |
102 | case InitializedEntity::EK_Result: |
103 | // -- The lifetime of a temporary bound to the returned value in a |
104 | // function return statement is not extended; the temporary is |
105 | // destroyed at the end of the full-expression in the return statement. |
106 | return {nullptr, LK_Return}; |
107 | |
108 | case InitializedEntity::EK_StmtExprResult: |
109 | // FIXME: Should we lifetime-extend through the result of a statement |
110 | // expression? |
111 | return {nullptr, LK_StmtExprResult}; |
112 | |
113 | case InitializedEntity::EK_New: |
114 | // -- A temporary bound to a reference in a new-initializer persists |
115 | // until the completion of the full-expression containing the |
116 | // new-initializer. |
117 | return {nullptr, LK_New}; |
118 | |
119 | case InitializedEntity::EK_Temporary: |
120 | case InitializedEntity::EK_CompoundLiteralInit: |
121 | case InitializedEntity::EK_RelatedResult: |
122 | // We don't yet know the storage duration of the surrounding temporary. |
123 | // Assume it's got full-expression duration for now, it will patch up our |
124 | // storage duration if that's not correct. |
125 | return {nullptr, LK_FullExpression}; |
126 | |
127 | case InitializedEntity::EK_ArrayElement: |
128 | // For subobjects, we look at the complete object. |
129 | return getEntityLifetime(Entity: Entity->getParent(), InitField); |
130 | |
131 | case InitializedEntity::EK_Base: |
132 | // For subobjects, we look at the complete object. |
133 | if (Entity->getParent()) |
134 | return getEntityLifetime(Entity: Entity->getParent(), InitField); |
135 | return {InitField, LK_MemInitializer}; |
136 | |
137 | case InitializedEntity::EK_Delegating: |
138 | // We can reach this case for aggregate initialization in a constructor: |
139 | // struct A { int &&r; }; |
140 | // struct B : A { B() : A{0} {} }; |
141 | // In this case, use the outermost field decl as the context. |
142 | return {InitField, LK_MemInitializer}; |
143 | |
144 | case InitializedEntity::EK_BlockElement: |
145 | case InitializedEntity::EK_LambdaToBlockConversionBlockElement: |
146 | case InitializedEntity::EK_LambdaCapture: |
147 | case InitializedEntity::EK_VectorElement: |
148 | case InitializedEntity::EK_ComplexElement: |
149 | return {nullptr, LK_FullExpression}; |
150 | |
151 | case InitializedEntity::EK_Exception: |
152 | // FIXME: Can we diagnose lifetime problems with exceptions? |
153 | return {nullptr, LK_FullExpression}; |
154 | |
155 | case InitializedEntity::EK_ParenAggInitMember: |
156 | // -- A temporary object bound to a reference element of an aggregate of |
157 | // class type initialized from a parenthesized expression-list |
158 | // [dcl.init, 9.3] persists until the completion of the full-expression |
159 | // containing the expression-list. |
160 | return {nullptr, LK_FullExpression}; |
161 | } |
162 | |
163 | llvm_unreachable("unknown entity kind" ); |
164 | } |
165 | |
166 | namespace { |
167 | enum ReferenceKind { |
168 | /// Lifetime would be extended by a reference binding to a temporary. |
169 | RK_ReferenceBinding, |
170 | /// Lifetime would be extended by a std::initializer_list object binding to |
171 | /// its backing array. |
172 | RK_StdInitializerList, |
173 | }; |
174 | |
175 | /// A temporary or local variable. This will be one of: |
176 | /// * A MaterializeTemporaryExpr. |
177 | /// * A DeclRefExpr whose declaration is a local. |
178 | /// * An AddrLabelExpr. |
179 | /// * A BlockExpr for a block with captures. |
180 | using Local = Expr *; |
181 | |
182 | /// Expressions we stepped over when looking for the local state. Any steps |
183 | /// that would inhibit lifetime extension or take us out of subexpressions of |
184 | /// the initializer are included. |
185 | struct IndirectLocalPathEntry { |
186 | enum EntryKind { |
187 | DefaultInit, |
188 | AddressOf, |
189 | VarInit, |
190 | LValToRVal, |
191 | LifetimeBoundCall, |
192 | TemporaryCopy, |
193 | LambdaCaptureInit, |
194 | GslReferenceInit, |
195 | GslPointerInit, |
196 | GslPointerAssignment, |
197 | } Kind; |
198 | Expr *E; |
199 | union { |
200 | const Decl *D = nullptr; |
201 | const LambdaCapture *Capture; |
202 | }; |
203 | IndirectLocalPathEntry() {} |
204 | IndirectLocalPathEntry(EntryKind K, Expr *E) : Kind(K), E(E) {} |
205 | IndirectLocalPathEntry(EntryKind K, Expr *E, const Decl *D) |
206 | : Kind(K), E(E), D(D) {} |
207 | IndirectLocalPathEntry(EntryKind K, Expr *E, const LambdaCapture *Capture) |
208 | : Kind(K), E(E), Capture(Capture) {} |
209 | }; |
210 | |
211 | using IndirectLocalPath = llvm::SmallVectorImpl<IndirectLocalPathEntry>; |
212 | |
213 | struct RevertToOldSizeRAII { |
214 | IndirectLocalPath &Path; |
215 | unsigned OldSize = Path.size(); |
216 | RevertToOldSizeRAII(IndirectLocalPath &Path) : Path(Path) {} |
217 | ~RevertToOldSizeRAII() { Path.resize(N: OldSize); } |
218 | }; |
219 | |
220 | using LocalVisitor = llvm::function_ref<bool(IndirectLocalPath &Path, Local L, |
221 | ReferenceKind RK)>; |
222 | } // namespace |
223 | |
224 | static bool isVarOnPath(IndirectLocalPath &Path, VarDecl *VD) { |
225 | for (auto E : Path) |
226 | if (E.Kind == IndirectLocalPathEntry::VarInit && E.D == VD) |
227 | return true; |
228 | return false; |
229 | } |
230 | |
231 | static bool pathContainsInit(IndirectLocalPath &Path) { |
232 | return llvm::any_of(Range&: Path, P: [=](IndirectLocalPathEntry E) { |
233 | return E.Kind == IndirectLocalPathEntry::DefaultInit || |
234 | E.Kind == IndirectLocalPathEntry::VarInit; |
235 | }); |
236 | } |
237 | |
238 | static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, |
239 | Expr *Init, LocalVisitor Visit, |
240 | bool RevisitSubinits, |
241 | bool EnableLifetimeWarnings); |
242 | |
243 | static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, |
244 | Expr *Init, ReferenceKind RK, |
245 | LocalVisitor Visit, |
246 | bool EnableLifetimeWarnings); |
247 | |
248 | template <typename T> static bool isRecordWithAttr(QualType Type) { |
249 | if (auto *RD = Type->getAsCXXRecordDecl()) |
250 | return RD->hasAttr<T>(); |
251 | return false; |
252 | } |
253 | |
254 | // Decl::isInStdNamespace will return false for iterators in some STL |
255 | // implementations due to them being defined in a namespace outside of the std |
256 | // namespace. |
257 | static bool isInStlNamespace(const Decl *D) { |
258 | const DeclContext *DC = D->getDeclContext(); |
259 | if (!DC) |
260 | return false; |
261 | if (const auto *ND = dyn_cast<NamespaceDecl>(Val: DC)) |
262 | if (const IdentifierInfo *II = ND->getIdentifier()) { |
263 | StringRef Name = II->getName(); |
264 | if (Name.size() >= 2 && Name.front() == '_' && |
265 | (Name[1] == '_' || isUppercase(c: Name[1]))) |
266 | return true; |
267 | } |
268 | |
269 | return DC->isStdNamespace(); |
270 | } |
271 | |
272 | static bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee) { |
273 | if (auto *Conv = dyn_cast_or_null<CXXConversionDecl>(Val: Callee)) |
274 | if (isRecordWithAttr<PointerAttr>(Type: Conv->getConversionType())) |
275 | return true; |
276 | if (!isInStlNamespace(D: Callee->getParent())) |
277 | return false; |
278 | if (!isRecordWithAttr<PointerAttr>( |
279 | Type: Callee->getFunctionObjectParameterType()) && |
280 | !isRecordWithAttr<OwnerAttr>(Type: Callee->getFunctionObjectParameterType())) |
281 | return false; |
282 | if (Callee->getReturnType()->isPointerType() || |
283 | isRecordWithAttr<PointerAttr>(Type: Callee->getReturnType())) { |
284 | if (!Callee->getIdentifier()) |
285 | return false; |
286 | return llvm::StringSwitch<bool>(Callee->getName()) |
287 | .Cases(S0: "begin" , S1: "rbegin" , S2: "cbegin" , S3: "crbegin" , Value: true) |
288 | .Cases(S0: "end" , S1: "rend" , S2: "cend" , S3: "crend" , Value: true) |
289 | .Cases(S0: "c_str" , S1: "data" , S2: "get" , Value: true) |
290 | // Map and set types. |
291 | .Cases(S0: "find" , S1: "equal_range" , S2: "lower_bound" , S3: "upper_bound" , Value: true) |
292 | .Default(Value: false); |
293 | } else if (Callee->getReturnType()->isReferenceType()) { |
294 | if (!Callee->getIdentifier()) { |
295 | auto OO = Callee->getOverloadedOperator(); |
296 | return OO == OverloadedOperatorKind::OO_Subscript || |
297 | OO == OverloadedOperatorKind::OO_Star; |
298 | } |
299 | return llvm::StringSwitch<bool>(Callee->getName()) |
300 | .Cases(S0: "front" , S1: "back" , S2: "at" , S3: "top" , S4: "value" , Value: true) |
301 | .Default(Value: false); |
302 | } |
303 | return false; |
304 | } |
305 | |
306 | static bool shouldTrackFirstArgument(const FunctionDecl *FD) { |
307 | if (!FD->getIdentifier() || FD->getNumParams() != 1) |
308 | return false; |
309 | const auto *RD = FD->getParamDecl(i: 0)->getType()->getPointeeCXXRecordDecl(); |
310 | if (!FD->isInStdNamespace() || !RD || !RD->isInStdNamespace()) |
311 | return false; |
312 | if (!RD->hasAttr<PointerAttr>() && !RD->hasAttr<OwnerAttr>()) |
313 | return false; |
314 | if (FD->getReturnType()->isPointerType() || |
315 | isRecordWithAttr<PointerAttr>(Type: FD->getReturnType())) { |
316 | return llvm::StringSwitch<bool>(FD->getName()) |
317 | .Cases(S0: "begin" , S1: "rbegin" , S2: "cbegin" , S3: "crbegin" , Value: true) |
318 | .Cases(S0: "end" , S1: "rend" , S2: "cend" , S3: "crend" , Value: true) |
319 | .Case(S: "data" , Value: true) |
320 | .Default(Value: false); |
321 | } else if (FD->getReturnType()->isReferenceType()) { |
322 | return llvm::StringSwitch<bool>(FD->getName()) |
323 | .Cases(S0: "get" , S1: "any_cast" , Value: true) |
324 | .Default(Value: false); |
325 | } |
326 | return false; |
327 | } |
328 | |
329 | static void handleGslAnnotatedTypes(IndirectLocalPath &Path, Expr *Call, |
330 | LocalVisitor Visit) { |
331 | auto VisitPointerArg = [&](const Decl *D, Expr *Arg, bool Value) { |
332 | // We are not interested in the temporary base objects of gsl Pointers: |
333 | // Temp().ptr; // Here ptr might not dangle. |
334 | if (isa<MemberExpr>(Val: Arg->IgnoreImpCasts())) |
335 | return; |
336 | // Once we initialized a value with a reference, it can no longer dangle. |
337 | if (!Value) { |
338 | for (const IndirectLocalPathEntry &PE : llvm::reverse(C&: Path)) { |
339 | if (PE.Kind == IndirectLocalPathEntry::GslReferenceInit) |
340 | continue; |
341 | if (PE.Kind == IndirectLocalPathEntry::GslPointerInit || |
342 | PE.Kind == IndirectLocalPathEntry::GslPointerAssignment) |
343 | return; |
344 | break; |
345 | } |
346 | } |
347 | Path.push_back(Elt: {Value ? IndirectLocalPathEntry::GslPointerInit |
348 | : IndirectLocalPathEntry::GslReferenceInit, |
349 | Arg, D}); |
350 | if (Arg->isGLValue()) |
351 | visitLocalsRetainedByReferenceBinding(Path, Init: Arg, RK: RK_ReferenceBinding, |
352 | Visit, |
353 | /*EnableLifetimeWarnings=*/true); |
354 | else |
355 | visitLocalsRetainedByInitializer(Path, Init: Arg, Visit, RevisitSubinits: true, |
356 | /*EnableLifetimeWarnings=*/true); |
357 | Path.pop_back(); |
358 | }; |
359 | |
360 | if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Val: Call)) { |
361 | const auto *MD = cast_or_null<CXXMethodDecl>(Val: MCE->getDirectCallee()); |
362 | if (MD && shouldTrackImplicitObjectArg(Callee: MD)) |
363 | VisitPointerArg(MD, MCE->getImplicitObjectArgument(), |
364 | !MD->getReturnType()->isReferenceType()); |
365 | return; |
366 | } else if (auto *OCE = dyn_cast<CXXOperatorCallExpr>(Val: Call)) { |
367 | FunctionDecl *Callee = OCE->getDirectCallee(); |
368 | if (Callee && Callee->isCXXInstanceMember() && |
369 | shouldTrackImplicitObjectArg(Callee: cast<CXXMethodDecl>(Val: Callee))) |
370 | VisitPointerArg(Callee, OCE->getArg(Arg: 0), |
371 | !Callee->getReturnType()->isReferenceType()); |
372 | return; |
373 | } else if (auto *CE = dyn_cast<CallExpr>(Val: Call)) { |
374 | FunctionDecl *Callee = CE->getDirectCallee(); |
375 | if (Callee && shouldTrackFirstArgument(FD: Callee)) |
376 | VisitPointerArg(Callee, CE->getArg(Arg: 0), |
377 | !Callee->getReturnType()->isReferenceType()); |
378 | return; |
379 | } |
380 | |
381 | if (auto *CCE = dyn_cast<CXXConstructExpr>(Val: Call)) { |
382 | const auto *Ctor = CCE->getConstructor(); |
383 | const CXXRecordDecl *RD = Ctor->getParent(); |
384 | if (CCE->getNumArgs() > 0 && RD->hasAttr<PointerAttr>()) |
385 | VisitPointerArg(Ctor->getParamDecl(i: 0), CCE->getArgs()[0], true); |
386 | } |
387 | } |
388 | |
389 | static bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) { |
390 | const TypeSourceInfo *TSI = FD->getTypeSourceInfo(); |
391 | if (!TSI) |
392 | return false; |
393 | // Don't declare this variable in the second operand of the for-statement; |
394 | // GCC miscompiles that by ending its lifetime before evaluating the |
395 | // third operand. See gcc.gnu.org/PR86769. |
396 | AttributedTypeLoc ATL; |
397 | for (TypeLoc TL = TSI->getTypeLoc(); |
398 | (ATL = TL.getAsAdjusted<AttributedTypeLoc>()); |
399 | TL = ATL.getModifiedLoc()) { |
400 | if (ATL.getAttrAs<LifetimeBoundAttr>()) |
401 | return true; |
402 | } |
403 | |
404 | // Assume that all assignment operators with a "normal" return type return |
405 | // *this, that is, an lvalue reference that is the same type as the implicit |
406 | // object parameter (or the LHS for a non-member operator$=). |
407 | OverloadedOperatorKind OO = FD->getDeclName().getCXXOverloadedOperator(); |
408 | if (OO == OO_Equal || isCompoundAssignmentOperator(Kind: OO)) { |
409 | QualType RetT = FD->getReturnType(); |
410 | if (RetT->isLValueReferenceType()) { |
411 | ASTContext &Ctx = FD->getASTContext(); |
412 | QualType LHST; |
413 | auto *MD = dyn_cast<CXXMethodDecl>(Val: FD); |
414 | if (MD && MD->isCXXInstanceMember()) |
415 | LHST = Ctx.getLValueReferenceType(T: MD->getFunctionObjectParameterType()); |
416 | else |
417 | LHST = MD->getParamDecl(i: 0)->getType(); |
418 | if (Ctx.hasSameType(T1: RetT, T2: LHST)) |
419 | return true; |
420 | } |
421 | } |
422 | |
423 | return false; |
424 | } |
425 | |
426 | static void visitLifetimeBoundArguments(IndirectLocalPath &Path, Expr *Call, |
427 | LocalVisitor Visit) { |
428 | const FunctionDecl *Callee; |
429 | ArrayRef<Expr *> Args; |
430 | |
431 | if (auto *CE = dyn_cast<CallExpr>(Val: Call)) { |
432 | Callee = CE->getDirectCallee(); |
433 | Args = llvm::ArrayRef(CE->getArgs(), CE->getNumArgs()); |
434 | } else { |
435 | auto *CCE = cast<CXXConstructExpr>(Val: Call); |
436 | Callee = CCE->getConstructor(); |
437 | Args = llvm::ArrayRef(CCE->getArgs(), CCE->getNumArgs()); |
438 | } |
439 | if (!Callee) |
440 | return; |
441 | |
442 | Expr *ObjectArg = nullptr; |
443 | if (isa<CXXOperatorCallExpr>(Val: Call) && Callee->isCXXInstanceMember()) { |
444 | ObjectArg = Args[0]; |
445 | Args = Args.slice(N: 1); |
446 | } else if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Val: Call)) { |
447 | ObjectArg = MCE->getImplicitObjectArgument(); |
448 | } |
449 | |
450 | auto VisitLifetimeBoundArg = [&](const Decl *D, Expr *Arg) { |
451 | Path.push_back(Elt: {IndirectLocalPathEntry::LifetimeBoundCall, Arg, D}); |
452 | if (Arg->isGLValue()) |
453 | visitLocalsRetainedByReferenceBinding(Path, Init: Arg, RK: RK_ReferenceBinding, |
454 | Visit, |
455 | /*EnableLifetimeWarnings=*/false); |
456 | else |
457 | visitLocalsRetainedByInitializer(Path, Init: Arg, Visit, RevisitSubinits: true, |
458 | /*EnableLifetimeWarnings=*/false); |
459 | Path.pop_back(); |
460 | }; |
461 | |
462 | bool CheckCoroCall = false; |
463 | if (const auto *RD = Callee->getReturnType()->getAsRecordDecl()) { |
464 | CheckCoroCall = RD->hasAttr<CoroLifetimeBoundAttr>() && |
465 | RD->hasAttr<CoroReturnTypeAttr>() && |
466 | !Callee->hasAttr<CoroDisableLifetimeBoundAttr>(); |
467 | } |
468 | |
469 | if (ObjectArg) { |
470 | bool CheckCoroObjArg = CheckCoroCall; |
471 | // Coroutine lambda objects with empty capture list are not lifetimebound. |
472 | if (auto *LE = dyn_cast<LambdaExpr>(Val: ObjectArg->IgnoreImplicit()); |
473 | LE && LE->captures().empty()) |
474 | CheckCoroObjArg = false; |
475 | // Allow `get_return_object()` as the object param (__promise) is not |
476 | // lifetimebound. |
477 | if (Sema::CanBeGetReturnObject(FD: Callee)) |
478 | CheckCoroObjArg = false; |
479 | if (implicitObjectParamIsLifetimeBound(FD: Callee) || CheckCoroObjArg) |
480 | VisitLifetimeBoundArg(Callee, ObjectArg); |
481 | } |
482 | |
483 | for (unsigned I = 0, |
484 | N = std::min<unsigned>(a: Callee->getNumParams(), b: Args.size()); |
485 | I != N; ++I) { |
486 | if (CheckCoroCall || Callee->getParamDecl(i: I)->hasAttr<LifetimeBoundAttr>()) |
487 | VisitLifetimeBoundArg(Callee->getParamDecl(i: I), Args[I]); |
488 | } |
489 | } |
490 | |
491 | /// Visit the locals that would be reachable through a reference bound to the |
492 | /// glvalue expression \c Init. |
493 | static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, |
494 | Expr *Init, ReferenceKind RK, |
495 | LocalVisitor Visit, |
496 | bool EnableLifetimeWarnings) { |
497 | RevertToOldSizeRAII RAII(Path); |
498 | |
499 | // Walk past any constructs which we can lifetime-extend across. |
500 | Expr *Old; |
501 | do { |
502 | Old = Init; |
503 | |
504 | if (auto *FE = dyn_cast<FullExpr>(Val: Init)) |
505 | Init = FE->getSubExpr(); |
506 | |
507 | if (InitListExpr *ILE = dyn_cast<InitListExpr>(Val: Init)) { |
508 | // If this is just redundant braces around an initializer, step over it. |
509 | if (ILE->isTransparent()) |
510 | Init = ILE->getInit(Init: 0); |
511 | } |
512 | |
513 | // Step over any subobject adjustments; we may have a materialized |
514 | // temporary inside them. |
515 | Init = const_cast<Expr *>(Init->skipRValueSubobjectAdjustments()); |
516 | |
517 | // Per current approach for DR1376, look through casts to reference type |
518 | // when performing lifetime extension. |
519 | if (CastExpr *CE = dyn_cast<CastExpr>(Val: Init)) |
520 | if (CE->getSubExpr()->isGLValue()) |
521 | Init = CE->getSubExpr(); |
522 | |
523 | // Per the current approach for DR1299, look through array element access |
524 | // on array glvalues when performing lifetime extension. |
525 | if (auto *ASE = dyn_cast<ArraySubscriptExpr>(Val: Init)) { |
526 | Init = ASE->getBase(); |
527 | auto *ICE = dyn_cast<ImplicitCastExpr>(Val: Init); |
528 | if (ICE && ICE->getCastKind() == CK_ArrayToPointerDecay) |
529 | Init = ICE->getSubExpr(); |
530 | else |
531 | // We can't lifetime extend through this but we might still find some |
532 | // retained temporaries. |
533 | return visitLocalsRetainedByInitializer(Path, Init, Visit, RevisitSubinits: true, |
534 | EnableLifetimeWarnings); |
535 | } |
536 | |
537 | // Step into CXXDefaultInitExprs so we can diagnose cases where a |
538 | // constructor inherits one as an implicit mem-initializer. |
539 | if (auto *DIE = dyn_cast<CXXDefaultInitExpr>(Val: Init)) { |
540 | Path.push_back( |
541 | Elt: {IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()}); |
542 | Init = DIE->getExpr(); |
543 | } |
544 | } while (Init != Old); |
545 | |
546 | if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Val: Init)) { |
547 | if (Visit(Path, Local(MTE), RK)) |
548 | visitLocalsRetainedByInitializer(Path, Init: MTE->getSubExpr(), Visit, RevisitSubinits: true, |
549 | EnableLifetimeWarnings); |
550 | } |
551 | |
552 | if (auto *M = dyn_cast<MemberExpr>(Val: Init)) { |
553 | // Lifetime of a non-reference type field is same as base object. |
554 | if (auto *F = dyn_cast<FieldDecl>(Val: M->getMemberDecl()); |
555 | F && !F->getType()->isReferenceType()) |
556 | visitLocalsRetainedByInitializer(Path, Init: M->getBase(), Visit, RevisitSubinits: true, |
557 | EnableLifetimeWarnings); |
558 | } |
559 | |
560 | if (isa<CallExpr>(Val: Init)) { |
561 | if (EnableLifetimeWarnings) |
562 | handleGslAnnotatedTypes(Path, Call: Init, Visit); |
563 | return visitLifetimeBoundArguments(Path, Call: Init, Visit); |
564 | } |
565 | |
566 | switch (Init->getStmtClass()) { |
567 | case Stmt::DeclRefExprClass: { |
568 | // If we find the name of a local non-reference parameter, we could have a |
569 | // lifetime problem. |
570 | auto *DRE = cast<DeclRefExpr>(Val: Init); |
571 | auto *VD = dyn_cast<VarDecl>(Val: DRE->getDecl()); |
572 | if (VD && VD->hasLocalStorage() && |
573 | !DRE->refersToEnclosingVariableOrCapture()) { |
574 | if (!VD->getType()->isReferenceType()) { |
575 | Visit(Path, Local(DRE), RK); |
576 | } else if (isa<ParmVarDecl>(Val: DRE->getDecl())) { |
577 | // The lifetime of a reference parameter is unknown; assume it's OK |
578 | // for now. |
579 | break; |
580 | } else if (VD->getInit() && !isVarOnPath(Path, VD)) { |
581 | Path.push_back(Elt: {IndirectLocalPathEntry::VarInit, DRE, VD}); |
582 | visitLocalsRetainedByReferenceBinding(Path, Init: VD->getInit(), |
583 | RK: RK_ReferenceBinding, Visit, |
584 | EnableLifetimeWarnings); |
585 | } |
586 | } |
587 | break; |
588 | } |
589 | |
590 | case Stmt::UnaryOperatorClass: { |
591 | // The only unary operator that make sense to handle here |
592 | // is Deref. All others don't resolve to a "name." This includes |
593 | // handling all sorts of rvalues passed to a unary operator. |
594 | const UnaryOperator *U = cast<UnaryOperator>(Val: Init); |
595 | if (U->getOpcode() == UO_Deref) |
596 | visitLocalsRetainedByInitializer(Path, Init: U->getSubExpr(), Visit, RevisitSubinits: true, |
597 | EnableLifetimeWarnings); |
598 | break; |
599 | } |
600 | |
601 | case Stmt::ArraySectionExprClass: { |
602 | visitLocalsRetainedByInitializer(Path, |
603 | Init: cast<ArraySectionExpr>(Val: Init)->getBase(), |
604 | Visit, RevisitSubinits: true, EnableLifetimeWarnings); |
605 | break; |
606 | } |
607 | |
608 | case Stmt::ConditionalOperatorClass: |
609 | case Stmt::BinaryConditionalOperatorClass: { |
610 | auto *C = cast<AbstractConditionalOperator>(Val: Init); |
611 | if (!C->getTrueExpr()->getType()->isVoidType()) |
612 | visitLocalsRetainedByReferenceBinding(Path, Init: C->getTrueExpr(), RK, Visit, |
613 | EnableLifetimeWarnings); |
614 | if (!C->getFalseExpr()->getType()->isVoidType()) |
615 | visitLocalsRetainedByReferenceBinding(Path, Init: C->getFalseExpr(), RK, Visit, |
616 | EnableLifetimeWarnings); |
617 | break; |
618 | } |
619 | |
620 | case Stmt::CompoundLiteralExprClass: { |
621 | if (auto *CLE = dyn_cast<CompoundLiteralExpr>(Val: Init)) { |
622 | if (!CLE->isFileScope()) |
623 | Visit(Path, Local(CLE), RK); |
624 | } |
625 | break; |
626 | } |
627 | |
628 | // FIXME: Visit the left-hand side of an -> or ->*. |
629 | |
630 | default: |
631 | break; |
632 | } |
633 | } |
634 | |
635 | /// Visit the locals that would be reachable through an object initialized by |
636 | /// the prvalue expression \c Init. |
637 | static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path, |
638 | Expr *Init, LocalVisitor Visit, |
639 | bool RevisitSubinits, |
640 | bool EnableLifetimeWarnings) { |
641 | RevertToOldSizeRAII RAII(Path); |
642 | |
643 | Expr *Old; |
644 | do { |
645 | Old = Init; |
646 | |
647 | // Step into CXXDefaultInitExprs so we can diagnose cases where a |
648 | // constructor inherits one as an implicit mem-initializer. |
649 | if (auto *DIE = dyn_cast<CXXDefaultInitExpr>(Val: Init)) { |
650 | Path.push_back( |
651 | Elt: {IndirectLocalPathEntry::DefaultInit, DIE, DIE->getField()}); |
652 | Init = DIE->getExpr(); |
653 | } |
654 | |
655 | if (auto *FE = dyn_cast<FullExpr>(Val: Init)) |
656 | Init = FE->getSubExpr(); |
657 | |
658 | // Dig out the expression which constructs the extended temporary. |
659 | Init = const_cast<Expr *>(Init->skipRValueSubobjectAdjustments()); |
660 | |
661 | if (CXXBindTemporaryExpr *BTE = dyn_cast<CXXBindTemporaryExpr>(Val: Init)) |
662 | Init = BTE->getSubExpr(); |
663 | |
664 | Init = Init->IgnoreParens(); |
665 | |
666 | // Step over value-preserving rvalue casts. |
667 | if (auto *CE = dyn_cast<CastExpr>(Val: Init)) { |
668 | switch (CE->getCastKind()) { |
669 | case CK_LValueToRValue: |
670 | // If we can match the lvalue to a const object, we can look at its |
671 | // initializer. |
672 | Path.push_back(Elt: {IndirectLocalPathEntry::LValToRVal, CE}); |
673 | return visitLocalsRetainedByReferenceBinding( |
674 | Path, Init, RK: RK_ReferenceBinding, |
675 | Visit: [&](IndirectLocalPath &Path, Local L, ReferenceKind RK) -> bool { |
676 | if (auto *DRE = dyn_cast<DeclRefExpr>(Val: L)) { |
677 | auto *VD = dyn_cast<VarDecl>(Val: DRE->getDecl()); |
678 | if (VD && VD->getType().isConstQualified() && VD->getInit() && |
679 | !isVarOnPath(Path, VD)) { |
680 | Path.push_back(Elt: {IndirectLocalPathEntry::VarInit, DRE, VD}); |
681 | visitLocalsRetainedByInitializer( |
682 | Path, Init: VD->getInit(), Visit, RevisitSubinits: true, EnableLifetimeWarnings); |
683 | } |
684 | } else if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Val: L)) { |
685 | if (MTE->getType().isConstQualified()) |
686 | visitLocalsRetainedByInitializer(Path, Init: MTE->getSubExpr(), |
687 | Visit, RevisitSubinits: true, |
688 | EnableLifetimeWarnings); |
689 | } |
690 | return false; |
691 | }, |
692 | EnableLifetimeWarnings); |
693 | |
694 | // We assume that objects can be retained by pointers cast to integers, |
695 | // but not if the integer is cast to floating-point type or to _Complex. |
696 | // We assume that casts to 'bool' do not preserve enough information to |
697 | // retain a local object. |
698 | case CK_NoOp: |
699 | case CK_BitCast: |
700 | case CK_BaseToDerived: |
701 | case CK_DerivedToBase: |
702 | case CK_UncheckedDerivedToBase: |
703 | case CK_Dynamic: |
704 | case CK_ToUnion: |
705 | case CK_UserDefinedConversion: |
706 | case CK_ConstructorConversion: |
707 | case CK_IntegralToPointer: |
708 | case CK_PointerToIntegral: |
709 | case CK_VectorSplat: |
710 | case CK_IntegralCast: |
711 | case CK_CPointerToObjCPointerCast: |
712 | case CK_BlockPointerToObjCPointerCast: |
713 | case CK_AnyPointerToBlockPointerCast: |
714 | case CK_AddressSpaceConversion: |
715 | break; |
716 | |
717 | case CK_ArrayToPointerDecay: |
718 | // Model array-to-pointer decay as taking the address of the array |
719 | // lvalue. |
720 | Path.push_back(Elt: {IndirectLocalPathEntry::AddressOf, CE}); |
721 | return visitLocalsRetainedByReferenceBinding(Path, Init: CE->getSubExpr(), |
722 | RK: RK_ReferenceBinding, Visit, |
723 | EnableLifetimeWarnings); |
724 | |
725 | default: |
726 | return; |
727 | } |
728 | |
729 | Init = CE->getSubExpr(); |
730 | } |
731 | } while (Old != Init); |
732 | |
733 | // C++17 [dcl.init.list]p6: |
734 | // initializing an initializer_list object from the array extends the |
735 | // lifetime of the array exactly like binding a reference to a temporary. |
736 | if (auto *ILE = dyn_cast<CXXStdInitializerListExpr>(Val: Init)) |
737 | return visitLocalsRetainedByReferenceBinding(Path, Init: ILE->getSubExpr(), |
738 | RK: RK_StdInitializerList, Visit, |
739 | EnableLifetimeWarnings); |
740 | |
741 | if (InitListExpr *ILE = dyn_cast<InitListExpr>(Val: Init)) { |
742 | // We already visited the elements of this initializer list while |
743 | // performing the initialization. Don't visit them again unless we've |
744 | // changed the lifetime of the initialized entity. |
745 | if (!RevisitSubinits) |
746 | return; |
747 | |
748 | if (ILE->isTransparent()) |
749 | return visitLocalsRetainedByInitializer(Path, Init: ILE->getInit(Init: 0), Visit, |
750 | RevisitSubinits, |
751 | EnableLifetimeWarnings); |
752 | |
753 | if (ILE->getType()->isArrayType()) { |
754 | for (unsigned I = 0, N = ILE->getNumInits(); I != N; ++I) |
755 | visitLocalsRetainedByInitializer(Path, Init: ILE->getInit(Init: I), Visit, |
756 | RevisitSubinits, |
757 | EnableLifetimeWarnings); |
758 | return; |
759 | } |
760 | |
761 | if (CXXRecordDecl *RD = ILE->getType()->getAsCXXRecordDecl()) { |
762 | assert(RD->isAggregate() && "aggregate init on non-aggregate" ); |
763 | |
764 | // If we lifetime-extend a braced initializer which is initializing an |
765 | // aggregate, and that aggregate contains reference members which are |
766 | // bound to temporaries, those temporaries are also lifetime-extended. |
767 | if (RD->isUnion() && ILE->getInitializedFieldInUnion() && |
768 | ILE->getInitializedFieldInUnion()->getType()->isReferenceType()) |
769 | visitLocalsRetainedByReferenceBinding(Path, Init: ILE->getInit(Init: 0), |
770 | RK: RK_ReferenceBinding, Visit, |
771 | EnableLifetimeWarnings); |
772 | else { |
773 | unsigned Index = 0; |
774 | for (; Index < RD->getNumBases() && Index < ILE->getNumInits(); ++Index) |
775 | visitLocalsRetainedByInitializer(Path, Init: ILE->getInit(Init: Index), Visit, |
776 | RevisitSubinits, |
777 | EnableLifetimeWarnings); |
778 | for (const auto *I : RD->fields()) { |
779 | if (Index >= ILE->getNumInits()) |
780 | break; |
781 | if (I->isUnnamedBitField()) |
782 | continue; |
783 | Expr *SubInit = ILE->getInit(Init: Index); |
784 | if (I->getType()->isReferenceType()) |
785 | visitLocalsRetainedByReferenceBinding(Path, Init: SubInit, |
786 | RK: RK_ReferenceBinding, Visit, |
787 | EnableLifetimeWarnings); |
788 | else |
789 | // This might be either aggregate-initialization of a member or |
790 | // initialization of a std::initializer_list object. Regardless, |
791 | // we should recursively lifetime-extend that initializer. |
792 | visitLocalsRetainedByInitializer( |
793 | Path, Init: SubInit, Visit, RevisitSubinits, EnableLifetimeWarnings); |
794 | ++Index; |
795 | } |
796 | } |
797 | } |
798 | return; |
799 | } |
800 | |
801 | // The lifetime of an init-capture is that of the closure object constructed |
802 | // by a lambda-expression. |
803 | if (auto *LE = dyn_cast<LambdaExpr>(Val: Init)) { |
804 | LambdaExpr::capture_iterator CapI = LE->capture_begin(); |
805 | for (Expr *E : LE->capture_inits()) { |
806 | assert(CapI != LE->capture_end()); |
807 | const LambdaCapture &Cap = *CapI++; |
808 | if (!E) |
809 | continue; |
810 | if (Cap.capturesVariable()) |
811 | Path.push_back(Elt: {IndirectLocalPathEntry::LambdaCaptureInit, E, &Cap}); |
812 | if (E->isGLValue()) |
813 | visitLocalsRetainedByReferenceBinding(Path, Init: E, RK: RK_ReferenceBinding, |
814 | Visit, EnableLifetimeWarnings); |
815 | else |
816 | visitLocalsRetainedByInitializer(Path, Init: E, Visit, RevisitSubinits: true, |
817 | EnableLifetimeWarnings); |
818 | if (Cap.capturesVariable()) |
819 | Path.pop_back(); |
820 | } |
821 | } |
822 | |
823 | // Assume that a copy or move from a temporary references the same objects |
824 | // that the temporary does. |
825 | if (auto *CCE = dyn_cast<CXXConstructExpr>(Val: Init)) { |
826 | if (CCE->getConstructor()->isCopyOrMoveConstructor()) { |
827 | if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Val: CCE->getArg(Arg: 0))) { |
828 | // assert(false && "hit temporary copy path"); |
829 | Expr *Arg = MTE->getSubExpr(); |
830 | Path.push_back(Elt: {IndirectLocalPathEntry::TemporaryCopy, Arg, |
831 | CCE->getConstructor()}); |
832 | visitLocalsRetainedByInitializer(Path, Init: Arg, Visit, RevisitSubinits: true, |
833 | /*EnableLifetimeWarnings*/ false); |
834 | Path.pop_back(); |
835 | } |
836 | } |
837 | } |
838 | |
839 | if (isa<CallExpr>(Val: Init) || isa<CXXConstructExpr>(Val: Init)) { |
840 | if (EnableLifetimeWarnings) |
841 | handleGslAnnotatedTypes(Path, Call: Init, Visit); |
842 | return visitLifetimeBoundArguments(Path, Call: Init, Visit); |
843 | } |
844 | |
845 | switch (Init->getStmtClass()) { |
846 | case Stmt::UnaryOperatorClass: { |
847 | auto *UO = cast<UnaryOperator>(Val: Init); |
848 | // If the initializer is the address of a local, we could have a lifetime |
849 | // problem. |
850 | if (UO->getOpcode() == UO_AddrOf) { |
851 | // If this is &rvalue, then it's ill-formed and we have already diagnosed |
852 | // it. Don't produce a redundant warning about the lifetime of the |
853 | // temporary. |
854 | if (isa<MaterializeTemporaryExpr>(Val: UO->getSubExpr())) |
855 | return; |
856 | |
857 | Path.push_back(Elt: {IndirectLocalPathEntry::AddressOf, UO}); |
858 | visitLocalsRetainedByReferenceBinding(Path, Init: UO->getSubExpr(), |
859 | RK: RK_ReferenceBinding, Visit, |
860 | EnableLifetimeWarnings); |
861 | } |
862 | break; |
863 | } |
864 | |
865 | case Stmt::BinaryOperatorClass: { |
866 | // Handle pointer arithmetic. |
867 | auto *BO = cast<BinaryOperator>(Val: Init); |
868 | BinaryOperatorKind BOK = BO->getOpcode(); |
869 | if (!BO->getType()->isPointerType() || (BOK != BO_Add && BOK != BO_Sub)) |
870 | break; |
871 | |
872 | if (BO->getLHS()->getType()->isPointerType()) |
873 | visitLocalsRetainedByInitializer(Path, Init: BO->getLHS(), Visit, RevisitSubinits: true, |
874 | EnableLifetimeWarnings); |
875 | else if (BO->getRHS()->getType()->isPointerType()) |
876 | visitLocalsRetainedByInitializer(Path, Init: BO->getRHS(), Visit, RevisitSubinits: true, |
877 | EnableLifetimeWarnings); |
878 | break; |
879 | } |
880 | |
881 | case Stmt::ConditionalOperatorClass: |
882 | case Stmt::BinaryConditionalOperatorClass: { |
883 | auto *C = cast<AbstractConditionalOperator>(Val: Init); |
884 | // In C++, we can have a throw-expression operand, which has 'void' type |
885 | // and isn't interesting from a lifetime perspective. |
886 | if (!C->getTrueExpr()->getType()->isVoidType()) |
887 | visitLocalsRetainedByInitializer(Path, Init: C->getTrueExpr(), Visit, RevisitSubinits: true, |
888 | EnableLifetimeWarnings); |
889 | if (!C->getFalseExpr()->getType()->isVoidType()) |
890 | visitLocalsRetainedByInitializer(Path, Init: C->getFalseExpr(), Visit, RevisitSubinits: true, |
891 | EnableLifetimeWarnings); |
892 | break; |
893 | } |
894 | |
895 | case Stmt::BlockExprClass: |
896 | if (cast<BlockExpr>(Val: Init)->getBlockDecl()->hasCaptures()) { |
897 | // This is a local block, whose lifetime is that of the function. |
898 | Visit(Path, Local(cast<BlockExpr>(Val: Init)), RK_ReferenceBinding); |
899 | } |
900 | break; |
901 | |
902 | case Stmt::AddrLabelExprClass: |
903 | // We want to warn if the address of a label would escape the function. |
904 | Visit(Path, Local(cast<AddrLabelExpr>(Val: Init)), RK_ReferenceBinding); |
905 | break; |
906 | |
907 | default: |
908 | break; |
909 | } |
910 | } |
911 | |
912 | /// Whether a path to an object supports lifetime extension. |
913 | enum PathLifetimeKind { |
914 | /// Lifetime-extend along this path. |
915 | Extend, |
916 | /// We should lifetime-extend, but we don't because (due to technical |
917 | /// limitations) we can't. This happens for default member initializers, |
918 | /// which we don't clone for every use, so we don't have a unique |
919 | /// MaterializeTemporaryExpr to update. |
920 | ShouldExtend, |
921 | /// Do not lifetime extend along this path. |
922 | NoExtend |
923 | }; |
924 | |
925 | /// Determine whether this is an indirect path to a temporary that we are |
926 | /// supposed to lifetime-extend along. |
927 | static PathLifetimeKind |
928 | shouldLifetimeExtendThroughPath(const IndirectLocalPath &Path) { |
929 | PathLifetimeKind Kind = PathLifetimeKind::Extend; |
930 | for (auto Elem : Path) { |
931 | if (Elem.Kind == IndirectLocalPathEntry::DefaultInit) |
932 | Kind = PathLifetimeKind::ShouldExtend; |
933 | else if (Elem.Kind != IndirectLocalPathEntry::LambdaCaptureInit) |
934 | return PathLifetimeKind::NoExtend; |
935 | } |
936 | return Kind; |
937 | } |
938 | |
939 | /// Find the range for the first interesting entry in the path at or after I. |
940 | static SourceRange nextPathEntryRange(const IndirectLocalPath &Path, unsigned I, |
941 | Expr *E) { |
942 | for (unsigned N = Path.size(); I != N; ++I) { |
943 | switch (Path[I].Kind) { |
944 | case IndirectLocalPathEntry::AddressOf: |
945 | case IndirectLocalPathEntry::LValToRVal: |
946 | case IndirectLocalPathEntry::LifetimeBoundCall: |
947 | case IndirectLocalPathEntry::TemporaryCopy: |
948 | case IndirectLocalPathEntry::GslReferenceInit: |
949 | case IndirectLocalPathEntry::GslPointerInit: |
950 | case IndirectLocalPathEntry::GslPointerAssignment: |
951 | // These exist primarily to mark the path as not permitting or |
952 | // supporting lifetime extension. |
953 | break; |
954 | |
955 | case IndirectLocalPathEntry::VarInit: |
956 | if (cast<VarDecl>(Val: Path[I].D)->isImplicit()) |
957 | return SourceRange(); |
958 | [[fallthrough]]; |
959 | case IndirectLocalPathEntry::DefaultInit: |
960 | return Path[I].E->getSourceRange(); |
961 | |
962 | case IndirectLocalPathEntry::LambdaCaptureInit: |
963 | if (!Path[I].Capture->capturesVariable()) |
964 | continue; |
965 | return Path[I].E->getSourceRange(); |
966 | } |
967 | } |
968 | return E->getSourceRange(); |
969 | } |
970 | |
971 | static bool pathOnlyHandlesGslPointer(IndirectLocalPath &Path) { |
972 | for (const auto &It : llvm::reverse(C&: Path)) { |
973 | switch (It.Kind) { |
974 | case IndirectLocalPathEntry::VarInit: |
975 | case IndirectLocalPathEntry::AddressOf: |
976 | case IndirectLocalPathEntry::LifetimeBoundCall: |
977 | continue; |
978 | case IndirectLocalPathEntry::GslPointerInit: |
979 | case IndirectLocalPathEntry::GslReferenceInit: |
980 | case IndirectLocalPathEntry::GslPointerAssignment: |
981 | return true; |
982 | default: |
983 | return false; |
984 | } |
985 | } |
986 | return false; |
987 | } |
988 | |
989 | static void checkExprLifetimeImpl(Sema &SemaRef, |
990 | const InitializedEntity *InitEntity, |
991 | const InitializedEntity *ExtendingEntity, |
992 | LifetimeKind LK, |
993 | const AssignedEntity *AEntity, Expr *Init, |
994 | bool EnableLifetimeWarnings) { |
995 | assert((AEntity && LK == LK_Assignment) || |
996 | (InitEntity && LK != LK_Assignment)); |
997 | // If this entity doesn't have an interesting lifetime, don't bother looking |
998 | // for temporaries within its initializer. |
999 | if (LK == LK_FullExpression) |
1000 | return; |
1001 | |
1002 | // FIXME: consider moving the TemporaryVisitor and visitLocalsRetained* |
1003 | // functions to a dedicated class. |
1004 | auto TemporaryVisitor = [&](IndirectLocalPath &Path, Local L, |
1005 | ReferenceKind RK) -> bool { |
1006 | SourceRange DiagRange = nextPathEntryRange(Path, I: 0, E: L); |
1007 | SourceLocation DiagLoc = DiagRange.getBegin(); |
1008 | |
1009 | auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Val: L); |
1010 | |
1011 | bool IsGslPtrValueFromGslTempOwner = false; |
1012 | bool IsLocalGslOwner = false; |
1013 | if (pathOnlyHandlesGslPointer(Path)) { |
1014 | if (isa<DeclRefExpr>(Val: L)) { |
1015 | // We do not want to follow the references when returning a pointer |
1016 | // originating from a local owner to avoid the following false positive: |
1017 | // int &p = *localUniquePtr; |
1018 | // someContainer.add(std::move(localUniquePtr)); |
1019 | // return p; |
1020 | IsLocalGslOwner = isRecordWithAttr<OwnerAttr>(Type: L->getType()); |
1021 | if (pathContainsInit(Path) || !IsLocalGslOwner) |
1022 | return false; |
1023 | } else { |
1024 | IsGslPtrValueFromGslTempOwner = |
1025 | MTE && !MTE->getExtendingDecl() && |
1026 | isRecordWithAttr<OwnerAttr>(Type: MTE->getType()); |
1027 | // Skipping a chain of initializing gsl::Pointer annotated objects. |
1028 | // We are looking only for the final source to find out if it was |
1029 | // a local or temporary owner or the address of a local variable/param. |
1030 | if (!IsGslPtrValueFromGslTempOwner) |
1031 | return true; |
1032 | } |
1033 | } |
1034 | |
1035 | switch (LK) { |
1036 | case LK_FullExpression: |
1037 | llvm_unreachable("already handled this" ); |
1038 | |
1039 | case LK_Extended: { |
1040 | if (!MTE) { |
1041 | // The initialized entity has lifetime beyond the full-expression, |
1042 | // and the local entity does too, so don't warn. |
1043 | // |
1044 | // FIXME: We should consider warning if a static / thread storage |
1045 | // duration variable retains an automatic storage duration local. |
1046 | return false; |
1047 | } |
1048 | |
1049 | if (IsGslPtrValueFromGslTempOwner && DiagLoc.isValid()) { |
1050 | SemaRef.Diag(Loc: DiagLoc, DiagID: diag::warn_dangling_lifetime_pointer) |
1051 | << DiagRange; |
1052 | return false; |
1053 | } |
1054 | |
1055 | switch (shouldLifetimeExtendThroughPath(Path)) { |
1056 | case PathLifetimeKind::Extend: |
1057 | // Update the storage duration of the materialized temporary. |
1058 | // FIXME: Rebuild the expression instead of mutating it. |
1059 | MTE->setExtendingDecl(ExtendedBy: ExtendingEntity->getDecl(), |
1060 | ManglingNumber: ExtendingEntity->allocateManglingNumber()); |
1061 | // Also visit the temporaries lifetime-extended by this initializer. |
1062 | return true; |
1063 | |
1064 | case PathLifetimeKind::ShouldExtend: |
1065 | // We're supposed to lifetime-extend the temporary along this path (per |
1066 | // the resolution of DR1815), but we don't support that yet. |
1067 | // |
1068 | // FIXME: Properly handle this situation. Perhaps the easiest approach |
1069 | // would be to clone the initializer expression on each use that would |
1070 | // lifetime extend its temporaries. |
1071 | SemaRef.Diag(Loc: DiagLoc, DiagID: diag::warn_unsupported_lifetime_extension) |
1072 | << RK << DiagRange; |
1073 | break; |
1074 | |
1075 | case PathLifetimeKind::NoExtend: |
1076 | // If the path goes through the initialization of a variable or field, |
1077 | // it can't possibly reach a temporary created in this full-expression. |
1078 | // We will have already diagnosed any problems with the initializer. |
1079 | if (pathContainsInit(Path)) |
1080 | return false; |
1081 | |
1082 | SemaRef.Diag(Loc: DiagLoc, DiagID: diag::warn_dangling_variable) |
1083 | << RK << !InitEntity->getParent() |
1084 | << ExtendingEntity->getDecl()->isImplicit() |
1085 | << ExtendingEntity->getDecl() << Init->isGLValue() << DiagRange; |
1086 | break; |
1087 | } |
1088 | break; |
1089 | } |
1090 | |
1091 | case LK_Assignment: { |
1092 | if (!MTE || pathContainsInit(Path)) |
1093 | return false; |
1094 | assert(shouldLifetimeExtendThroughPath(Path) == |
1095 | PathLifetimeKind::NoExtend && |
1096 | "No lifetime extension for assignments" ); |
1097 | SemaRef.Diag(Loc: DiagLoc, |
1098 | DiagID: IsGslPtrValueFromGslTempOwner |
1099 | ? diag::warn_dangling_lifetime_pointer_assignment |
1100 | : diag::warn_dangling_pointer_assignment) |
1101 | << AEntity->LHS << DiagRange; |
1102 | return false; |
1103 | } |
1104 | case LK_MemInitializer: { |
1105 | if (MTE) { |
1106 | // Under C++ DR1696, if a mem-initializer (or a default member |
1107 | // initializer used by the absence of one) would lifetime-extend a |
1108 | // temporary, the program is ill-formed. |
1109 | if (auto *ExtendingDecl = |
1110 | ExtendingEntity ? ExtendingEntity->getDecl() : nullptr) { |
1111 | if (IsGslPtrValueFromGslTempOwner) { |
1112 | SemaRef.Diag(Loc: DiagLoc, DiagID: diag::warn_dangling_lifetime_pointer_member) |
1113 | << ExtendingDecl << DiagRange; |
1114 | SemaRef.Diag(Loc: ExtendingDecl->getLocation(), |
1115 | DiagID: diag::note_ref_or_ptr_member_declared_here) |
1116 | << true; |
1117 | return false; |
1118 | } |
1119 | bool IsSubobjectMember = ExtendingEntity != InitEntity; |
1120 | SemaRef.Diag(Loc: DiagLoc, DiagID: shouldLifetimeExtendThroughPath(Path) != |
1121 | PathLifetimeKind::NoExtend |
1122 | ? diag::err_dangling_member |
1123 | : diag::warn_dangling_member) |
1124 | << ExtendingDecl << IsSubobjectMember << RK << DiagRange; |
1125 | // Don't bother adding a note pointing to the field if we're inside |
1126 | // its default member initializer; our primary diagnostic points to |
1127 | // the same place in that case. |
1128 | if (Path.empty() || |
1129 | Path.back().Kind != IndirectLocalPathEntry::DefaultInit) { |
1130 | SemaRef.Diag(Loc: ExtendingDecl->getLocation(), |
1131 | DiagID: diag::note_lifetime_extending_member_declared_here) |
1132 | << RK << IsSubobjectMember; |
1133 | } |
1134 | } else { |
1135 | // We have a mem-initializer but no particular field within it; this |
1136 | // is either a base class or a delegating initializer directly |
1137 | // initializing the base-class from something that doesn't live long |
1138 | // enough. |
1139 | // |
1140 | // FIXME: Warn on this. |
1141 | return false; |
1142 | } |
1143 | } else { |
1144 | // Paths via a default initializer can only occur during error recovery |
1145 | // (there's no other way that a default initializer can refer to a |
1146 | // local). Don't produce a bogus warning on those cases. |
1147 | if (pathContainsInit(Path)) |
1148 | return false; |
1149 | |
1150 | // Suppress false positives for code like the one below: |
1151 | // Ctor(unique_ptr<T> up) : member(*up), member2(move(up)) {} |
1152 | if (IsLocalGslOwner && pathOnlyHandlesGslPointer(Path)) |
1153 | return false; |
1154 | |
1155 | auto *DRE = dyn_cast<DeclRefExpr>(Val: L); |
1156 | auto *VD = DRE ? dyn_cast<VarDecl>(Val: DRE->getDecl()) : nullptr; |
1157 | if (!VD) { |
1158 | // A member was initialized to a local block. |
1159 | // FIXME: Warn on this. |
1160 | return false; |
1161 | } |
1162 | |
1163 | if (auto *Member = |
1164 | ExtendingEntity ? ExtendingEntity->getDecl() : nullptr) { |
1165 | bool IsPointer = !Member->getType()->isReferenceType(); |
1166 | SemaRef.Diag(Loc: DiagLoc, |
1167 | DiagID: IsPointer ? diag::warn_init_ptr_member_to_parameter_addr |
1168 | : diag::warn_bind_ref_member_to_parameter) |
1169 | << Member << VD << isa<ParmVarDecl>(Val: VD) << DiagRange; |
1170 | SemaRef.Diag(Loc: Member->getLocation(), |
1171 | DiagID: diag::note_ref_or_ptr_member_declared_here) |
1172 | << (unsigned)IsPointer; |
1173 | } |
1174 | } |
1175 | break; |
1176 | } |
1177 | |
1178 | case LK_New: |
1179 | if (isa<MaterializeTemporaryExpr>(Val: L)) { |
1180 | if (IsGslPtrValueFromGslTempOwner) |
1181 | SemaRef.Diag(Loc: DiagLoc, DiagID: diag::warn_dangling_lifetime_pointer) |
1182 | << DiagRange; |
1183 | else |
1184 | SemaRef.Diag(Loc: DiagLoc, DiagID: RK == RK_ReferenceBinding |
1185 | ? diag::warn_new_dangling_reference |
1186 | : diag::warn_new_dangling_initializer_list) |
1187 | << !InitEntity->getParent() << DiagRange; |
1188 | } else { |
1189 | // We can't determine if the allocation outlives the local declaration. |
1190 | return false; |
1191 | } |
1192 | break; |
1193 | |
1194 | case LK_Return: |
1195 | case LK_StmtExprResult: |
1196 | if (auto *DRE = dyn_cast<DeclRefExpr>(Val: L)) { |
1197 | // We can't determine if the local variable outlives the statement |
1198 | // expression. |
1199 | if (LK == LK_StmtExprResult) |
1200 | return false; |
1201 | SemaRef.Diag(Loc: DiagLoc, DiagID: diag::warn_ret_stack_addr_ref) |
1202 | << InitEntity->getType()->isReferenceType() << DRE->getDecl() |
1203 | << isa<ParmVarDecl>(Val: DRE->getDecl()) << DiagRange; |
1204 | } else if (isa<BlockExpr>(Val: L)) { |
1205 | SemaRef.Diag(Loc: DiagLoc, DiagID: diag::err_ret_local_block) << DiagRange; |
1206 | } else if (isa<AddrLabelExpr>(Val: L)) { |
1207 | // Don't warn when returning a label from a statement expression. |
1208 | // Leaving the scope doesn't end its lifetime. |
1209 | if (LK == LK_StmtExprResult) |
1210 | return false; |
1211 | SemaRef.Diag(Loc: DiagLoc, DiagID: diag::warn_ret_addr_label) << DiagRange; |
1212 | } else if (auto *CLE = dyn_cast<CompoundLiteralExpr>(Val: L)) { |
1213 | SemaRef.Diag(Loc: DiagLoc, DiagID: diag::warn_ret_stack_addr_ref) |
1214 | << InitEntity->getType()->isReferenceType() << CLE->getInitializer() |
1215 | << 2 << DiagRange; |
1216 | } else { |
1217 | // P2748R5: Disallow Binding a Returned Glvalue to a Temporary. |
1218 | // [stmt.return]/p6: In a function whose return type is a reference, |
1219 | // other than an invented function for std::is_convertible ([meta.rel]), |
1220 | // a return statement that binds the returned reference to a temporary |
1221 | // expression ([class.temporary]) is ill-formed. |
1222 | if (SemaRef.getLangOpts().CPlusPlus26 && |
1223 | InitEntity->getType()->isReferenceType()) |
1224 | SemaRef.Diag(Loc: DiagLoc, DiagID: diag::err_ret_local_temp_ref) |
1225 | << InitEntity->getType()->isReferenceType() << DiagRange; |
1226 | else |
1227 | SemaRef.Diag(Loc: DiagLoc, DiagID: diag::warn_ret_local_temp_addr_ref) |
1228 | << InitEntity->getType()->isReferenceType() << DiagRange; |
1229 | } |
1230 | break; |
1231 | } |
1232 | |
1233 | for (unsigned I = 0; I != Path.size(); ++I) { |
1234 | auto Elem = Path[I]; |
1235 | |
1236 | switch (Elem.Kind) { |
1237 | case IndirectLocalPathEntry::AddressOf: |
1238 | case IndirectLocalPathEntry::LValToRVal: |
1239 | // These exist primarily to mark the path as not permitting or |
1240 | // supporting lifetime extension. |
1241 | break; |
1242 | |
1243 | case IndirectLocalPathEntry::LifetimeBoundCall: |
1244 | case IndirectLocalPathEntry::TemporaryCopy: |
1245 | case IndirectLocalPathEntry::GslPointerInit: |
1246 | case IndirectLocalPathEntry::GslReferenceInit: |
1247 | case IndirectLocalPathEntry::GslPointerAssignment: |
1248 | // FIXME: Consider adding a note for these. |
1249 | break; |
1250 | |
1251 | case IndirectLocalPathEntry::DefaultInit: { |
1252 | auto *FD = cast<FieldDecl>(Val: Elem.D); |
1253 | SemaRef.Diag(Loc: FD->getLocation(), |
1254 | DiagID: diag::note_init_with_default_member_initializer) |
1255 | << FD << nextPathEntryRange(Path, I: I + 1, E: L); |
1256 | break; |
1257 | } |
1258 | |
1259 | case IndirectLocalPathEntry::VarInit: { |
1260 | const VarDecl *VD = cast<VarDecl>(Val: Elem.D); |
1261 | SemaRef.Diag(Loc: VD->getLocation(), DiagID: diag::note_local_var_initializer) |
1262 | << VD->getType()->isReferenceType() << VD->isImplicit() |
1263 | << VD->getDeclName() << nextPathEntryRange(Path, I: I + 1, E: L); |
1264 | break; |
1265 | } |
1266 | |
1267 | case IndirectLocalPathEntry::LambdaCaptureInit: |
1268 | if (!Elem.Capture->capturesVariable()) |
1269 | break; |
1270 | // FIXME: We can't easily tell apart an init-capture from a nested |
1271 | // capture of an init-capture. |
1272 | const ValueDecl *VD = Elem.Capture->getCapturedVar(); |
1273 | SemaRef.Diag(Loc: Elem.Capture->getLocation(), |
1274 | DiagID: diag::note_lambda_capture_initializer) |
1275 | << VD << VD->isInitCapture() << Elem.Capture->isExplicit() |
1276 | << (Elem.Capture->getCaptureKind() == LCK_ByRef) << VD |
1277 | << nextPathEntryRange(Path, I: I + 1, E: L); |
1278 | break; |
1279 | } |
1280 | } |
1281 | |
1282 | // We didn't lifetime-extend, so don't go any further; we don't need more |
1283 | // warnings or errors on inner temporaries within this one's initializer. |
1284 | return false; |
1285 | }; |
1286 | |
1287 | llvm::SmallVector<IndirectLocalPathEntry, 8> Path; |
1288 | if (EnableLifetimeWarnings && LK == LK_Assignment && |
1289 | isRecordWithAttr<PointerAttr>(Type: AEntity->LHS->getType())) |
1290 | Path.push_back(Elt: {IndirectLocalPathEntry::GslPointerAssignment, Init}); |
1291 | |
1292 | if (Init->isGLValue()) |
1293 | visitLocalsRetainedByReferenceBinding(Path, Init, RK: RK_ReferenceBinding, |
1294 | Visit: TemporaryVisitor, |
1295 | EnableLifetimeWarnings); |
1296 | else |
1297 | visitLocalsRetainedByInitializer( |
1298 | Path, Init, Visit: TemporaryVisitor, |
1299 | // Don't revisit the sub inits for the intialization case. |
1300 | /*RevisitSubinits=*/!InitEntity, EnableLifetimeWarnings); |
1301 | } |
1302 | |
1303 | void checkExprLifetime(Sema &SemaRef, const InitializedEntity &Entity, |
1304 | Expr *Init) { |
1305 | auto LTResult = getEntityLifetime(Entity: &Entity); |
1306 | LifetimeKind LK = LTResult.getInt(); |
1307 | const InitializedEntity *ExtendingEntity = LTResult.getPointer(); |
1308 | bool EnableLifetimeWarnings = !SemaRef.getDiagnostics().isIgnored( |
1309 | DiagID: diag::warn_dangling_lifetime_pointer, Loc: SourceLocation()); |
1310 | checkExprLifetimeImpl(SemaRef, InitEntity: &Entity, ExtendingEntity, LK, |
1311 | /*AEntity*/ nullptr, Init, EnableLifetimeWarnings); |
1312 | } |
1313 | |
1314 | void checkExprLifetime(Sema &SemaRef, const AssignedEntity &Entity, |
1315 | Expr *Init) { |
1316 | bool EnableLifetimeWarnings = !SemaRef.getDiagnostics().isIgnored( |
1317 | DiagID: diag::warn_dangling_lifetime_pointer, Loc: SourceLocation()); |
1318 | bool RunAnalysis = Entity.LHS->getType()->isPointerType() || |
1319 | (EnableLifetimeWarnings && |
1320 | isRecordWithAttr<PointerAttr>(Type: Entity.LHS->getType())); |
1321 | |
1322 | if (!RunAnalysis) |
1323 | return; |
1324 | |
1325 | checkExprLifetimeImpl(SemaRef, /*InitEntity=*/nullptr, |
1326 | /*ExtendingEntity=*/nullptr, LK: LK_Assignment, AEntity: &Entity, |
1327 | Init, EnableLifetimeWarnings); |
1328 | } |
1329 | |
1330 | } // namespace clang::sema |
1331 | |