blob: 83faa10aa64143485b92919058d212aa9c04ca57 [file] [log] [blame]
Luca Versari99fddff2022-05-25 10:22:32 -07001// Part of the Crubit project, under the Apache License v2.0 with LLVM
2// Exceptions. See /LICENSE for license information.
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5#include "lifetime_analysis/lifetime_analysis.h"
6
7#include <iostream>
8#include <memory>
9#include <optional>
10#include <string>
11#include <utility>
12#include <variant>
13#include <vector>
14
15#include "lifetime_analysis/builtin_lifetimes.h"
16#include "lifetime_analysis/object.h"
17#include "lifetime_analysis/object_repository.h"
18#include "lifetime_analysis/object_set.h"
19#include "lifetime_analysis/pointer_compatibility.h"
20#include "lifetime_analysis/points_to_map.h"
Luca Versari99fddff2022-05-25 10:22:32 -070021#include "lifetime_annotations/function_lifetimes.h"
22#include "lifetime_annotations/lifetime.h"
23#include "lifetime_annotations/pointee_type.h"
24#include "lifetime_annotations/type_lifetimes.h"
25#include "clang/AST/Decl.h"
26#include "clang/AST/DeclCXX.h"
27#include "clang/AST/Expr.h"
28#include "clang/AST/ExprCXX.h"
29#include "clang/AST/OperationKinds.h"
30#include "clang/AST/Stmt.h"
31#include "clang/AST/StmtVisitor.h"
32#include "clang/AST/TemplateBase.h"
33#include "clang/AST/Type.h"
Wei Yi Teedae0e822022-09-20 15:03:43 -070034#include "clang/Analysis/CFG.h"
Luca Versari99fddff2022-05-25 10:22:32 -070035#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
36#include "llvm/ADT/ArrayRef.h"
37#include "llvm/ADT/DenseMap.h"
Luca Versari99fddff2022-05-25 10:22:32 -070038#include "llvm/Support/Error.h"
39#include "llvm/Support/ErrorHandling.h"
40
41namespace clang {
42namespace tidy {
43namespace lifetimes {
44
45namespace {
46
47class TransferStmtVisitor
48 : public clang::StmtVisitor<TransferStmtVisitor,
49 std::optional<std::string>> {
50 public:
51 TransferStmtVisitor(
52 ObjectRepository& object_repository, PointsToMap& points_to_map,
Luca Versari53a2f582023-01-16 10:09:29 -080053 LifetimeConstraints& constraints, ObjectSet& single_valued_objects,
54 const clang::FunctionDecl* func,
Luca Versari99fddff2022-05-25 10:22:32 -070055 const llvm::DenseMap<const clang::FunctionDecl*,
56 FunctionLifetimesOrError>& callee_lifetimes,
57 const DiagnosticReporter& diag_reporter)
58 : object_repository_(object_repository),
59 points_to_map_(points_to_map),
Luca Versari1f9fc2e2022-08-17 07:06:00 -070060 constraints_(constraints),
Luca Versari53a2f582023-01-16 10:09:29 -080061 single_valued_objects_(single_valued_objects),
Luca Versari99fddff2022-05-25 10:22:32 -070062 func_(func),
63 callee_lifetimes_(callee_lifetimes),
64 diag_reporter_(diag_reporter) {}
65
66 std::optional<std::string> VisitExpr(const clang::Expr* expr);
67 std::optional<std::string> VisitDeclRefExpr(
68 const clang::DeclRefExpr* decl_ref);
69 std::optional<std::string> VisitStringLiteral(
70 const clang::StringLiteral* strlit);
71 std::optional<std::string> VisitCastExpr(const clang::CastExpr* cast);
72 std::optional<std::string> VisitReturnStmt(
73 const clang::ReturnStmt* return_stmt);
74 std::optional<std::string> VisitDeclStmt(const clang::DeclStmt* decl_stmt);
75 std::optional<std::string> VisitUnaryOperator(const clang::UnaryOperator* op);
76 std::optional<std::string> VisitArraySubscriptExpr(
77 const clang::ArraySubscriptExpr* subscript);
78 std::optional<std::string> VisitBinaryOperator(
79 const clang::BinaryOperator* op);
80 std::optional<std::string> VisitConditionalOperator(
81 const clang::ConditionalOperator* op);
82 std::optional<std::string> VisitInitListExpr(
83 const clang::InitListExpr* init_list);
84 std::optional<std::string> VisitMaterializeTemporaryExpr(
85 const clang::MaterializeTemporaryExpr* temporary_expr);
86 std::optional<std::string> VisitMemberExpr(const clang::MemberExpr* member);
87 std::optional<std::string> VisitCXXThisExpr(
88 const clang::CXXThisExpr* this_expr);
89 std::optional<std::string> VisitCallExpr(const clang::CallExpr* call);
90 std::optional<std::string> VisitCXXConstructExpr(
91 const clang::CXXConstructExpr* construct_expr);
92
93 private:
94 ObjectRepository& object_repository_;
95 PointsToMap& points_to_map_;
Luca Versari1f9fc2e2022-08-17 07:06:00 -070096 LifetimeConstraints& constraints_;
Luca Versari53a2f582023-01-16 10:09:29 -080097 ObjectSet& single_valued_objects_;
Luca Versari99fddff2022-05-25 10:22:32 -070098 const clang::FunctionDecl* func_;
99 const llvm::DenseMap<const clang::FunctionDecl*, FunctionLifetimesOrError>&
100 callee_lifetimes_;
101 const DiagnosticReporter& diag_reporter_;
102};
103
Luca Versaria12c9d52023-02-23 08:40:48 -0800104void GenerateConstraintsForSingleAssignment(const Object* oldp,
105 const Object* newp,
106 LifetimeConstraints& constraints) {
107 if (oldp->GetFuncLifetimes().has_value() &&
108 newp->GetFuncLifetimes().has_value()) {
109 // The order of `newp` and `oldp` here may seem surprising. However, this
110 // can be thought of as: "I am assigning `newp` where before I had `oldp`,
111 // therefore `oldp` needs to be able to represent a call to whatever it is
112 // that `newp` represents, hence I need to generate constraints for
113 // replacing `newp` with `oldp`". At least this is why veluca@ thinks this
114 // is the correct order (the opposite order generates incorrect results).
115 constraints.join(LifetimeConstraints::ForCallableSubstitutionFull(
116 *newp->GetFuncLifetimes(), *oldp->GetFuncLifetimes()));
117 }
118 constraints.AddOutlivesConstraint(oldp->GetLifetime(), newp->GetLifetime());
119}
120
Luca Versari5a414a22022-10-05 03:35:56 -0700121void GenerateConstraintsForAssignmentNonRecursive(
122 const ObjectSet& old_pointees, const ObjectSet& new_pointees,
123 bool is_in_invariant_context, LifetimeConstraints& constraints) {
124 // The new pointees must always outlive the old pointees.
125 for (const Object* old : old_pointees) {
126 for (const Object* newp : new_pointees) {
Luca Versaria12c9d52023-02-23 08:40:48 -0800127 GenerateConstraintsForSingleAssignment(old, newp, constraints);
Luca Versari5a414a22022-10-05 03:35:56 -0700128 }
129 }
130
131 // If we are in an invariant context, we need to insert constraints in the
132 // opposite direction too (i.e. we need equality).
133 if (is_in_invariant_context) {
134 for (const Object* old : old_pointees) {
135 for (const Object* newp : new_pointees) {
Luca Versaria12c9d52023-02-23 08:40:48 -0800136 GenerateConstraintsForSingleAssignment(newp, old, constraints);
Luca Versari5a414a22022-10-05 03:35:56 -0700137 }
138 }
139 }
140}
141
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700142// TODO(veluca): this is quadratic.
Luca Versari5a414a22022-10-05 03:35:56 -0700143void GenerateConstraintsForAssignmentRecursive(
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700144 const ObjectSet& pointers, const ObjectSet& new_pointees,
145 clang::QualType pointer_type, const ObjectRepository& object_repository,
Luca Versaric6965ec2022-09-02 02:51:33 -0700146 const PointsToMap& points_to_map, bool is_in_invariant_context,
147 LifetimeConstraints& constraints,
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700148 llvm::DenseSet<std::pair<const Object*, const Object*>>& seen_pairs) {
149 // Check for cycles.
150 {
151 size_t num_seen_pairs = seen_pairs.size();
152 for (auto pointer : pointers) {
153 for (auto pointee : new_pointees) {
154 seen_pairs.insert({pointer, pointee});
155 }
156 }
157 // All done: all the pairs we have were already seen.
158 if (num_seen_pairs == seen_pairs.size()) return;
159 }
160
Luca Versaricd257d32022-08-22 02:09:25 -0700161 if (pointer_type->isIncompleteType()) {
162 // Nothing we *can* do.
163 return;
164 }
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700165 assert(!pointer_type->isRecordType());
166 if (!pointer_type->isPointerType() && !pointer_type->isReferenceType()) {
167 // Nothing to do.
168 return;
169 }
170
171 ObjectSet old_pointees = points_to_map.GetPointerPointsToSet(pointers);
172
Luca Versari5a414a22022-10-05 03:35:56 -0700173 GenerateConstraintsForAssignmentNonRecursive(
174 old_pointees, new_pointees, is_in_invariant_context, constraints);
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700175
Luca Versaric6965ec2022-09-02 02:51:33 -0700176 // See https://doc.rust-lang.org/nomicon/subtyping.html for an explanation of
177 // variance; here in particular, we use the fact that the pointee of a pointer
Luca Versaric0843152022-09-16 02:10:25 -0700178 // is covariant if the pointer points to a const-qualified type, and invariant
179 // otherwise.
180 is_in_invariant_context = !pointer_type->getPointeeType().isConstQualified();
Luca Versaric6965ec2022-09-02 02:51:33 -0700181
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700182 // Recurse in pointees. As the pointee might be of struct type, we need first
183 // to extract all field pointers from it.
184 struct RecursiveVisitInfo {
185 clang::QualType type;
186 ObjectSet old_pointees;
187 ObjectSet new_pointees;
188 };
189
190 std::vector<RecursiveVisitInfo> calls_to_make;
191 calls_to_make.push_back(
192 {pointer_type->getPointeeType(), old_pointees, new_pointees});
193 while (!calls_to_make.empty()) {
194 RecursiveVisitInfo call = std::move(calls_to_make.back());
195 calls_to_make.pop_back();
196
Luca Versaricd257d32022-08-22 02:09:25 -0700197 if (call.type->isIncompleteType()) {
198 // Nothing we *can* do.
199 continue;
200 }
201
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700202 if (const auto* record_type = call.type->getAs<clang::RecordType>()) {
203 for (auto field : record_type->getDecl()->fields()) {
204 calls_to_make.push_back(
205 {field->getType(),
Luca Versari187176a2022-09-02 02:42:23 -0700206 object_repository.GetFieldObject(call.old_pointees, field),
207 object_repository.GetFieldObject(call.new_pointees, field)});
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700208 }
209 if (auto* cxxrecord =
210 clang::dyn_cast<clang::CXXRecordDecl>(record_type->getDecl())) {
211 for (const clang::CXXBaseSpecifier& base : cxxrecord->bases()) {
212 calls_to_make.push_back({base.getType(),
213 object_repository.GetBaseClassObject(
Luca Versari187176a2022-09-02 02:42:23 -0700214 call.old_pointees, base.getType()),
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700215 object_repository.GetBaseClassObject(
Luca Versari187176a2022-09-02 02:42:23 -0700216 call.new_pointees, base.getType())});
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700217 }
218 }
219 } else {
Luca Versari5a414a22022-10-05 03:35:56 -0700220 GenerateConstraintsForAssignmentRecursive(
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700221 call.old_pointees,
222 points_to_map.GetPointerPointsToSet(call.new_pointees), call.type,
Luca Versaric6965ec2022-09-02 02:51:33 -0700223 object_repository, points_to_map, is_in_invariant_context,
224 constraints, seen_pairs);
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700225 }
226 }
227}
228
Luca Versari187176a2022-09-02 02:42:23 -0700229void GenerateConstraintsForAssignment(const ObjectSet& pointers,
230 const ObjectSet& new_pointees,
231 clang::QualType pointer_type,
232 const ObjectRepository& object_repository,
233 PointsToMap& points_to_map,
234 LifetimeConstraints& constraints) {
235 llvm::DenseSet<std::pair<const Object*, const Object*>> seen_pairs;
Luca Versaric6965ec2022-09-02 02:51:33 -0700236 // Outer-most pointers are never invariant.
Luca Versari5a414a22022-10-05 03:35:56 -0700237 GenerateConstraintsForAssignmentRecursive(
Luca Versaric6965ec2022-09-02 02:51:33 -0700238 pointers, new_pointees, pointer_type, object_repository, points_to_map,
239 /*is_in_invariant_context=*/false, constraints, seen_pairs);
Luca Versari187176a2022-09-02 02:42:23 -0700240}
241
Luca Versariefeaf272023-01-16 10:19:28 -0800242void GenerateConstraintsForObjectLifetimeEquality(
243 const ObjectSet& a, const ObjectSet& b, const clang::QualType& type,
244 const PointsToMap& points_to_map, const ObjectRepository& object_repository,
245 LifetimeConstraints& constraints) {
246 GenerateConstraintsForAssignmentNonRecursive(
247 a, b, /*is_in_invariant_context=*/true, constraints);
248 if (const auto* record_type = type->getAs<clang::RecordType>()) {
249 for (auto field : record_type->getDecl()->fields()) {
250 GenerateConstraintsForObjectLifetimeEquality(
251 object_repository.GetFieldObject(a, field),
252 object_repository.GetFieldObject(b, field), field->getType(),
253 points_to_map, object_repository, constraints);
254 }
255 if (auto* cxxrecord =
256 clang::dyn_cast<clang::CXXRecordDecl>(record_type->getDecl())) {
257 for (const clang::CXXBaseSpecifier& base : cxxrecord->bases()) {
258 GenerateConstraintsForObjectLifetimeEquality(
259 object_repository.GetBaseClassObject(a, base.getType()),
260 object_repository.GetBaseClassObject(b, base.getType()),
261 base.getType(), points_to_map, object_repository, constraints);
262 }
263 }
264 } else if (!PointeeType(type).isNull()) {
265 GenerateConstraintsForObjectLifetimeEquality(
266 points_to_map.GetPointerPointsToSet(a),
267 points_to_map.GetPointerPointsToSet(b), PointeeType(type),
268 points_to_map, object_repository, constraints);
269 }
270}
271
272} // namespace
273
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700274void HandlePointsToSetExtension(const ObjectSet& pointers,
275 const ObjectSet& new_pointees,
276 clang::QualType pointer_type,
277 const ObjectRepository& object_repository,
278 PointsToMap& points_to_map,
279 LifetimeConstraints& constraints) {
Luca Versariefeaf272023-01-16 10:19:28 -0800280 // Record types should not get to this point at all, as
281 // their initialization is done by constructor calls.
282 assert(!pointer_type->isRecordType());
283 GenerateConstraintsForAssignment(pointers, new_pointees, pointer_type,
284 object_repository, points_to_map,
285 constraints);
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700286 for (const Object* pointer : pointers) {
287 points_to_map.ExtendPointerPointsToSet(pointer, new_pointees);
288 }
289}
290
Martin Brænne2a1b27a2022-07-01 06:17:32 -0700291void TransferInitializer(const Object* dest, clang::QualType type,
Luca Versari99fddff2022-05-25 10:22:32 -0700292 const ObjectRepository& object_repository,
293 const clang::Expr* init_expr,
Luca Versariefeaf272023-01-16 10:19:28 -0800294 TargetPointeeBehavior pointee_behavior,
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700295 PointsToMap& points_to_map,
296 LifetimeConstraints& constraints) {
Luca Versari99fddff2022-05-25 10:22:32 -0700297 type = type.getCanonicalType();
298 if (type->isArrayType()) {
299 type = type->castAsArrayTypeUnsafe()->getElementType();
300 }
301
302 // Initializer lists are handled one member/field at a time.
303 if (type->isRecordType()) {
304 if (auto init_list_expr = clang::dyn_cast<clang::InitListExpr>(init_expr)) {
305 // We assume that initializers are always the semantic form of
306 // InitListExpr.
307 assert(init_list_expr->isSemanticForm());
308 size_t init = 0;
309 for (auto f : type->getAs<clang::RecordType>()->getDecl()->fields()) {
310 assert(init < init_list_expr->getNumInits());
311 auto field_init = init_list_expr->getInit(init);
312 ++init;
Martin Brænne2a1b27a2022-07-01 06:17:32 -0700313 TransferInitializer(object_repository.GetFieldObject(dest, f),
Luca Versari99fddff2022-05-25 10:22:32 -0700314 f->getType(), object_repository, field_init,
Luca Versariefeaf272023-01-16 10:19:28 -0800315 pointee_behavior, points_to_map, constraints);
Luca Versari99fddff2022-05-25 10:22:32 -0700316 }
317 return;
318 }
319 }
320
321 if (type->isPointerType() || type->isReferenceType() ||
322 type->isStructureOrClassType()) {
323 ObjectSet init_points_to = points_to_map.GetExprObjectSet(init_expr);
Luca Versariefeaf272023-01-16 10:19:28 -0800324 if (pointee_behavior == TargetPointeeBehavior::kKeep) {
325 // It's important to use "Extend" (not "Set") here because we process
326 // initializers for member variables only _after_ the dataflow analysis
327 // has run.
328 HandlePointsToSetExtension({dest}, init_points_to, type,
329 object_repository, points_to_map, constraints);
Martin Brænne5d1a5242022-06-30 06:26:28 -0700330 } else {
Luca Versariefeaf272023-01-16 10:19:28 -0800331 points_to_map.SetPointerPointsToSet(dest, init_points_to);
Martin Brænne5d1a5242022-06-30 06:26:28 -0700332 }
333 }
Martin Brænne5d1a5242022-06-30 06:26:28 -0700334}
335
Luca Versari99fddff2022-05-25 10:22:32 -0700336LifetimeLattice LifetimeAnalysis::initialElement() {
Luca Versari53a2f582023-01-16 10:09:29 -0800337 return LifetimeLattice(object_repository_.InitialPointsToMap(),
338 object_repository_.InitialSingleValuedObjects());
Luca Versari99fddff2022-05-25 10:22:32 -0700339}
340
341std::string LifetimeAnalysis::ToString(const LifetimeLattice& state) {
342 return state.ToString();
343}
344
345bool LifetimeAnalysis::IsEqual(const LifetimeLattice& state1,
346 const LifetimeLattice& state2) {
347 return state1 == state2;
348}
349
Googler9949b462023-02-15 05:09:07 -0800350void LifetimeAnalysis::transfer(const clang::CFGElement& elt,
Wei Yi Teedae0e822022-09-20 15:03:43 -0700351 LifetimeLattice& state,
Luca Versari99fddff2022-05-25 10:22:32 -0700352 clang::dataflow::Environment& /*environment*/) {
353 if (state.IsError()) return;
354
Googler9949b462023-02-15 05:09:07 -0800355 auto cfg_stmt = elt.getAs<clang::CFGStmt>();
Wei Yi Teedae0e822022-09-20 15:03:43 -0700356 if (!cfg_stmt) return;
357 auto stmt = cfg_stmt->getStmt();
358
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700359 TransferStmtVisitor visitor(object_repository_, state.PointsTo(),
Luca Versari53a2f582023-01-16 10:09:29 -0800360 state.Constraints(), state.SingleValuedObjects(),
361 func_, callee_lifetimes_, diag_reporter_);
Luca Versari99fddff2022-05-25 10:22:32 -0700362 if (std::optional<std::string> err =
363 visitor.Visit(const_cast<clang::Stmt*>(stmt))) {
364 state = LifetimeLattice(*err);
365 }
366}
367
368namespace {
369
370std::optional<std::string> TransferStmtVisitor::VisitExpr(
371 const clang::Expr* expr) {
372 // Ensure that we don't attempt to analyze code that contains errors.
373 // This is triggered by TypoExpr and RecoveryExpr, but rather than handling
374 // these particular expression types individually, we just check
375 // Expr::containsErrors().
376 if (expr->containsErrors()) {
377 return "encountered an expression containing errors";
378 }
379 return std::nullopt;
380}
381
382std::optional<std::string> TransferStmtVisitor::VisitDeclRefExpr(
383 const clang::DeclRefExpr* decl_ref) {
384 auto* decl = decl_ref->getDecl();
385 if (!clang::isa<clang::VarDecl>(decl) &&
386 !clang::isa<clang::FunctionDecl>(decl)) {
387 return std::nullopt;
388 }
389
Martin Brænne46b5f072022-07-01 02:29:16 -0700390 const Object* object = object_repository_.GetDeclObject(decl);
Luca Versari99fddff2022-05-25 10:22:32 -0700391
392 assert(decl_ref->isGLValue() || decl_ref->getType()->isBuiltinType());
393
394 clang::QualType type = decl->getType().getCanonicalType();
395
396 if (type->isReferenceType()) {
397 points_to_map_.SetExprObjectSet(
398 decl_ref, points_to_map_.GetPointerPointsToSet(object));
399 } else {
400 points_to_map_.SetExprObjectSet(decl_ref, {object});
401 }
402
403 return std::nullopt;
404}
405
406std::optional<std::string> TransferStmtVisitor::VisitStringLiteral(
407 const clang::StringLiteral* strlit) {
Luca Versaria12c9d52023-02-23 08:40:48 -0800408 points_to_map_.SetExprObjectSet(
409 strlit, {object_repository_.GetStringLiteralObject()});
Luca Versari99fddff2022-05-25 10:22:32 -0700410 return std::nullopt;
411}
412
413std::optional<std::string> TransferStmtVisitor::VisitCastExpr(
414 const clang::CastExpr* cast) {
415 switch (cast->getCastKind()) {
416 case clang::CK_LValueToRValue: {
417 if (cast->getType()->isPointerType()) {
418 // Converting from a glvalue to a prvalue means that we need to perform
419 // a dereferencing operation because the objects associated with
420 // glvalues and prvalues have different meanings:
421 // - A glvalue is associated with the object identified by the glvalue.
422 // - A prvalue is only associated with an object if the prvalue is of
423 // pointer type; the object it is associated with is the object the
424 // pointer points to.
425 // See also documentation for PointsToMap.
426 ObjectSet points_to = points_to_map_.GetPointerPointsToSet(
427 points_to_map_.GetExprObjectSet(cast->getSubExpr()));
428 points_to_map_.SetExprObjectSet(cast, points_to);
429 }
430 break;
431 }
432 case clang::CK_NullToPointer: {
433 points_to_map_.SetExprObjectSet(cast, {});
434 break;
435 }
436 // These casts are just no-ops from a Object point of view.
437 case clang::CK_FunctionToPointerDecay:
438 case clang::CK_BuiltinFnToFnPtr:
439 case clang::CK_ArrayToPointerDecay:
440 case clang::CK_UserDefinedConversion:
441 // Note on CK_UserDefinedConversion: The actual conversion happens in a
442 // CXXMemberCallExpr that is a subexpression of this CastExpr. The
443 // CK_UserDefinedConversion is just used to mark the fact that this is a
444 // user-defined conversion; it's therefore a no-op for our purposes.
445 case clang::CK_NoOp: {
446 clang::QualType type = cast->getType().getCanonicalType();
447 if (type->isPointerType() || cast->isGLValue()) {
448 points_to_map_.SetExprObjectSet(
449 cast, points_to_map_.GetExprObjectSet(cast->getSubExpr()));
450 }
451 break;
452 }
453 case clang::CK_DerivedToBase:
454 case clang::CK_UncheckedDerivedToBase:
455 case clang::CK_BaseToDerived:
456 case clang::CK_Dynamic: {
457 // These need to be mapped to what the subexpr points to.
458 // (Simple cases just work okay with this; may need to be revisited when
459 // we add more inheritance support.)
460 ObjectSet points_to = points_to_map_.GetExprObjectSet(cast->getSubExpr());
461 points_to_map_.SetExprObjectSet(cast, points_to);
462 break;
463 }
464 case clang::CK_BitCast:
465 case clang::CK_LValueBitCast:
466 case clang::CK_IntegralToPointer: {
467 // We don't support analyzing functions that perform a reinterpret_cast.
468 diag_reporter_(
469 func_->getBeginLoc(),
470 "cannot infer lifetimes because function uses a type-unsafe cast",
471 clang::DiagnosticIDs::Warning);
472 diag_reporter_(cast->getBeginLoc(), "type-unsafe cast occurs here",
473 clang::DiagnosticIDs::Note);
474 return "type-unsafe cast prevents analysis";
475 }
476 default: {
477 if (cast->isGLValue() ||
478 cast->getType().getCanonicalType()->isPointerType()) {
479 llvm::errs() << "Unknown cast type:\n";
480 cast->dump();
481 // No-noop casts of pointer types are not handled yet.
482 llvm::report_fatal_error("unknown cast type encountered");
483 }
484 }
485 }
486 return std::nullopt;
487}
488
489std::optional<std::string> TransferStmtVisitor::VisitReturnStmt(
490 const clang::ReturnStmt* return_stmt) {
491 clang::QualType return_type = func_->getReturnType();
492 // We only need to handle pointers and references.
493 // For record types, initialization of the return value has already been
494 // handled in VisitCXXConstructExpr() or VisitInitListExpr(), so nothing
495 // to do here.
496 if (!return_type->isPointerType() && !return_type->isReferenceType()) {
497 return std::nullopt;
498 }
499
500 const clang::Expr* ret_expr = return_stmt->getRetValue();
501 // This occurs when computing `ret_expr`s result includes creating temporary
502 // objects with destructors. We want to find the value to be returned inside
503 // the ExprWithCleanups.
504 //
505 // The PointsToMap::GetExprObjectSet() function could do this but it doesn't
506 // understand the context from which it is being called. This operation needs
507 // to be done only in cases where we are leaving scope - that is, the return
508 // statement. And the return statement also needs to look for initializers in
509 // its sub expressions, after looking inside ExprWithCleanups.
510 //
511 // That means GetExprObjectSet() would need to also look for initializers but
512 // we don't want to do this on every call to GetExprObjectSet().
513 if (auto cleanups = clang::dyn_cast<clang::ExprWithCleanups>(ret_expr)) {
514 ret_expr = cleanups->getSubExpr();
515 }
516
517 ObjectSet expr_points_to = points_to_map_.GetExprObjectSet(ret_expr);
Luca Versarid1b2e692023-01-18 13:07:25 -0800518 GenerateConstraintsForAssignment(
519 {object_repository_.GetReturnObject()}, expr_points_to, return_type,
520 object_repository_, points_to_map_, constraints_);
Luca Versari99fddff2022-05-25 10:22:32 -0700521 return std::nullopt;
522}
523
524std::optional<std::string> TransferStmtVisitor::VisitDeclStmt(
525 const clang::DeclStmt* decl_stmt) {
526 for (const clang::Decl* decl : decl_stmt->decls()) {
527 if (const auto* var_decl = clang::dyn_cast<clang::VarDecl>(decl)) {
Martin Brænne46b5f072022-07-01 02:29:16 -0700528 const Object* var_object = object_repository_.GetDeclObject(var_decl);
Luca Versari99fddff2022-05-25 10:22:32 -0700529
530 // Don't need to record initializers because initialization has already
531 // happened in VisitCXXConstructExpr(), VisitInitListExpr(), or
532 // VisitCallExpr().
533 if (var_decl->hasInit() && !var_decl->getType()->isRecordType()) {
Martin Brænne2a1b27a2022-07-01 06:17:32 -0700534 TransferInitializer(var_object, var_decl->getType(), object_repository_,
Luca Versariefeaf272023-01-16 10:19:28 -0800535 var_decl->getInit(), TargetPointeeBehavior::kIgnore,
536 points_to_map_, constraints_);
Luca Versari99fddff2022-05-25 10:22:32 -0700537 }
538 }
539 }
540 return std::nullopt;
541}
542
543std::optional<std::string> TransferStmtVisitor::VisitUnaryOperator(
544 const clang::UnaryOperator* op) {
545 if (!op->isGLValue() && !op->getType()->isPointerType() &&
546 !op->getType()->isArrayType()) {
547 return std::nullopt;
548 }
549
550 ObjectSet sub_points_to = points_to_map_.GetExprObjectSet(op->getSubExpr());
551
552 // Maybe surprisingly, the code here doesn't do any actual address-taking or
553 // dereferencing.
554 // This is because AddrOf and Deref really only do a reinterpretation:
555 // - AddrOf reinterprets a glvalue of type T as a prvalue of type T*
556 // - Deref reinterprets an prvalue of type T* as a glvalue of type T
557 // (See also the assertions below.)
558 // The actual dereferencing happens in the LValueToRValue CastExpr,
559 // see TransferCastExpr().
560
561 switch (op->getOpcode()) {
562 case clang::UO_AddrOf:
563 assert(!op->isGLValue());
564 assert(op->getSubExpr()->isGLValue());
565 points_to_map_.SetExprObjectSet(op, sub_points_to);
566 break;
567
568 case clang::UO_Deref:
569 assert(op->isGLValue());
570 assert(!op->getSubExpr()->isGLValue());
571 points_to_map_.SetExprObjectSet(op, sub_points_to);
572 break;
573
574 case clang::UO_PostInc:
575 case clang::UO_PostDec:
576 assert(!op->isGLValue());
577 assert(op->getSubExpr()->isGLValue());
578 points_to_map_.SetExprObjectSet(
579 op, points_to_map_.GetPointerPointsToSet(sub_points_to));
580 break;
581
582 case clang::UO_PreInc:
583 case clang::UO_PreDec:
584 assert(op->isGLValue());
585 assert(op->getSubExpr()->isGLValue());
586 points_to_map_.SetExprObjectSet(op, sub_points_to);
587 break;
588
589 default:
590 break;
591 }
592 return std::nullopt;
593}
594
595std::optional<std::string> TransferStmtVisitor::VisitArraySubscriptExpr(
596 const clang::ArraySubscriptExpr* subscript) {
597 // For our purposes here, a subscripting operation is equivalent to a
598 // dereference on its base - we don't make a distinction between different
599 // lifetimes in an array. This effectively merges the points-to sets of all
Luca Versarie5f66ad2022-08-02 02:18:15 -0700600 // elements in the array. See [/docs/lifetimes_static_analysis.md](/docs/lifetimes_static_analysis.md) for why we
Luca Versari99fddff2022-05-25 10:22:32 -0700601 // don't track individual array elements.
602
603 ObjectSet sub_points_to =
604 points_to_map_.GetExprObjectSet(subscript->getBase());
605
606 assert(subscript->isGLValue());
607 assert(!subscript->getBase()->isGLValue());
608 points_to_map_.SetExprObjectSet(subscript, sub_points_to);
609 return std::nullopt;
610}
611
612std::optional<std::string> TransferStmtVisitor::VisitBinaryOperator(
613 const clang::BinaryOperator* op) {
614 switch (op->getOpcode()) {
615 case clang::BO_Assign: {
616 assert(op->getLHS()->isGLValue());
617 ObjectSet lhs_points_to = points_to_map_.GetExprObjectSet(op->getLHS());
618 points_to_map_.SetExprObjectSet(op, lhs_points_to);
619 // Because of how we handle reference-like structs, a member access to a
620 // non-reference-like field in a struct might still produce lifetimes. We
621 // don't want to change points-to sets in those cases.
622 if (!op->getLHS()->getType()->isPointerType()) break;
623 ObjectSet rhs_points_to = points_to_map_.GetExprObjectSet(op->getRHS());
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700624 // We can overwrite (instead of extend) the destination points-to-set
625 // only in very specific circumstances:
626 // - We need to know unambiguously what the LHS refers to, so that we
627 // know we're definitely writing to a particular object, and
Luca Versari53a2f582023-01-16 10:09:29 -0800628 // - That destination object needs to be "single-valued" (see docstring of
629 // LifetimeLattice::SingleValuedObjects for the definition of this
630 // term).
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700631 if (lhs_points_to.size() == 1 &&
Luca Versari53a2f582023-01-16 10:09:29 -0800632 single_valued_objects_.Contains(*lhs_points_to.begin())) {
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700633 // Replacing the points-to-set entirely does not generate any
634 // constraints.
635 points_to_map_.SetPointerPointsToSet(lhs_points_to, rhs_points_to);
636 } else {
637 HandlePointsToSetExtension(lhs_points_to, rhs_points_to,
638 op->getLHS()->getType(), object_repository_,
639 points_to_map_, constraints_);
Luca Versari99fddff2022-05-25 10:22:32 -0700640 }
641 break;
642 }
643
644 case clang::BO_Add:
645 case clang::BO_Sub: {
646 // Pointer arithmetic.
647 // We are only interested in the case in which exactly one of the two
648 // operands is a pointer (in particular we want to exclude int* - int*).
649 if (op->getLHS()->getType()->isPointerType() ^
650 op->getRHS()->getType()->isPointerType()) {
651 if (op->getLHS()->getType()->isPointerType()) {
652 points_to_map_.SetExprObjectSet(
653 op, points_to_map_.GetExprObjectSet(op->getLHS()));
654 } else {
655 points_to_map_.SetExprObjectSet(
656 op, points_to_map_.GetExprObjectSet(op->getRHS()));
657 }
658 }
659 break;
660 }
661
662 default:
663 break;
664 }
665 return std::nullopt;
666}
667
668std::optional<std::string> TransferStmtVisitor::VisitConditionalOperator(
669 const clang::ConditionalOperator* op) {
670 clang::QualType type = op->getType().getCanonicalType();
671
672 if (op->isGLValue() || type->isPointerType()) {
Kinuko Yasuda45fd4be2023-05-03 02:11:57 -0700673 // It is possible that either of the expressions may not have an ObjectSet
674 // if the node is pruned as it is considered unreachable.
675 assert(points_to_map_.ExprHasObjectSet(op->getTrueExpr()) ||
676 points_to_map_.ExprHasObjectSet(op->getFalseExpr()));
Luca Versari99fddff2022-05-25 10:22:32 -0700677 ObjectSet points_to_true =
Kinuko Yasuda45fd4be2023-05-03 02:11:57 -0700678 points_to_map_.ExprHasObjectSet(op->getTrueExpr())
679 ? points_to_map_.GetExprObjectSet(op->getTrueExpr())
680 : ObjectSet();
Luca Versari99fddff2022-05-25 10:22:32 -0700681 ObjectSet points_to_false =
Kinuko Yasuda45fd4be2023-05-03 02:11:57 -0700682 points_to_map_.ExprHasObjectSet(op->getFalseExpr())
683 ? points_to_map_.GetExprObjectSet(op->getFalseExpr())
684 : ObjectSet();
Luca Versari99fddff2022-05-25 10:22:32 -0700685 points_to_map_.SetExprObjectSet(op, points_to_true.Union(points_to_false));
686 }
687 return std::nullopt;
688}
689
690std::optional<std::string> TransferStmtVisitor::VisitInitListExpr(
691 const clang::InitListExpr* init_list) {
692 if (init_list->isSyntacticForm()) {
693 // We are only interested in the semantic form, which is fully realized,
694 // and is the one considered to be the initializer.
695 return std::nullopt;
696 }
697 if (IsInitExprInitializingARecordObject(init_list)) {
698 if (init_list->isTransparent()) {
699 // A transparent initializer list does nothing, the actual initializer
700 // terminating expression is within, and has already transferred lifetimes
701 // up to the object being initialized.
702 return std::nullopt;
703 }
704 // The object set for each field should be pointing to the initializers.
Martin Brænned7c0d0b2022-07-01 05:43:00 -0700705 const Object* init_object =
706 object_repository_.GetInitializedObject(init_list);
Martin Brænne2a1b27a2022-07-01 06:17:32 -0700707 TransferInitializer(init_object, init_list->getType(), object_repository_,
Luca Versariefeaf272023-01-16 10:19:28 -0800708 init_list, TargetPointeeBehavior::kKeep, points_to_map_,
709 constraints_);
Luca Versari99fddff2022-05-25 10:22:32 -0700710 } else {
711 // If the InitListExpr is not initializing a record object, we assume it's
712 // initializing an array or a reference and hence associate the InitListExpr
713 // with the union of the points-to sets of the initializers (as the analysis
714 // is array-insensitive).
715 ObjectSet targets;
716 for (clang::Expr* expr : init_list->inits()) {
717 // If we are constructing an initializer list of non-pointer types, we
718 // don't need to do anything here. Note that initializer list elements
719 // must all have the same type in this case.
720 if (PointeeType(expr->getType()).isNull() && !expr->isGLValue()) {
721 return std::nullopt;
722 }
723 targets.Add(points_to_map_.GetExprObjectSet(expr));
724 }
725 points_to_map_.SetExprObjectSet(init_list, std::move(targets));
726 }
727 return std::nullopt;
728}
729
730std::optional<std::string> TransferStmtVisitor::VisitMaterializeTemporaryExpr(
731 const clang::MaterializeTemporaryExpr* temporary_expr) {
Martin Brænned7c0d0b2022-07-01 05:43:00 -0700732 const Object* temp_object =
733 object_repository_.GetTemporaryObject(temporary_expr);
Luca Versari99fddff2022-05-25 10:22:32 -0700734 points_to_map_.SetExprObjectSet(temporary_expr, {temp_object});
735 return std::nullopt;
736}
737
738std::optional<std::string> TransferStmtVisitor::VisitMemberExpr(
739 const clang::MemberExpr* member) {
740 ObjectSet struct_points_to =
741 points_to_map_.GetExprObjectSet(member->getBase());
742
743 if (const auto* method =
744 clang::dyn_cast<clang::CXXMethodDecl>(member->getMemberDecl())) {
745 // It doesn't really make sense to associate an object set with a non-static
746 // member function.
747 // If the member function is being called, we're not interested in its
748 // "value" anyway. If the non-static member function is used outside of a
749 // function call, then, it's a pointer-to-member, but those aren't
750 // really pointers anyway, and we'll need special treatment for them.
751 if (method->isStatic()) {
752 points_to_map_.SetExprObjectSet(
753 member, {object_repository_.GetDeclObject(method)});
754 }
755 return std::nullopt;
756 }
757
758 auto field = clang::dyn_cast<clang::FieldDecl>(member->getMemberDecl());
759 if (field == nullptr) {
760 llvm::report_fatal_error("indirect member access is not supported yet");
761 }
762 ObjectSet expr_points_to =
763 object_repository_.GetFieldObject(struct_points_to, field);
764 if (field->getType()->isReferenceType()) {
765 expr_points_to = points_to_map_.GetPointerPointsToSet(expr_points_to);
766 }
767 points_to_map_.SetExprObjectSet(member, expr_points_to);
768 return std::nullopt;
769}
770
771std::optional<std::string> TransferStmtVisitor::VisitCXXThisExpr(
772 const clang::CXXThisExpr* this_expr) {
Martin Brænned7c0d0b2022-07-01 05:43:00 -0700773 std::optional<const Object*> this_object = object_repository_.GetThisObject();
Luca Versari99fddff2022-05-25 10:22:32 -0700774 assert(this_object.has_value());
775 points_to_map_.SetExprObjectSet(this_expr, ObjectSet{this_object.value()});
776 return std::nullopt;
777}
778
Luca Versariefeaf272023-01-16 10:19:28 -0800779namespace {
780void ConstrainFunctionLifetimesForCall(
781 const FunctionLifetimes& callee_lifetimes,
782 const FunctionLifetimes& placeholder_lifetimes,
783 LifetimeConstraints& constraints) {
784 // We handle function calls as follows:
785 // - We create a placeholder FunctionLifetimes for each call location, meant
786 // to indicate the concrete lifetimes that the callee is instantiated with for
787 // that specific call.
788 // - When we analyze the call, we constrain the concrete lifetimes so that
789 // they are compatible with the lifetimes of the arguments.
790 // - We then constrain the placeholder lifetimes so that the actual callee
791 // could be used in place of the placeholder callee.
792 // - As a consequence, the lifetimes of the return object are also constrained
793 // correctly (ie. in such a way that they are compatible with the callee and
794 // the call arguments).
795 // TODO(veluca): this code assumes that the lifetime of variables cannot
796 // change across function call boundaries. It is not an intrinsic limitation
797 // (could potentially be resolved by updating the PointsToMap after the fact,
798 // or - even better - by converting the entire CFG into SSA form), but for
799 // simplicity we are leaving things as they are for now; if real-world usage
800 // shows this to be an important limitation, we should revisit this decision.
801 constraints.join(LifetimeConstraints::ForCallableSubstitutionFull(
802 callee_lifetimes, placeholder_lifetimes));
803}
804} // namespace
805
Luca Versari99fddff2022-05-25 10:22:32 -0700806std::optional<std::string> TransferStmtVisitor::VisitCallExpr(
807 const clang::CallExpr* call) {
Luca Versari50824882023-01-23 05:51:35 -0800808 struct CalleeInfo {
809 bool is_member_operator;
810 FunctionLifetimes lifetimes;
811 // Type of the function being called. Note that this might not know anything
812 // about the `this` argument for non-static methods.
813 const clang::FunctionProtoType* type;
814 };
815
816 llvm::SmallVector<CalleeInfo, 1> callees;
817
818 auto add_callee_from_decl =
819 [&callees, call,
820 this](const clang::FunctionDecl* decl) -> std::optional<std::string> {
Luca Versari50824882023-01-23 05:51:35 -0800821 const FunctionLifetimesOrError& callee_lifetimes_or_error =
Luca Versaria12c9d52023-02-23 08:40:48 -0800822 GetFunctionLifetimes(decl, callee_lifetimes_);
Luca Versari50824882023-01-23 05:51:35 -0800823
824 if (!std::holds_alternative<FunctionLifetimes>(callee_lifetimes_or_error)) {
Kinuko Yasuda45fd4be2023-05-03 02:11:57 -0700825 // Note: It is possible that this does not have an entry if the function
826 // is not analyzed (because its body is not defined in this TU). If this
827 // happens, we currently bail without analyzing further.
Luca Versari50824882023-01-23 05:51:35 -0800828 return "No lifetimes for callee '" + decl->getNameAsString() + "': " +
829 std::get<FunctionAnalysisError>(callee_lifetimes_or_error).message;
830 }
831 FunctionLifetimes callee_lifetimes =
832 std::get<FunctionLifetimes>(callee_lifetimes_or_error);
833
834 bool is_member_operator = clang::isa<clang::CXXOperatorCallExpr>(call) &&
835 clang::isa<clang::CXXMethodDecl>(decl);
836 callees.push_back(
837 CalleeInfo{is_member_operator, callee_lifetimes,
838 decl->getType()->getAs<clang::FunctionProtoType>()});
839 return std::nullopt;
840 };
Luca Versari99fddff2022-05-25 10:22:32 -0700841
842 const clang::FunctionDecl* direct_callee = call->getDirectCallee();
843 if (direct_callee) {
844 // This code path is needed for non-static member functions, as those don't
845 // have an `Object` for their callees.
Luca Versari50824882023-01-23 05:51:35 -0800846 if (auto err = add_callee_from_decl(direct_callee); err.has_value()) {
847 return err;
848 }
Luca Versari99fddff2022-05-25 10:22:32 -0700849 } else {
850 const clang::Expr* callee = call->getCallee();
851 for (const auto& object : points_to_map_.GetExprObjectSet(callee)) {
Luca Versaria12c9d52023-02-23 08:40:48 -0800852 if (const std::optional<FunctionLifetimes>& func_lifetimes =
853 object->GetFuncLifetimes();
854 func_lifetimes.has_value()) {
Luca Versari50824882023-01-23 05:51:35 -0800855 callees.push_back(
856 {.is_member_operator = false,
857 .lifetimes = *func_lifetimes,
858 .type = object->Type()->getAs<clang::FunctionProtoType>()});
Luca Versariefeaf272023-01-16 10:19:28 -0800859 }
Luca Versari99fddff2022-05-25 10:22:32 -0700860 }
861 }
862
Luca Versari50824882023-01-23 05:51:35 -0800863 for (const CalleeInfo& callee : callees) {
Luca Versariefeaf272023-01-16 10:19:28 -0800864 ConstrainFunctionLifetimesForCall(
Luca Versari50824882023-01-23 05:51:35 -0800865 callee.lifetimes, object_repository_.GetCallExprVirtualLifetimes(call),
Luca Versariefeaf272023-01-16 10:19:28 -0800866 constraints_);
Luca Versari99fddff2022-05-25 10:22:32 -0700867
Luca Versari50824882023-01-23 05:51:35 -0800868 for (size_t i = callee.is_member_operator ? 1 : 0; i < call->getNumArgs();
869 i++) {
Luca Versari99fddff2022-05-25 10:22:32 -0700870 // We can't just use SetPointerPointsToSet here because call->getArg(i)
871 // might not have an ObjectSet (for example for integer constants); it
872 // also may be needed for struct initialization.
873 // Note that we don't need to worry about possibly extending the
874 // PointsToSet more than needed, as dataflow analysis relies on points-to
875 // sets never shrinking.
876 TransferInitializer(
Martin Brænne2a1b27a2022-07-01 06:17:32 -0700877 object_repository_.GetCallExprArgumentObject(call, i),
Luca Versari50824882023-01-23 05:51:35 -0800878 callee.type->getParamType(callee.is_member_operator ? i - 1 : i),
Luca Versariefeaf272023-01-16 10:19:28 -0800879 object_repository_, call->getArg(i), TargetPointeeBehavior::kKeep,
880 points_to_map_, constraints_);
Luca Versari99fddff2022-05-25 10:22:32 -0700881 }
Luca Versariefeaf272023-01-16 10:19:28 -0800882
883 std::optional<ObjectSet> this_object_set;
Luca Versari50824882023-01-23 05:51:35 -0800884 if (callee.is_member_operator) {
Luca Versariefeaf272023-01-16 10:19:28 -0800885 this_object_set = points_to_map_.GetExprObjectSet(call->getArg(0));
886 } else if (const auto* member_call =
887 clang::dyn_cast<clang::CXXMemberCallExpr>(call)) {
888 this_object_set = points_to_map_.GetExprObjectSet(
889 member_call->getImplicitObjectArgument());
Luca Versari99fddff2022-05-25 10:22:32 -0700890 }
Luca Versariefeaf272023-01-16 10:19:28 -0800891 if (this_object_set.has_value()) {
892 const Object* this_ptr = object_repository_.GetCallExprThisPointer(call);
893 HandlePointsToSetExtension({this_ptr}, *this_object_set, this_ptr->Type(),
894 object_repository_, points_to_map_,
895 constraints_);
Luca Versari99fddff2022-05-25 10:22:32 -0700896 }
897 }
898
Luca Versariefeaf272023-01-16 10:19:28 -0800899 if (IsInitExprInitializingARecordObject(call)) {
900 const Object* init_object = object_repository_.GetInitializedObject(call);
901 GenerateConstraintsForObjectLifetimeEquality(
902 {init_object}, {object_repository_.GetCallExprRetObject(call)},
903 init_object->Type(), points_to_map_, object_repository_, constraints_);
904 } else {
905 ObjectSet ret_pts = points_to_map_.GetPointerPointsToSet(
906 object_repository_.GetCallExprRetObject(call));
907 // SetExprObjectSet will assert-fail if `call` does not have a type that can
908 // have an object set; this `if` guards against that.
909 if (!ret_pts.empty()) {
910 points_to_map_.SetExprObjectSet(call, ret_pts);
911 }
Luca Versari99fddff2022-05-25 10:22:32 -0700912 }
913 return std::nullopt;
914}
915
916std::optional<std::string> TransferStmtVisitor::VisitCXXConstructExpr(
917 const clang::CXXConstructExpr* construct_expr) {
918 const clang::CXXConstructorDecl* constructor =
919 construct_expr->getConstructor();
920
921 assert(callee_lifetimes_.count(constructor->getCanonicalDecl()));
922 const FunctionLifetimesOrError& callee_lifetimes_or_error =
923 callee_lifetimes_.lookup(constructor->getCanonicalDecl());
924 if (!std::holds_alternative<FunctionLifetimes>(callee_lifetimes_or_error)) {
925 return "No lifetimes for constructor " + constructor->getNameAsString();
926 }
927 const FunctionLifetimes& callee_lifetimes =
928 std::get<FunctionLifetimes>(callee_lifetimes_or_error);
929
Luca Versariefeaf272023-01-16 10:19:28 -0800930 ConstrainFunctionLifetimesForCall(
931 callee_lifetimes,
932 object_repository_.GetCallExprVirtualLifetimes(construct_expr),
933 constraints_);
934
Luca Versari99fddff2022-05-25 10:22:32 -0700935 // We check <= instead of == because of default arguments.
936 assert(construct_expr->getNumArgs() <= constructor->getNumParams());
937
938 for (size_t i = 0; i < construct_expr->getNumArgs(); i++) {
Martin Brænne2a1b27a2022-07-01 06:17:32 -0700939 TransferInitializer(
940 object_repository_.GetCXXConstructExprArgumentObject(construct_expr, i),
Luca Versariefeaf272023-01-16 10:19:28 -0800941 constructor->getParamDecl(i)->getType(), object_repository_,
942 construct_expr->getArg(i), TargetPointeeBehavior::kKeep, points_to_map_,
943 constraints_);
Luca Versari99fddff2022-05-25 10:22:32 -0700944 }
945
946 // Handle the `this` parameter, which should point to the object getting
947 // initialized.
Luca Versariefeaf272023-01-16 10:19:28 -0800948 HandlePointsToSetExtension(
949 {object_repository_.GetCXXConstructExprThisPointer(construct_expr)},
950 {object_repository_.GetInitializedObject(construct_expr)},
951 constructor->getThisType(), object_repository_, points_to_map_,
952 constraints_);
Luca Versari99fddff2022-05-25 10:22:32 -0700953
Luca Versari99fddff2022-05-25 10:22:32 -0700954 return std::nullopt;
955}
956
957} // namespace
958
959} // namespace lifetimes
960} // namespace tidy
961} // namespace clang