blob: 35af397150197e101d5ba5b8dae715cbd9c5d674 [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"
21#include "lifetime_analysis/visit_lifetimes.h"
22#include "lifetime_annotations/function_lifetimes.h"
23#include "lifetime_annotations/lifetime.h"
24#include "lifetime_annotations/pointee_type.h"
25#include "lifetime_annotations/type_lifetimes.h"
26#include "clang/AST/Decl.h"
27#include "clang/AST/DeclCXX.h"
28#include "clang/AST/Expr.h"
29#include "clang/AST/ExprCXX.h"
30#include "clang/AST/OperationKinds.h"
31#include "clang/AST/Stmt.h"
32#include "clang/AST/StmtVisitor.h"
33#include "clang/AST/TemplateBase.h"
34#include "clang/AST/Type.h"
Wei Yi Teedae0e822022-09-20 15:03:43 -070035#include "clang/Analysis/CFG.h"
Luca Versari99fddff2022-05-25 10:22:32 -070036#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
37#include "llvm/ADT/ArrayRef.h"
38#include "llvm/ADT/DenseMap.h"
39#include "llvm/ADT/Optional.h"
40#include "llvm/Support/Error.h"
41#include "llvm/Support/ErrorHandling.h"
42
43namespace clang {
44namespace tidy {
45namespace lifetimes {
46
47namespace {
48
49class TransferStmtVisitor
50 : public clang::StmtVisitor<TransferStmtVisitor,
51 std::optional<std::string>> {
52 public:
53 TransferStmtVisitor(
54 ObjectRepository& object_repository, PointsToMap& points_to_map,
Luca Versari1f9fc2e2022-08-17 07:06:00 -070055 LifetimeConstraints& constraints, const clang::FunctionDecl* func,
Luca Versari99fddff2022-05-25 10:22:32 -070056 const llvm::DenseMap<const clang::FunctionDecl*,
57 FunctionLifetimesOrError>& callee_lifetimes,
58 const DiagnosticReporter& diag_reporter)
59 : object_repository_(object_repository),
60 points_to_map_(points_to_map),
Luca Versari1f9fc2e2022-08-17 07:06:00 -070061 constraints_(constraints),
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 Versari99fddff2022-05-25 10:22:32 -070097 const clang::FunctionDecl* func_;
98 const llvm::DenseMap<const clang::FunctionDecl*, FunctionLifetimesOrError>&
99 callee_lifetimes_;
100 const DiagnosticReporter& diag_reporter_;
101};
102
Luca Versari5a414a22022-10-05 03:35:56 -0700103void GenerateConstraintsForAssignmentNonRecursive(
104 const ObjectSet& old_pointees, const ObjectSet& new_pointees,
105 bool is_in_invariant_context, LifetimeConstraints& constraints) {
106 // The new pointees must always outlive the old pointees.
107 for (const Object* old : old_pointees) {
108 for (const Object* newp : new_pointees) {
109 constraints.AddOutlivesConstraint(old->GetLifetime(),
110 newp->GetLifetime());
111 }
112 }
113
114 // If we are in an invariant context, we need to insert constraints in the
115 // opposite direction too (i.e. we need equality).
116 if (is_in_invariant_context) {
117 for (const Object* old : old_pointees) {
118 for (const Object* newp : new_pointees) {
119 constraints.AddOutlivesConstraint(newp->GetLifetime(),
120 old->GetLifetime());
121 }
122 }
123 }
124}
125
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700126// TODO(veluca): this is quadratic.
Luca Versari5a414a22022-10-05 03:35:56 -0700127void GenerateConstraintsForAssignmentRecursive(
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700128 const ObjectSet& pointers, const ObjectSet& new_pointees,
129 clang::QualType pointer_type, const ObjectRepository& object_repository,
Luca Versaric6965ec2022-09-02 02:51:33 -0700130 const PointsToMap& points_to_map, bool is_in_invariant_context,
131 LifetimeConstraints& constraints,
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700132 llvm::DenseSet<std::pair<const Object*, const Object*>>& seen_pairs) {
133 // Check for cycles.
134 {
135 size_t num_seen_pairs = seen_pairs.size();
136 for (auto pointer : pointers) {
137 for (auto pointee : new_pointees) {
138 seen_pairs.insert({pointer, pointee});
139 }
140 }
141 // All done: all the pairs we have were already seen.
142 if (num_seen_pairs == seen_pairs.size()) return;
143 }
144
Luca Versaricd257d32022-08-22 02:09:25 -0700145 if (pointer_type->isIncompleteType()) {
146 // Nothing we *can* do.
147 return;
148 }
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700149 assert(!pointer_type->isRecordType());
150 if (!pointer_type->isPointerType() && !pointer_type->isReferenceType()) {
151 // Nothing to do.
152 return;
153 }
154
155 ObjectSet old_pointees = points_to_map.GetPointerPointsToSet(pointers);
156
Luca Versari5a414a22022-10-05 03:35:56 -0700157 GenerateConstraintsForAssignmentNonRecursive(
158 old_pointees, new_pointees, is_in_invariant_context, constraints);
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700159
Luca Versaric6965ec2022-09-02 02:51:33 -0700160 // See https://doc.rust-lang.org/nomicon/subtyping.html for an explanation of
161 // variance; here in particular, we use the fact that the pointee of a pointer
Luca Versaric0843152022-09-16 02:10:25 -0700162 // is covariant if the pointer points to a const-qualified type, and invariant
163 // otherwise.
164 is_in_invariant_context = !pointer_type->getPointeeType().isConstQualified();
Luca Versaric6965ec2022-09-02 02:51:33 -0700165
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700166 // Recurse in pointees. As the pointee might be of struct type, we need first
167 // to extract all field pointers from it.
168 struct RecursiveVisitInfo {
169 clang::QualType type;
170 ObjectSet old_pointees;
171 ObjectSet new_pointees;
172 };
173
174 std::vector<RecursiveVisitInfo> calls_to_make;
175 calls_to_make.push_back(
176 {pointer_type->getPointeeType(), old_pointees, new_pointees});
177 while (!calls_to_make.empty()) {
178 RecursiveVisitInfo call = std::move(calls_to_make.back());
179 calls_to_make.pop_back();
180
Luca Versaricd257d32022-08-22 02:09:25 -0700181 if (call.type->isIncompleteType()) {
182 // Nothing we *can* do.
183 continue;
184 }
185
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700186 if (const auto* record_type = call.type->getAs<clang::RecordType>()) {
187 for (auto field : record_type->getDecl()->fields()) {
188 calls_to_make.push_back(
189 {field->getType(),
Luca Versari187176a2022-09-02 02:42:23 -0700190 object_repository.GetFieldObject(call.old_pointees, field),
191 object_repository.GetFieldObject(call.new_pointees, field)});
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700192 }
193 if (auto* cxxrecord =
194 clang::dyn_cast<clang::CXXRecordDecl>(record_type->getDecl())) {
195 for (const clang::CXXBaseSpecifier& base : cxxrecord->bases()) {
196 calls_to_make.push_back({base.getType(),
197 object_repository.GetBaseClassObject(
Luca Versari187176a2022-09-02 02:42:23 -0700198 call.old_pointees, base.getType()),
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700199 object_repository.GetBaseClassObject(
Luca Versari187176a2022-09-02 02:42:23 -0700200 call.new_pointees, base.getType())});
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700201 }
202 }
203 } else {
Luca Versari5a414a22022-10-05 03:35:56 -0700204 GenerateConstraintsForAssignmentRecursive(
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700205 call.old_pointees,
206 points_to_map.GetPointerPointsToSet(call.new_pointees), call.type,
Luca Versaric6965ec2022-09-02 02:51:33 -0700207 object_repository, points_to_map, is_in_invariant_context,
208 constraints, seen_pairs);
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700209 }
210 }
211}
212
Luca Versari187176a2022-09-02 02:42:23 -0700213void GenerateConstraintsForAssignment(const ObjectSet& pointers,
214 const ObjectSet& new_pointees,
215 clang::QualType pointer_type,
216 const ObjectRepository& object_repository,
217 PointsToMap& points_to_map,
218 LifetimeConstraints& constraints) {
219 llvm::DenseSet<std::pair<const Object*, const Object*>> seen_pairs;
Luca Versaric6965ec2022-09-02 02:51:33 -0700220 // Outer-most pointers are never invariant.
Luca Versari5a414a22022-10-05 03:35:56 -0700221 GenerateConstraintsForAssignmentRecursive(
Luca Versaric6965ec2022-09-02 02:51:33 -0700222 pointers, new_pointees, pointer_type, object_repository, points_to_map,
223 /*is_in_invariant_context=*/false, constraints, seen_pairs);
Luca Versari187176a2022-09-02 02:42:23 -0700224}
225
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700226void HandlePointsToSetExtension(const ObjectSet& pointers,
227 const ObjectSet& new_pointees,
228 clang::QualType pointer_type,
229 const ObjectRepository& object_repository,
230 PointsToMap& points_to_map,
231 LifetimeConstraints& constraints) {
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700232 // TODO(veluca): record types should probably not get to this point at all, as
233 // their initialization is done by constructor calls. This currently happens
234 // because of the "synthetic" objects used to handle constructor calls.
235 if (!pointer_type->isRecordType()) {
236 GenerateConstraintsForAssignment(pointers, new_pointees, pointer_type,
237 object_repository, points_to_map,
Luca Versari187176a2022-09-02 02:42:23 -0700238 constraints);
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700239 }
240 for (const Object* pointer : pointers) {
241 points_to_map.ExtendPointerPointsToSet(pointer, new_pointees);
242 }
243}
244
Luca Versari99fddff2022-05-25 10:22:32 -0700245} // namespace
246
Martin Brænne2a1b27a2022-07-01 06:17:32 -0700247void TransferInitializer(const Object* dest, clang::QualType type,
Luca Versari99fddff2022-05-25 10:22:32 -0700248 const ObjectRepository& object_repository,
249 const clang::Expr* init_expr,
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700250 PointsToMap& points_to_map,
251 LifetimeConstraints& constraints) {
Luca Versari99fddff2022-05-25 10:22:32 -0700252 type = type.getCanonicalType();
253 if (type->isArrayType()) {
254 type = type->castAsArrayTypeUnsafe()->getElementType();
255 }
256
257 // Initializer lists are handled one member/field at a time.
258 if (type->isRecordType()) {
259 if (auto init_list_expr = clang::dyn_cast<clang::InitListExpr>(init_expr)) {
260 // We assume that initializers are always the semantic form of
261 // InitListExpr.
262 assert(init_list_expr->isSemanticForm());
263 size_t init = 0;
264 for (auto f : type->getAs<clang::RecordType>()->getDecl()->fields()) {
265 assert(init < init_list_expr->getNumInits());
266 auto field_init = init_list_expr->getInit(init);
267 ++init;
Martin Brænne2a1b27a2022-07-01 06:17:32 -0700268 TransferInitializer(object_repository.GetFieldObject(dest, f),
Luca Versari99fddff2022-05-25 10:22:32 -0700269 f->getType(), object_repository, field_init,
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700270 points_to_map, constraints);
Luca Versari99fddff2022-05-25 10:22:32 -0700271 }
272 return;
273 }
274 }
275
276 if (type->isPointerType() || type->isReferenceType() ||
277 type->isStructureOrClassType()) {
278 ObjectSet init_points_to = points_to_map.GetExprObjectSet(init_expr);
279 // It's important to use "Extend" (not "Set") here because we process
280 // initializers for member variables only _after_ the dataflow analysis has
281 // run.
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700282 HandlePointsToSetExtension({dest}, init_points_to, type, object_repository,
283 points_to_map, constraints);
Luca Versari99fddff2022-05-25 10:22:32 -0700284 }
285}
286
Martin Brænne5d1a5242022-06-30 06:26:28 -0700287namespace {
288
Luca Versari0959cc42022-10-25 02:26:52 -0700289void SetPointerPointsToSetRespectingTypes(
290 const Object* pointer, const ObjectSet& points_to,
291 PointsToMap& points_to_map, clang::ASTContext& ast_context,
292 LifetimeConstraints& constraints,
293 const ObjectRepository& object_repository) {
Martin Brænne4d8cdfd2022-07-01 06:16:36 -0700294 assert(pointer->Type()->isPointerType() ||
295 pointer->Type()->isReferenceType());
Martin Brænne5d1a5242022-06-30 06:26:28 -0700296
Luca Versari0959cc42022-10-25 02:26:52 -0700297 ObjectSet original_points_to_set =
298 points_to_map.GetPointerPointsToSet(pointer);
299
Martin Brænne5d1a5242022-06-30 06:26:28 -0700300 ObjectSet points_to_filtered;
301
302 for (auto object : points_to) {
Martin Brænne4d8cdfd2022-07-01 06:16:36 -0700303 if (MayPointTo(pointer->Type(), object->Type(), ast_context)) {
Martin Brænne5d1a5242022-06-30 06:26:28 -0700304 points_to_filtered.Add(object);
Luca Versari0959cc42022-10-25 02:26:52 -0700305 // To handle flow-sensitiveness, we don't generate any constraints when
306 // replacing the points-to-set of a variable that is single-valued.
307 // TODO(veluca): explicitly splitting between "object values before an
308 // expression" and "object values after an expression" will likely make
309 // this whole kind of reasoning clearer and less error-prone.
310 if (object_repository.GetObjectValueType(pointer) !=
311 ObjectRepository::ObjectValueType::kSingleValued) {
312 // Descending in callees is handled at a higher level.
313 GenerateConstraintsForAssignmentNonRecursive(
314 original_points_to_set, {object},
315 /*is_in_invariant_context=*/
316 !PointeeType(pointer->Type()).isConstQualified(), constraints);
317 }
Martin Brænne5d1a5242022-06-30 06:26:28 -0700318 }
319 }
320
321 points_to_map.SetPointerPointsToSet(pointer, points_to_filtered);
322}
323
Luca Versari0959cc42022-10-25 02:26:52 -0700324void SetAllPointersPointsToSetRespectingTypes(
325 const ObjectSet& pointers, const ObjectSet& points_to,
326 PointsToMap& points_to_map, clang::ASTContext& ast_context,
327 LifetimeConstraints& constraints,
328 const ObjectRepository& object_repository) {
Martin Brænne5d1a5242022-06-30 06:26:28 -0700329 for (auto pointer : pointers) {
330 SetPointerPointsToSetRespectingTypes(pointer, points_to, points_to_map,
Luca Versari0959cc42022-10-25 02:26:52 -0700331 ast_context, constraints,
332 object_repository);
Martin Brænne5d1a5242022-06-30 06:26:28 -0700333 }
334}
335
336void CollectLifetimes(
Martin Brænne05818552022-07-01 06:06:13 -0700337 const Object* arg_object, clang::QualType type,
Martin Brænne5d1a5242022-06-30 06:26:28 -0700338 const ValueLifetimes& value_lifetimes, const PointsToMap& points_to_map,
339 const ObjectRepository& object_repository,
Martin Brænneba9a3442022-06-30 07:08:45 -0700340 llvm::DenseMap<Lifetime, ObjectSet>& lifetime_to_object_set) {
Martin Brænne5d1a5242022-06-30 06:26:28 -0700341 class Visitor : public LifetimeVisitor {
342 public:
343 Visitor(const ObjectRepository& object_repository,
344 const PointsToMap& points_to_map,
Martin Brænneba9a3442022-06-30 07:08:45 -0700345 llvm::DenseMap<Lifetime, ObjectSet>& lifetime_to_object_set)
Martin Brænne5d1a5242022-06-30 06:26:28 -0700346 : object_repository_(object_repository),
347 points_to_map_(points_to_map),
Martin Brænneba9a3442022-06-30 07:08:45 -0700348 lifetime_to_object_set_(lifetime_to_object_set) {}
Martin Brænne5d1a5242022-06-30 06:26:28 -0700349
Martin Brænne2c003b62022-07-01 05:45:42 -0700350 const Object* GetFieldObject(const ObjectSet& objects,
351 const clang::FieldDecl* field) override {
Martin Brænne5d1a5242022-06-30 06:26:28 -0700352 // All the objects have the same field.
353 assert(!objects.empty());
Martin Brænne2c003b62022-07-01 05:45:42 -0700354 return object_repository_.GetFieldObject(*objects.begin(), field);
Martin Brænne5d1a5242022-06-30 06:26:28 -0700355 }
356
Martin Brænne2c003b62022-07-01 05:45:42 -0700357 const Object* GetBaseClassObject(const ObjectSet& objects,
358 clang::QualType base) override {
Martin Brænne5d1a5242022-06-30 06:26:28 -0700359 // All the objects have the same base.
360 assert(!objects.empty());
Martin Brænne2c003b62022-07-01 05:45:42 -0700361 return object_repository_.GetBaseClassObject(*objects.begin(), base);
Martin Brænne5d1a5242022-06-30 06:26:28 -0700362 }
363
364 ObjectSet Traverse(const ObjectLifetimes& lifetimes,
365 const ObjectSet& objects,
366 int /*pointee_depth*/) override {
Martin Brænneba9a3442022-06-30 07:08:45 -0700367 lifetime_to_object_set_[lifetimes.GetLifetime()].Add(objects);
Martin Brænne5d1a5242022-06-30 06:26:28 -0700368 return points_to_map_.GetPointerPointsToSet(objects);
369 }
370
371 private:
372 const ObjectRepository& object_repository_;
373 const PointsToMap& points_to_map_;
Martin Brænneba9a3442022-06-30 07:08:45 -0700374 llvm::DenseMap<Lifetime, ObjectSet>& lifetime_to_object_set_;
Martin Brænne5d1a5242022-06-30 06:26:28 -0700375 };
Martin Brænneba9a3442022-06-30 07:08:45 -0700376 Visitor visitor(object_repository, points_to_map, lifetime_to_object_set);
Martin Brænne5d1a5242022-06-30 06:26:28 -0700377 VisitLifetimes({arg_object}, type,
Martin Brænne05818552022-07-01 06:06:13 -0700378 ObjectLifetimes(arg_object->GetLifetime(), value_lifetimes),
Martin Brænne5d1a5242022-06-30 06:26:28 -0700379 visitor);
380}
381
382void PropagateLifetimesToPointees(
Martin Brænne05818552022-07-01 06:06:13 -0700383 const Object* arg_object, clang::QualType type,
Martin Brænne5d1a5242022-06-30 06:26:28 -0700384 const ValueLifetimes& value_lifetimes, PointsToMap& points_to_map,
Luca Versari187176a2022-09-02 02:42:23 -0700385 ObjectRepository& object_repository, LifetimeConstraints& constraints,
Martin Brænneba9a3442022-06-30 07:08:45 -0700386 const llvm::DenseMap<Lifetime, ObjectSet>& lifetime_to_object_set,
Martin Brænne5d1a5242022-06-30 06:26:28 -0700387 clang::ASTContext& ast_context) {
388 class Visitor : public LifetimeVisitor {
389 public:
390 Visitor(ObjectRepository& object_repository, PointsToMap& points_to_map,
Luca Versari187176a2022-09-02 02:42:23 -0700391 LifetimeConstraints& constraints,
Martin Brænneba9a3442022-06-30 07:08:45 -0700392 const llvm::DenseMap<Lifetime, ObjectSet>& lifetime_to_object_set,
Martin Brænne5d1a5242022-06-30 06:26:28 -0700393 clang::ASTContext& ast_context)
394 : object_repository_(object_repository),
395 points_to_map_(points_to_map),
Luca Versari187176a2022-09-02 02:42:23 -0700396 constraints_(constraints),
Martin Brænneba9a3442022-06-30 07:08:45 -0700397 lifetime_to_object_set_(lifetime_to_object_set),
Martin Brænne5d1a5242022-06-30 06:26:28 -0700398 ast_context_(ast_context) {}
399
Martin Brænne2c003b62022-07-01 05:45:42 -0700400 const Object* GetFieldObject(const ObjectSet& objects,
401 const clang::FieldDecl* field) override {
Martin Brænne5d1a5242022-06-30 06:26:28 -0700402 // All the objects have the same field.
403 assert(!objects.empty());
Martin Brænne2c003b62022-07-01 05:45:42 -0700404 return object_repository_.GetFieldObject(*objects.begin(), field);
Martin Brænne5d1a5242022-06-30 06:26:28 -0700405 }
406
Martin Brænne2c003b62022-07-01 05:45:42 -0700407 const Object* GetBaseClassObject(const ObjectSet& objects,
408 clang::QualType base) override {
Martin Brænne5d1a5242022-06-30 06:26:28 -0700409 // All the objects have the same base.
410 assert(!objects.empty());
Martin Brænne2c003b62022-07-01 05:45:42 -0700411 return object_repository_.GetBaseClassObject(*objects.begin(), base);
Martin Brænne5d1a5242022-06-30 06:26:28 -0700412 }
413
414 ObjectSet Traverse(const ObjectLifetimes& lifetimes,
415 const ObjectSet& objects,
416 int /*pointee_depth*/) override {
417 clang::QualType type = lifetimes.GetValueLifetimes().Type();
418 ObjectSet points_to_original =
419 points_to_map_.GetPointerPointsToSet(objects);
420 if (!type.isConstQualified() && !PointeeType(type).isNull()) {
421 Lifetime pointee_lifetime =
422 lifetimes.GetValueLifetimes().GetPointeeLifetimes().GetLifetime();
Martin Brænneba9a3442022-06-30 07:08:45 -0700423 ObjectSet points_to = lifetime_to_object_set_.lookup(pointee_lifetime);
Martin Brænne5d1a5242022-06-30 06:26:28 -0700424 // If this is pointer-to-static, assume the callee can modify it to
425 // point to a static object that we don't know about.
426 if (pointee_lifetime == Lifetime::Static()) {
427 points_to.Add(
428 object_repository_.CreateStaticObject(PointeeType(type)));
429 }
Luca Versari0959cc42022-10-25 02:26:52 -0700430 SetAllPointersPointsToSetRespectingTypes(
431 objects, points_to, points_to_map_, ast_context_, constraints_,
432 object_repository_);
Luca Versaricd257d32022-08-22 02:09:25 -0700433 if (!kUseConstraintBasedAnalysis) {
434 // This assertion may fail to be true when visiting the return value
435 // object, if its objects were created recursively.
436 // TODO(veluca): figure out a way to preserve this assertion in the
437 // presence of fictitious objects.
438 assert(points_to_map_.GetPointerPointsToSet(objects).Contains(
439 points_to_original));
440 }
Luca Versari0959cc42022-10-25 02:26:52 -0700441 // We can't simply call GenerateConstraintsForAssignment here, as the
442 // points_to_map_ has been modified already.
Martin Brænne5d1a5242022-06-30 06:26:28 -0700443 }
444 // Return the original points-to set, not the modified one. The original
445 // points-to set is sufficient because it captures the arguments that
446 // were passed to the function, but it doesn't contain any possibly
447 // spurious edges that may have been inserted by the logic above, which
448 // can reduce the precision of the analysis.
449 return points_to_original;
450 }
451
452 private:
453 ObjectRepository& object_repository_;
454 PointsToMap& points_to_map_;
Luca Versari187176a2022-09-02 02:42:23 -0700455 LifetimeConstraints& constraints_;
Martin Brænneba9a3442022-06-30 07:08:45 -0700456 const llvm::DenseMap<Lifetime, ObjectSet>& lifetime_to_object_set_;
Martin Brænne5d1a5242022-06-30 06:26:28 -0700457 clang::ASTContext& ast_context_;
458 };
Luca Versari187176a2022-09-02 02:42:23 -0700459 Visitor visitor(object_repository, points_to_map, constraints,
460 lifetime_to_object_set, ast_context);
Martin Brænne5d1a5242022-06-30 06:26:28 -0700461 VisitLifetimes({arg_object}, type,
Martin Brænne05818552022-07-01 06:06:13 -0700462 ObjectLifetimes(arg_object->GetLifetime(), value_lifetimes),
Martin Brænne5d1a5242022-06-30 06:26:28 -0700463 visitor);
464}
465
466bool AllStatic(const ValueLifetimes& lifetimes) {
467 return !lifetimes.HasAny([](Lifetime l) { return l != Lifetime::Static(); });
468}
469
470} // namespace
471
472std::optional<ObjectSet> TransferLifetimesForCall(
473 const clang::Expr* call, const std::vector<FunctionParameter>& fn_params,
474 const ValueLifetimes& return_lifetimes, ObjectRepository& object_repository,
Luca Versari187176a2022-09-02 02:42:23 -0700475 PointsToMap& points_to_map, LifetimeConstraints& constraints,
476 clang::ASTContext& ast_context) {
Martin Brænne5d1a5242022-06-30 06:26:28 -0700477 // TODO(mboehme): The following description says what we _want_ to do, but
478 // this isn't what we actually do right now. Modify the code so that it
479 // corresponds to the description, then remove this TODO.
480 //
481 // Overall approach:
482 // - Step 1: Find all objects accessible by the callee.
483 // This means finding all objects transitively accessible from the argument
484 // pointees passed to the callee. As part of this step, we establish a
485 // mapping from callee lifetimes to caller lifetimes, which will be used in
486 // subsequent steps to determine whether a given object (whose lifetime is
487 // a caller lifetime) has a given callee lifetime. Note that, in general, a
488 // single callee lifetime may correspond to multiple caller lifetimes.
489 //
490 // - Step 2: Perform all modifications the callee could make to the points-to
491 // map that are permissible from a lifetime and type system point of view.
492 // Specifically, for every non-const pointer accessible by the callee:
493 // - Determine the callee lifetime 'l associated with that pointer.
494 // - For each object accessible by the callee, determine whether it has
495 // callee lifetime 'l (using the mapping established in step 1) and
496 // and whether the type of the pointer is compatible with the type of the
497 // object. If both of these conditions are met, add an edge from the
498 // pointer to the object into the points-to map.
499 // It remains to be explained what "compatible" means above. The most
500 // principled approach would be to use C++'s strict aliasing rules, but some
501 // real-world code unfortunately violates the strict aliasing rules.
502 // Instead, we make the compatibility rule more permissive than strict
503 // aliasing; we expect we will need some experimentation to achieve a
504 // good tradeoff between the following considerations:
505 // - If we make the compatibility rule too strict, we miss some points-to
506 // edges that may be introduced by real-world code (even though that code
507 // is in violation of the strict aliasing rule), and the analysis result
508 // becomes wrong.
509 // - If we make the compatibility rule too permissive, we allow spurious
510 // edges in the points-to map, and the analysis result becomes overly
511 // restrictive.
512 // We also need to consider that the type returned by Object::Type() might
513 // not be identical to the actual dynamic type of the object. If the object
514 // was passed in to the function through a pointer or reference to class
515 // type, the dynamic type of the object might be a derived class of the
516 // type we assumed for the object.
517 //
518 // - Step 3: Determine points-to set for the return value.
519 // This is the set of all objects accessible by the callee that
520 // - are compatible with the callee's return type, and
521 // - conform to the lifetime annotations on the return type.
522 // The latter point means that every object that is transitively reachable
523 // from the original object has a lifetime that corresponds to the callee
524 // lifetime implied by the annotation.
525 //
526 // Some additional considerations apply if the callee signature contains the
527 // 'static lifetime, either in the parameters or the return value:
528 // - Any objects that are associated with the static lifetime in the callee
529 // must be forced to have static lifetime.
530 // We have no way of doing this directly, as we cannot mutate the lifetime
531 // of the object (and, in any case, such a mutation would be global and not
532 // limited to the current point in the program flow).
533 // Instead, for each such object, we synthesize a pointer with static
534 // lifetime and make it point at the object. Later, in
535 // PropagateStaticToPointees(), this will cause us to assign static lifetime
536 // to the object.
537 // A cleaner solution to this would be to explicitly express "outlives"
538 // constraints in the lattice. This might also help more generally to
539 // simplify the logic associated with static lifetimes, but it would also be
540 // a more invasive change.
541 //
542 // - Any pointer or reference may point to an object of static lifetime. This
543 // has the following implications:
544 // - In step 2, when adding edges to the points-to map, we always add edges
545 // to objects of static lifetime if their type is compatible with the
546 // type of the pointer.
547 // - In step 3, an object of static lifetime conforms to any callee lifetime
548 // if that lifetime occurs in covariant position.
549 //
550 // - The callee may have access to objects of static lifetime that are not
551 // passed as arguments, in addition to the ones that are accessible from the
552 // arguments.
553 // Because of this, for any non-const pointer accessible by the callee, we
554 // add a points-to edge to a newly created static object of the appropriate
555 // type.
556 // This does cause us to add a lot of static objects to the graph that we
557 // do not expect to occur in reality. If this turns out to have undesired
558 // effects, we could use the following alternative approach as a compromise:
559 // - In step 2, if the non-const pointer is associated with static lifetime,
560 // does not already point to an object of static lifetime and would not
561 // gain an edge to an existing object of static lifetime, create a new
562 // object of static lifetime and the appropriate type and add an edge
563 // from the pointer to the newly created object.
564 // - In step 3, if we obtain an empty points-to set for the return value
565 // because the return type contains 'static lifetime annotations and the
566 // existing objects do not conform to these annotations, add newly
567 // created static objects to the points-to map in suitable places so that
568 // we can return a non-empty points-to set.
569 // TODO(mboehme): Investigate whether it's really so bad to add newly
570 // created static objects in all the places they could theoretically occur.
571 // If this turns out not to have any adverse effect on the analysis, it
572 // would be the more principled and simpler thing to do.
573
Martin Brænne239a2212022-06-30 07:07:27 -0700574 assert(call || !return_lifetimes.HasLifetimes());
575
Martin Brænne5d1a5242022-06-30 06:26:28 -0700576 // Step 1: Create mapping from callee lifetimes to points-to sets.
577 llvm::DenseMap<Lifetime, ObjectSet> lifetime_to_object_set;
578 for (auto [type, param_lifetimes, arg_object] : fn_params) {
Martin Brænne05818552022-07-01 06:06:13 -0700579 CollectLifetimes(arg_object, type, param_lifetimes, points_to_map,
Martin Brænne5d1a5242022-06-30 06:26:28 -0700580 object_repository, lifetime_to_object_set);
581 }
582
583 // Force any objects associated with the static lifetime in the callee to have
584 // static lifetime (see more detailed explanation above).
585 if (auto iter = lifetime_to_object_set.find(Lifetime::Static());
586 iter != lifetime_to_object_set.end()) {
Martin Brænne4d8cdfd2022-07-01 06:16:36 -0700587 for (const Object* object : iter->second) {
Martin Brænned7c0d0b2022-07-01 05:43:00 -0700588 const Object* pointer = object_repository.CreateStaticObject(
Martin Brænne4d8cdfd2022-07-01 06:16:36 -0700589 ast_context.getPointerType(object->Type()));
Martin Brænne5d1a5242022-06-30 06:26:28 -0700590 points_to_map.ExtendPointerPointsToSet(pointer, {object});
591 }
592 }
593
594 // Step 2: Propagate points-to sets to output parameters.
595 for (auto [type, param_lifetimes, arg_object] : fn_params) {
Martin Brænne05818552022-07-01 06:06:13 -0700596 PropagateLifetimesToPointees(arg_object, type, param_lifetimes,
Luca Versari187176a2022-09-02 02:42:23 -0700597 points_to_map, object_repository, constraints,
Martin Brænne5d1a5242022-06-30 06:26:28 -0700598 lifetime_to_object_set, ast_context);
599 }
600
601 // Step 3: Determine points-to set for the return value.
602 if (return_lifetimes.HasLifetimes()) {
603 if (IsInitExprInitializingARecordObject(call)) {
Martin Brænned7c0d0b2022-07-01 05:43:00 -0700604 const Object* init_object = object_repository.GetInitializedObject(call);
Martin Brænne5d1a5242022-06-30 06:26:28 -0700605 PropagateLifetimesToPointees(
Martin Brænne05818552022-07-01 06:06:13 -0700606 init_object, call->getType(), return_lifetimes, points_to_map,
Luca Versari187176a2022-09-02 02:42:23 -0700607 object_repository, constraints, lifetime_to_object_set, ast_context);
Martin Brænne5d1a5242022-06-30 06:26:28 -0700608 } else {
609 ObjectSet rval_points_to;
610
611 rval_points_to = lifetime_to_object_set.lookup(
612 return_lifetimes.GetPointeeLifetimes().GetLifetime());
613 // If this return value is a pointer-to-static, assume the callee can
614 // return a static object that we don't know about.
615 if (return_lifetimes.GetPointeeLifetimes().GetLifetime() ==
616 Lifetime::Static()) {
617 bool all_static = AllStatic(return_lifetimes);
618 (void)all_static;
619 assert(all_static);
620 rval_points_to.Add(
621 object_repository.CreateStaticObject(PointeeType(call->getType())));
622 }
623 return rval_points_to;
624 }
625 }
626 return std::nullopt;
627}
628
Luca Versari99fddff2022-05-25 10:22:32 -0700629LifetimeLattice LifetimeAnalysis::initialElement() {
630 return LifetimeLattice(object_repository_.InitialPointsToMap());
631}
632
633std::string LifetimeAnalysis::ToString(const LifetimeLattice& state) {
634 return state.ToString();
635}
636
637bool LifetimeAnalysis::IsEqual(const LifetimeLattice& state1,
638 const LifetimeLattice& state2) {
639 return state1 == state2;
640}
641
Wei Yi Teedae0e822022-09-20 15:03:43 -0700642void LifetimeAnalysis::transfer(const clang::CFGElement* elt,
643 LifetimeLattice& state,
Luca Versari99fddff2022-05-25 10:22:32 -0700644 clang::dataflow::Environment& /*environment*/) {
645 if (state.IsError()) return;
646
Wei Yi Teedae0e822022-09-20 15:03:43 -0700647 auto cfg_stmt = elt->getAs<clang::CFGStmt>();
648 if (!cfg_stmt) return;
649 auto stmt = cfg_stmt->getStmt();
650
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700651 TransferStmtVisitor visitor(object_repository_, state.PointsTo(),
652 state.Constraints(), func_, callee_lifetimes_,
653 diag_reporter_);
Luca Versari99fddff2022-05-25 10:22:32 -0700654 if (std::optional<std::string> err =
655 visitor.Visit(const_cast<clang::Stmt*>(stmt))) {
656 state = LifetimeLattice(*err);
657 }
658}
659
660namespace {
661
662std::optional<std::string> TransferStmtVisitor::VisitExpr(
663 const clang::Expr* expr) {
664 // Ensure that we don't attempt to analyze code that contains errors.
665 // This is triggered by TypoExpr and RecoveryExpr, but rather than handling
666 // these particular expression types individually, we just check
667 // Expr::containsErrors().
668 if (expr->containsErrors()) {
669 return "encountered an expression containing errors";
670 }
671 return std::nullopt;
672}
673
674std::optional<std::string> TransferStmtVisitor::VisitDeclRefExpr(
675 const clang::DeclRefExpr* decl_ref) {
676 auto* decl = decl_ref->getDecl();
677 if (!clang::isa<clang::VarDecl>(decl) &&
678 !clang::isa<clang::FunctionDecl>(decl)) {
679 return std::nullopt;
680 }
681
Martin Brænne46b5f072022-07-01 02:29:16 -0700682 const Object* object = object_repository_.GetDeclObject(decl);
Luca Versari99fddff2022-05-25 10:22:32 -0700683
684 assert(decl_ref->isGLValue() || decl_ref->getType()->isBuiltinType());
685
686 clang::QualType type = decl->getType().getCanonicalType();
687
688 if (type->isReferenceType()) {
689 points_to_map_.SetExprObjectSet(
690 decl_ref, points_to_map_.GetPointerPointsToSet(object));
691 } else {
692 points_to_map_.SetExprObjectSet(decl_ref, {object});
693 }
694
695 return std::nullopt;
696}
697
698std::optional<std::string> TransferStmtVisitor::VisitStringLiteral(
699 const clang::StringLiteral* strlit) {
Martin Brænned7c0d0b2022-07-01 05:43:00 -0700700 const Object* obj = object_repository_.CreateStaticObject(strlit->getType());
Luca Versari99fddff2022-05-25 10:22:32 -0700701 points_to_map_.SetExprObjectSet(strlit, {obj});
702 return std::nullopt;
703}
704
705std::optional<std::string> TransferStmtVisitor::VisitCastExpr(
706 const clang::CastExpr* cast) {
707 switch (cast->getCastKind()) {
708 case clang::CK_LValueToRValue: {
709 if (cast->getType()->isPointerType()) {
710 // Converting from a glvalue to a prvalue means that we need to perform
711 // a dereferencing operation because the objects associated with
712 // glvalues and prvalues have different meanings:
713 // - A glvalue is associated with the object identified by the glvalue.
714 // - A prvalue is only associated with an object if the prvalue is of
715 // pointer type; the object it is associated with is the object the
716 // pointer points to.
717 // See also documentation for PointsToMap.
718 ObjectSet points_to = points_to_map_.GetPointerPointsToSet(
719 points_to_map_.GetExprObjectSet(cast->getSubExpr()));
720 points_to_map_.SetExprObjectSet(cast, points_to);
721 }
722 break;
723 }
724 case clang::CK_NullToPointer: {
725 points_to_map_.SetExprObjectSet(cast, {});
726 break;
727 }
728 // These casts are just no-ops from a Object point of view.
729 case clang::CK_FunctionToPointerDecay:
730 case clang::CK_BuiltinFnToFnPtr:
731 case clang::CK_ArrayToPointerDecay:
732 case clang::CK_UserDefinedConversion:
733 // Note on CK_UserDefinedConversion: The actual conversion happens in a
734 // CXXMemberCallExpr that is a subexpression of this CastExpr. The
735 // CK_UserDefinedConversion is just used to mark the fact that this is a
736 // user-defined conversion; it's therefore a no-op for our purposes.
737 case clang::CK_NoOp: {
738 clang::QualType type = cast->getType().getCanonicalType();
739 if (type->isPointerType() || cast->isGLValue()) {
740 points_to_map_.SetExprObjectSet(
741 cast, points_to_map_.GetExprObjectSet(cast->getSubExpr()));
742 }
743 break;
744 }
745 case clang::CK_DerivedToBase:
746 case clang::CK_UncheckedDerivedToBase:
747 case clang::CK_BaseToDerived:
748 case clang::CK_Dynamic: {
749 // These need to be mapped to what the subexpr points to.
750 // (Simple cases just work okay with this; may need to be revisited when
751 // we add more inheritance support.)
752 ObjectSet points_to = points_to_map_.GetExprObjectSet(cast->getSubExpr());
753 points_to_map_.SetExprObjectSet(cast, points_to);
754 break;
755 }
756 case clang::CK_BitCast:
757 case clang::CK_LValueBitCast:
758 case clang::CK_IntegralToPointer: {
759 // We don't support analyzing functions that perform a reinterpret_cast.
760 diag_reporter_(
761 func_->getBeginLoc(),
762 "cannot infer lifetimes because function uses a type-unsafe cast",
763 clang::DiagnosticIDs::Warning);
764 diag_reporter_(cast->getBeginLoc(), "type-unsafe cast occurs here",
765 clang::DiagnosticIDs::Note);
766 return "type-unsafe cast prevents analysis";
767 }
768 default: {
769 if (cast->isGLValue() ||
770 cast->getType().getCanonicalType()->isPointerType()) {
771 llvm::errs() << "Unknown cast type:\n";
772 cast->dump();
773 // No-noop casts of pointer types are not handled yet.
774 llvm::report_fatal_error("unknown cast type encountered");
775 }
776 }
777 }
778 return std::nullopt;
779}
780
781std::optional<std::string> TransferStmtVisitor::VisitReturnStmt(
782 const clang::ReturnStmt* return_stmt) {
783 clang::QualType return_type = func_->getReturnType();
784 // We only need to handle pointers and references.
785 // For record types, initialization of the return value has already been
786 // handled in VisitCXXConstructExpr() or VisitInitListExpr(), so nothing
787 // to do here.
788 if (!return_type->isPointerType() && !return_type->isReferenceType()) {
789 return std::nullopt;
790 }
791
792 const clang::Expr* ret_expr = return_stmt->getRetValue();
793 // This occurs when computing `ret_expr`s result includes creating temporary
794 // objects with destructors. We want to find the value to be returned inside
795 // the ExprWithCleanups.
796 //
797 // The PointsToMap::GetExprObjectSet() function could do this but it doesn't
798 // understand the context from which it is being called. This operation needs
799 // to be done only in cases where we are leaving scope - that is, the return
800 // statement. And the return statement also needs to look for initializers in
801 // its sub expressions, after looking inside ExprWithCleanups.
802 //
803 // That means GetExprObjectSet() would need to also look for initializers but
804 // we don't want to do this on every call to GetExprObjectSet().
805 if (auto cleanups = clang::dyn_cast<clang::ExprWithCleanups>(ret_expr)) {
806 ret_expr = cleanups->getSubExpr();
807 }
808
809 ObjectSet expr_points_to = points_to_map_.GetExprObjectSet(ret_expr);
Luca Versari187176a2022-09-02 02:42:23 -0700810 if (!kUseConstraintBasedAnalysis) {
811 HandlePointsToSetExtension({object_repository_.GetReturnObject()},
812 expr_points_to, return_type, object_repository_,
813 points_to_map_, constraints_);
814 } else {
815 GenerateConstraintsForAssignment(
816 {object_repository_.GetReturnObject()}, expr_points_to, return_type,
817 object_repository_, points_to_map_, constraints_);
818 }
Luca Versari99fddff2022-05-25 10:22:32 -0700819 return std::nullopt;
820}
821
822std::optional<std::string> TransferStmtVisitor::VisitDeclStmt(
823 const clang::DeclStmt* decl_stmt) {
824 for (const clang::Decl* decl : decl_stmt->decls()) {
825 if (const auto* var_decl = clang::dyn_cast<clang::VarDecl>(decl)) {
Martin Brænne46b5f072022-07-01 02:29:16 -0700826 const Object* var_object = object_repository_.GetDeclObject(var_decl);
Luca Versari99fddff2022-05-25 10:22:32 -0700827
828 // Don't need to record initializers because initialization has already
829 // happened in VisitCXXConstructExpr(), VisitInitListExpr(), or
830 // VisitCallExpr().
831 if (var_decl->hasInit() && !var_decl->getType()->isRecordType()) {
Martin Brænne2a1b27a2022-07-01 06:17:32 -0700832 TransferInitializer(var_object, var_decl->getType(), object_repository_,
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700833 var_decl->getInit(), points_to_map_, constraints_);
Luca Versari99fddff2022-05-25 10:22:32 -0700834 }
835 }
836 }
837 return std::nullopt;
838}
839
840std::optional<std::string> TransferStmtVisitor::VisitUnaryOperator(
841 const clang::UnaryOperator* op) {
842 if (!op->isGLValue() && !op->getType()->isPointerType() &&
843 !op->getType()->isArrayType()) {
844 return std::nullopt;
845 }
846
847 ObjectSet sub_points_to = points_to_map_.GetExprObjectSet(op->getSubExpr());
848
849 // Maybe surprisingly, the code here doesn't do any actual address-taking or
850 // dereferencing.
851 // This is because AddrOf and Deref really only do a reinterpretation:
852 // - AddrOf reinterprets a glvalue of type T as a prvalue of type T*
853 // - Deref reinterprets an prvalue of type T* as a glvalue of type T
854 // (See also the assertions below.)
855 // The actual dereferencing happens in the LValueToRValue CastExpr,
856 // see TransferCastExpr().
857
858 switch (op->getOpcode()) {
859 case clang::UO_AddrOf:
860 assert(!op->isGLValue());
861 assert(op->getSubExpr()->isGLValue());
862 points_to_map_.SetExprObjectSet(op, sub_points_to);
863 break;
864
865 case clang::UO_Deref:
866 assert(op->isGLValue());
867 assert(!op->getSubExpr()->isGLValue());
868 points_to_map_.SetExprObjectSet(op, sub_points_to);
869 break;
870
871 case clang::UO_PostInc:
872 case clang::UO_PostDec:
873 assert(!op->isGLValue());
874 assert(op->getSubExpr()->isGLValue());
875 points_to_map_.SetExprObjectSet(
876 op, points_to_map_.GetPointerPointsToSet(sub_points_to));
877 break;
878
879 case clang::UO_PreInc:
880 case clang::UO_PreDec:
881 assert(op->isGLValue());
882 assert(op->getSubExpr()->isGLValue());
883 points_to_map_.SetExprObjectSet(op, sub_points_to);
884 break;
885
886 default:
887 break;
888 }
889 return std::nullopt;
890}
891
892std::optional<std::string> TransferStmtVisitor::VisitArraySubscriptExpr(
893 const clang::ArraySubscriptExpr* subscript) {
894 // For our purposes here, a subscripting operation is equivalent to a
895 // dereference on its base - we don't make a distinction between different
896 // lifetimes in an array. This effectively merges the points-to sets of all
Luca Versarie5f66ad2022-08-02 02:18:15 -0700897 // 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 -0700898 // don't track individual array elements.
899
900 ObjectSet sub_points_to =
901 points_to_map_.GetExprObjectSet(subscript->getBase());
902
903 assert(subscript->isGLValue());
904 assert(!subscript->getBase()->isGLValue());
905 points_to_map_.SetExprObjectSet(subscript, sub_points_to);
906 return std::nullopt;
907}
908
909std::optional<std::string> TransferStmtVisitor::VisitBinaryOperator(
910 const clang::BinaryOperator* op) {
911 switch (op->getOpcode()) {
912 case clang::BO_Assign: {
913 assert(op->getLHS()->isGLValue());
914 ObjectSet lhs_points_to = points_to_map_.GetExprObjectSet(op->getLHS());
915 points_to_map_.SetExprObjectSet(op, lhs_points_to);
916 // Because of how we handle reference-like structs, a member access to a
917 // non-reference-like field in a struct might still produce lifetimes. We
918 // don't want to change points-to sets in those cases.
919 if (!op->getLHS()->getType()->isPointerType()) break;
920 ObjectSet rhs_points_to = points_to_map_.GetExprObjectSet(op->getRHS());
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700921 // We can overwrite (instead of extend) the destination points-to-set
922 // only in very specific circumstances:
923 // - We need to know unambiguously what the LHS refers to, so that we
924 // know we're definitely writing to a particular object, and
925 // - That destination object needs to be "single-valued" (it can't be
926 // an array, for example).
927 if (lhs_points_to.size() == 1 &&
928 object_repository_.GetObjectValueType(*lhs_points_to.begin()) ==
929 ObjectRepository::ObjectValueType::kSingleValued) {
930 // Replacing the points-to-set entirely does not generate any
931 // constraints.
932 points_to_map_.SetPointerPointsToSet(lhs_points_to, rhs_points_to);
933 } else {
934 HandlePointsToSetExtension(lhs_points_to, rhs_points_to,
935 op->getLHS()->getType(), object_repository_,
936 points_to_map_, constraints_);
Luca Versari99fddff2022-05-25 10:22:32 -0700937 }
938 break;
939 }
940
941 case clang::BO_Add:
942 case clang::BO_Sub: {
943 // Pointer arithmetic.
944 // We are only interested in the case in which exactly one of the two
945 // operands is a pointer (in particular we want to exclude int* - int*).
946 if (op->getLHS()->getType()->isPointerType() ^
947 op->getRHS()->getType()->isPointerType()) {
948 if (op->getLHS()->getType()->isPointerType()) {
949 points_to_map_.SetExprObjectSet(
950 op, points_to_map_.GetExprObjectSet(op->getLHS()));
951 } else {
952 points_to_map_.SetExprObjectSet(
953 op, points_to_map_.GetExprObjectSet(op->getRHS()));
954 }
955 }
956 break;
957 }
958
959 default:
960 break;
961 }
962 return std::nullopt;
963}
964
965std::optional<std::string> TransferStmtVisitor::VisitConditionalOperator(
966 const clang::ConditionalOperator* op) {
967 clang::QualType type = op->getType().getCanonicalType();
968
969 if (op->isGLValue() || type->isPointerType()) {
970 ObjectSet points_to_true =
971 points_to_map_.GetExprObjectSet(op->getTrueExpr());
972 ObjectSet points_to_false =
973 points_to_map_.GetExprObjectSet(op->getFalseExpr());
974 points_to_map_.SetExprObjectSet(op, points_to_true.Union(points_to_false));
975 }
976 return std::nullopt;
977}
978
979std::optional<std::string> TransferStmtVisitor::VisitInitListExpr(
980 const clang::InitListExpr* init_list) {
981 if (init_list->isSyntacticForm()) {
982 // We are only interested in the semantic form, which is fully realized,
983 // and is the one considered to be the initializer.
984 return std::nullopt;
985 }
986 if (IsInitExprInitializingARecordObject(init_list)) {
987 if (init_list->isTransparent()) {
988 // A transparent initializer list does nothing, the actual initializer
989 // terminating expression is within, and has already transferred lifetimes
990 // up to the object being initialized.
991 return std::nullopt;
992 }
993 // The object set for each field should be pointing to the initializers.
Martin Brænned7c0d0b2022-07-01 05:43:00 -0700994 const Object* init_object =
995 object_repository_.GetInitializedObject(init_list);
Martin Brænne2a1b27a2022-07-01 06:17:32 -0700996 TransferInitializer(init_object, init_list->getType(), object_repository_,
Luca Versari1f9fc2e2022-08-17 07:06:00 -0700997 init_list, points_to_map_, constraints_);
Luca Versari99fddff2022-05-25 10:22:32 -0700998 } else {
999 // If the InitListExpr is not initializing a record object, we assume it's
1000 // initializing an array or a reference and hence associate the InitListExpr
1001 // with the union of the points-to sets of the initializers (as the analysis
1002 // is array-insensitive).
1003 ObjectSet targets;
1004 for (clang::Expr* expr : init_list->inits()) {
1005 // If we are constructing an initializer list of non-pointer types, we
1006 // don't need to do anything here. Note that initializer list elements
1007 // must all have the same type in this case.
1008 if (PointeeType(expr->getType()).isNull() && !expr->isGLValue()) {
1009 return std::nullopt;
1010 }
1011 targets.Add(points_to_map_.GetExprObjectSet(expr));
1012 }
1013 points_to_map_.SetExprObjectSet(init_list, std::move(targets));
1014 }
1015 return std::nullopt;
1016}
1017
1018std::optional<std::string> TransferStmtVisitor::VisitMaterializeTemporaryExpr(
1019 const clang::MaterializeTemporaryExpr* temporary_expr) {
Martin Brænned7c0d0b2022-07-01 05:43:00 -07001020 const Object* temp_object =
1021 object_repository_.GetTemporaryObject(temporary_expr);
Luca Versari99fddff2022-05-25 10:22:32 -07001022 points_to_map_.SetExprObjectSet(temporary_expr, {temp_object});
1023 return std::nullopt;
1024}
1025
1026std::optional<std::string> TransferStmtVisitor::VisitMemberExpr(
1027 const clang::MemberExpr* member) {
1028 ObjectSet struct_points_to =
1029 points_to_map_.GetExprObjectSet(member->getBase());
1030
1031 if (const auto* method =
1032 clang::dyn_cast<clang::CXXMethodDecl>(member->getMemberDecl())) {
1033 // It doesn't really make sense to associate an object set with a non-static
1034 // member function.
1035 // If the member function is being called, we're not interested in its
1036 // "value" anyway. If the non-static member function is used outside of a
1037 // function call, then, it's a pointer-to-member, but those aren't
1038 // really pointers anyway, and we'll need special treatment for them.
1039 if (method->isStatic()) {
1040 points_to_map_.SetExprObjectSet(
1041 member, {object_repository_.GetDeclObject(method)});
1042 }
1043 return std::nullopt;
1044 }
1045
1046 auto field = clang::dyn_cast<clang::FieldDecl>(member->getMemberDecl());
1047 if (field == nullptr) {
1048 llvm::report_fatal_error("indirect member access is not supported yet");
1049 }
1050 ObjectSet expr_points_to =
1051 object_repository_.GetFieldObject(struct_points_to, field);
1052 if (field->getType()->isReferenceType()) {
1053 expr_points_to = points_to_map_.GetPointerPointsToSet(expr_points_to);
1054 }
1055 points_to_map_.SetExprObjectSet(member, expr_points_to);
1056 return std::nullopt;
1057}
1058
1059std::optional<std::string> TransferStmtVisitor::VisitCXXThisExpr(
1060 const clang::CXXThisExpr* this_expr) {
Martin Brænned7c0d0b2022-07-01 05:43:00 -07001061 std::optional<const Object*> this_object = object_repository_.GetThisObject();
Luca Versari99fddff2022-05-25 10:22:32 -07001062 assert(this_object.has_value());
1063 points_to_map_.SetExprObjectSet(this_expr, ObjectSet{this_object.value()});
1064 return std::nullopt;
1065}
1066
Luca Versari99fddff2022-05-25 10:22:32 -07001067// Collects all function parameters, including (if this is a member call) the
1068// implicit this argument.
1069std::vector<FunctionParameter> CollectFunctionParameters(
1070 const clang::CallExpr* call, const clang::FunctionDecl* callee,
1071 const FunctionLifetimes& callee_lifetimes,
1072 const ObjectRepository& object_repository) {
1073 std::vector<FunctionParameter> fn_params;
1074
1075 if (clang::isa<clang::CXXOperatorCallExpr>(call) &&
1076 clang::isa<clang::CXXMethodDecl>(callee)) {
1077 // `this` is considered an argument in this case (but not a parameter on its
1078 // definition).
1079 assert(call->getNumArgs() == callee->getNumParams() + 1);
1080
1081 // Handle the `this` argument.
1082 {
1083 fn_params.push_back(FunctionParameter{
1084 clang::dyn_cast<clang::CXXMethodDecl>(callee)->getThisType(),
1085 callee_lifetimes.GetThisLifetimes(),
Martin Brænne05818552022-07-01 06:06:13 -07001086 object_repository.GetCallExprThisPointer(call)});
Luca Versari99fddff2022-05-25 10:22:32 -07001087 }
1088
1089 // Handle all other arguments.
1090 for (size_t i = 1; i < call->getNumArgs(); i++) {
1091 fn_params.push_back(FunctionParameter{
1092 callee->getParamDecl(i - 1)->getType().getCanonicalType(),
1093 callee_lifetimes.GetParamLifetimes(i - 1),
Martin Brænne05818552022-07-01 06:06:13 -07001094 object_repository.GetCallExprArgumentObject(call, i)});
Luca Versari99fddff2022-05-25 10:22:32 -07001095 }
1096 } else {
1097 // We check <= instead of == because of default arguments.
1098 assert(call->getNumArgs() <= callee->getNumParams());
1099
1100 for (size_t i = 0; i < call->getNumArgs(); i++) {
1101 fn_params.push_back(FunctionParameter{
1102 callee->getParamDecl(i)->getType().getCanonicalType(),
1103 callee_lifetimes.GetParamLifetimes(i),
Martin Brænne05818552022-07-01 06:06:13 -07001104 object_repository.GetCallExprArgumentObject(call, i)});
Luca Versari99fddff2022-05-25 10:22:32 -07001105 }
1106 if (const auto* member_call =
1107 clang::dyn_cast<clang::CXXMemberCallExpr>(call)) {
1108 // The callee is always a MemberExpr.
1109 // - If the call uses `->`, the object argument should be a prvalue that
1110 // is a pointer to the struct.
1111 // - If the call uses `.`, the object argument should be a glvalue of
1112 // struct type.
1113 assert(clang::isa<clang::MemberExpr>(member_call->getCallee()));
1114 assert(clang::dyn_cast<clang::MemberExpr>(member_call->getCallee())
1115 ->isArrow() ^
1116 member_call->getImplicitObjectArgument()->isGLValue());
1117 // This is the type of the function *parameter*, not of the argument.
1118 // This is always a pointer, even if the argument is a reference, but as
1119 // we don't treat pointers or references differently, this is not an
1120 // issue.
1121 fn_params.push_back(
1122 FunctionParameter{member_call->getMethodDecl()->getThisType(),
1123 callee_lifetimes.GetThisLifetimes(),
Martin Brænne05818552022-07-01 06:06:13 -07001124 object_repository.GetCallExprThisPointer(call)});
Luca Versari99fddff2022-05-25 10:22:32 -07001125 }
1126 }
1127 return fn_params;
1128}
1129
Luca Versari99fddff2022-05-25 10:22:32 -07001130void SetExprObjectSetRespectingType(const clang::Expr* expr,
1131 const ObjectSet& points_to,
1132 PointsToMap& points_to_map,
1133 clang::ASTContext& ast_context) {
1134 ObjectSet points_to_filtered;
1135
1136 for (auto object : points_to) {
1137 if (expr->isGLValue()) {
Martin Brænne4d8cdfd2022-07-01 06:16:36 -07001138 if (PointeesCompatible(expr->getType(), object->Type(), ast_context)) {
Luca Versari99fddff2022-05-25 10:22:32 -07001139 points_to_filtered.Add(object);
1140 }
1141 } else {
1142 clang::QualType expr_type = expr->getType();
1143 // CXXConstructExpr is a special case -- it is a non-glvalue with the type
1144 // of the constructed object itself. Non-pointer, non-glvalue expressions
1145 // like this are not usually allowed to be associated with a points-to
1146 // set, but CXXConstructExpr is an exception. We need to associate it with
1147 // an `Object` representing the newly constructed object so that
1148 // TransferInitializer() can then retrieve this object. So we pretend that
1149 // the type is actually "pointer to object" to give MayPointTo() what it
1150 // expects.
1151 //
1152 // Note that we will not see clang::InitListExpr here, which is the other
1153 // form of initializer along with CXXConstructExpr. That is because we
1154 // come here through a "call" and we don't consider an initializer list to
1155 // be a "call" or treat it as such.
1156 assert(!clang::isa<clang::InitListExpr>(expr));
1157 if (clang::isa<clang::CXXConstructExpr>(expr)) {
1158 expr_type = ast_context.getPointerType(expr_type);
1159 }
1160
Martin Brænne4d8cdfd2022-07-01 06:16:36 -07001161 if (MayPointTo(expr_type, object->Type(), ast_context)) {
Luca Versari99fddff2022-05-25 10:22:32 -07001162 points_to_filtered.Add(object);
1163 }
1164 }
1165 }
1166
1167 points_to_map.SetExprObjectSet(expr, points_to_filtered);
1168}
1169
Luca Versari99fddff2022-05-25 10:22:32 -07001170std::optional<std::string> TransferStmtVisitor::VisitCallExpr(
1171 const clang::CallExpr* call) {
1172 llvm::SmallVector<const clang::FunctionDecl*> callees;
1173
1174 const clang::FunctionDecl* direct_callee = call->getDirectCallee();
1175 if (direct_callee) {
1176 // This code path is needed for non-static member functions, as those don't
1177 // have an `Object` for their callees.
1178 callees.push_back(direct_callee);
1179 } else {
1180 const clang::Expr* callee = call->getCallee();
1181 for (const auto& object : points_to_map_.GetExprObjectSet(callee)) {
Martin Brænne4d8cdfd2022-07-01 06:16:36 -07001182 const clang::FunctionDecl* func = object->GetFunc();
Luca Versari99fddff2022-05-25 10:22:32 -07001183 assert(func);
1184 callees.push_back(func);
1185 }
1186 }
1187
1188 std::optional<ObjectSet> call_points_to;
1189
1190 for (const auto* callee : callees) {
1191 bool is_builtin = callee->getBuiltinID() != 0;
1192
1193 FunctionLifetimesOrError builtin_callee_lifetimes_or_error;
1194 if (is_builtin) {
1195 builtin_callee_lifetimes_or_error = GetBuiltinLifetimes(callee);
1196 } else {
1197 assert(callee_lifetimes_.count(callee->getCanonicalDecl()));
1198 }
1199 const FunctionLifetimesOrError& callee_lifetimes_or_error =
1200 is_builtin ? builtin_callee_lifetimes_or_error
1201 : callee_lifetimes_.lookup(callee->getCanonicalDecl());
1202
1203 if (!std::holds_alternative<FunctionLifetimes>(callee_lifetimes_or_error)) {
1204 return "No lifetimes for callee '" + callee->getNameAsString() + "': " +
1205 std::get<FunctionAnalysisError>(callee_lifetimes_or_error).message;
1206 }
1207 FunctionLifetimes callee_lifetimes =
1208 std::get<FunctionLifetimes>(callee_lifetimes_or_error);
1209
1210 bool is_member_operator = clang::isa<clang::CXXOperatorCallExpr>(call) &&
1211 clang::isa<clang::CXXMethodDecl>(callee);
1212 for (size_t i = is_member_operator ? 1 : 0; i < call->getNumArgs(); i++) {
1213 // We can't just use SetPointerPointsToSet here because call->getArg(i)
1214 // might not have an ObjectSet (for example for integer constants); it
1215 // also may be needed for struct initialization.
1216 // Note that we don't need to worry about possibly extending the
1217 // PointsToSet more than needed, as dataflow analysis relies on points-to
1218 // sets never shrinking.
1219 TransferInitializer(
Martin Brænne2a1b27a2022-07-01 06:17:32 -07001220 object_repository_.GetCallExprArgumentObject(call, i),
Luca Versari99fddff2022-05-25 10:22:32 -07001221 callee->getParamDecl(is_member_operator ? i - 1 : i)->getType(),
Luca Versari1f9fc2e2022-08-17 07:06:00 -07001222 object_repository_, call->getArg(i), points_to_map_, constraints_);
Luca Versari99fddff2022-05-25 10:22:32 -07001223 }
1224 if (is_member_operator) {
1225 points_to_map_.SetPointerPointsToSet(
1226 object_repository_.GetCallExprThisPointer(call),
1227 points_to_map_.GetExprObjectSet(call->getArg(0)));
1228 }
1229 if (const auto* member_call =
1230 clang::dyn_cast<clang::CXXMemberCallExpr>(call)) {
1231 points_to_map_.SetPointerPointsToSet(
1232 object_repository_.GetCallExprThisPointer(call),
1233 points_to_map_.GetExprObjectSet(
1234 member_call->getImplicitObjectArgument()));
1235 }
1236
1237 std::vector<FunctionParameter> fn_params = CollectFunctionParameters(
1238 call, callee, callee_lifetimes, object_repository_);
1239
1240 std::optional<ObjectSet> single_call_points_to = TransferLifetimesForCall(
1241 call, fn_params, callee_lifetimes.GetReturnLifetimes(),
Luca Versari187176a2022-09-02 02:42:23 -07001242 object_repository_, points_to_map_, constraints_,
1243 callee->getASTContext());
Luca Versari99fddff2022-05-25 10:22:32 -07001244 if (single_call_points_to) {
1245 if (call_points_to) {
1246 call_points_to.value().Add(std::move(single_call_points_to).value());
1247 } else {
1248 call_points_to = std::move(single_call_points_to);
1249 }
1250 }
1251 }
1252
1253 if (call_points_to) {
1254 SetExprObjectSetRespectingType(call, call_points_to.value(), points_to_map_,
1255 callees[0]->getASTContext());
1256 }
1257 return std::nullopt;
1258}
1259
1260std::optional<std::string> TransferStmtVisitor::VisitCXXConstructExpr(
1261 const clang::CXXConstructExpr* construct_expr) {
1262 const clang::CXXConstructorDecl* constructor =
1263 construct_expr->getConstructor();
1264
1265 assert(callee_lifetimes_.count(constructor->getCanonicalDecl()));
1266 const FunctionLifetimesOrError& callee_lifetimes_or_error =
1267 callee_lifetimes_.lookup(constructor->getCanonicalDecl());
1268 if (!std::holds_alternative<FunctionLifetimes>(callee_lifetimes_or_error)) {
1269 return "No lifetimes for constructor " + constructor->getNameAsString();
1270 }
1271 const FunctionLifetimes& callee_lifetimes =
1272 std::get<FunctionLifetimes>(callee_lifetimes_or_error);
1273
1274 // We check <= instead of == because of default arguments.
1275 assert(construct_expr->getNumArgs() <= constructor->getNumParams());
1276
1277 for (size_t i = 0; i < construct_expr->getNumArgs(); i++) {
Martin Brænne2a1b27a2022-07-01 06:17:32 -07001278 TransferInitializer(
1279 object_repository_.GetCXXConstructExprArgumentObject(construct_expr, i),
1280 construct_expr->getArg(i)->getType(), object_repository_,
Luca Versari1f9fc2e2022-08-17 07:06:00 -07001281 construct_expr->getArg(i), points_to_map_, constraints_);
Luca Versari99fddff2022-05-25 10:22:32 -07001282 }
1283
1284 // Handle the `this` parameter, which should point to the object getting
1285 // initialized.
1286 points_to_map_.SetPointerPointsToSet(
1287 object_repository_.GetCXXConstructExprThisPointer(construct_expr),
1288 {object_repository_.GetInitializedObject(construct_expr)});
1289
1290 // Populate fn_params for the constructor call.
1291 std::vector<FunctionParameter> fn_params;
1292
1293 for (size_t i = 0; i < construct_expr->getNumArgs(); i++) {
1294 clang::QualType arg_type =
1295 constructor->getParamDecl(i)->getType().getCanonicalType();
1296 fn_params.push_back(
1297 FunctionParameter{arg_type, callee_lifetimes.GetParamLifetimes(i),
Martin Brænne05818552022-07-01 06:06:13 -07001298 object_repository_.GetCXXConstructExprArgumentObject(
Luca Versari99fddff2022-05-25 10:22:32 -07001299 construct_expr, i)});
1300 }
1301
1302 clang::QualType type = constructor->getThisType();
1303 fn_params.push_back(FunctionParameter{
1304 type, callee_lifetimes.GetThisLifetimes(),
Martin Brænne05818552022-07-01 06:06:13 -07001305 object_repository_.GetCXXConstructExprThisPointer(construct_expr)});
Luca Versari99fddff2022-05-25 10:22:32 -07001306
1307 TransferLifetimesForCall(
1308 construct_expr, fn_params,
1309 ValueLifetimes::ForLifetimeLessType(constructor->getReturnType()),
Luca Versari187176a2022-09-02 02:42:23 -07001310 object_repository_, points_to_map_, constraints_,
1311 constructor->getASTContext());
Luca Versari99fddff2022-05-25 10:22:32 -07001312 return std::nullopt;
1313}
1314
1315} // namespace
1316
1317} // namespace lifetimes
1318} // namespace tidy
1319} // namespace clang