blob: c946c1ccebc2eac709d975def6c6c111e00f029a [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/object_repository.h"
6
7#include <functional>
8#include <optional>
9#include <string>
10#include <utility>
11#include <vector>
12
13#include "lifetime_analysis/object.h"
14#include "lifetime_analysis/visit_lifetimes.h"
15#include "lifetime_annotations/lifetime.h"
16#include "lifetime_annotations/pointee_type.h"
17#include "lifetime_annotations/type_lifetimes.h"
18#include "clang/AST/Decl.h"
19#include "clang/AST/DeclCXX.h"
20#include "clang/AST/Expr.h"
21#include "clang/AST/ExprCXX.h"
22#include "clang/AST/RecursiveASTVisitor.h"
23#include "clang/AST/Type.h"
24#include "clang/Basic/LLVM.h"
25#include "llvm/ADT/SmallVector.h"
26#include "llvm/ADT/StringRef.h"
27#include "llvm/Support/ErrorHandling.h"
28
29namespace clang {
30namespace tidy {
31namespace lifetimes {
32
33class ObjectRepository::VarDeclVisitor
34 : public clang::RecursiveASTVisitor<VarDeclVisitor> {
35 public:
36 explicit VarDeclVisitor(ObjectRepository& object_repository)
37 : object_repository_(object_repository) {}
38
39 // We need to visit implicitly-defined constructors and assignment operators.
40 bool shouldVisitImplicitCode() { return true; }
41
42 bool VisitVarDecl(clang::VarDecl* var) {
43 // Add objects for any local variables declared in this function.
44 AddObjectForVar(var);
45 return true;
46 }
47
48 bool VisitReturnStmt(clang::ReturnStmt* stmt) {
49 const clang::Expr* expr = stmt->getRetValue();
50 if (IsInitExprInitializingARecordObject(expr)) {
51 PropagateInitializedObject(expr, object_repository_.return_object_);
52 }
53 return true;
54 }
55
56 bool VisitMemberExpr(clang::MemberExpr* member) {
57 if (auto* method =
58 clang::dyn_cast<clang::CXXMethodDecl>(member->getMemberDecl());
59 method && method->isStatic()) {
60 // Create objects for static member functions.
61 AddObjectForFunc(method);
62 }
63 return true;
64 }
65
66 bool VisitDeclRefExpr(clang::DeclRefExpr* decl_ref) {
67 // Add objects for any global variables referenced in this function.
68 // This also runs for local variables, but we don't have to treat those
69 // differently as AddObjectForVar() protects against duplication.
70 if (auto* var_decl = clang::dyn_cast<clang::VarDecl>(decl_ref->getDecl())) {
71 AddObjectForVar(var_decl);
72 }
73 // Add objects for any function referenced in this function.
74 if (auto* function_decl =
75 clang::dyn_cast<clang::FunctionDecl>(decl_ref->getDecl())) {
76 AddObjectForFunc(function_decl);
77 }
78 return true;
79 }
80
81 bool VisitObjCMessageExpr(clang::ObjCMessageExpr* msg_expr) {
82 // ObjCMessageExpr is an initializer expression terminator, so we should
83 // have walked down from the object which requires initialization to find
84 // its terminating expressions, which should have found this expression and
85 // connected it to that object already.
86 if (!object_repository_.initialized_objects_.count(msg_expr)) {
87 msg_expr->dump();
88 llvm::report_fatal_error(
89 "Missing initializer for ObjCMessageExpr, we did not record it "
90 "when we visited something earlier in the tree yet?");
91 }
92 return true;
93 }
94
95 // Create objects for function call arguments.
96 bool VisitCallExpr(clang::CallExpr* call_expr) {
97 if (IsInitExprInitializingARecordObject(call_expr)) {
98 assert(InitializedObjectWasPropagatedTo(call_expr));
99 }
100
101 // For calls to members, the type of the callee is a "bound member function
102 // type", so we look at the declaration instead.
103 if (auto member_call =
104 clang::dyn_cast<clang::CXXMemberCallExpr>(call_expr)) {
105 const clang::FunctionDecl* callee = call_expr->getDirectCallee();
106 // TODO(veluca): pointers-to-members are not supported (yet?)
107 assert(callee);
108 AddObjectsForArguments(call_expr, callee->getType(),
109 /*index_shift=*/0);
110 auto method = clang::cast<clang::CXXMethodDecl>(callee);
111 clang::QualType type = method->getThisType();
112 object_repository_.call_expr_this_pointers_[call_expr] =
113 CreateLocalObject(type);
114 } else if (auto op_call =
115 clang::dyn_cast<clang::CXXOperatorCallExpr>(call_expr)) {
116 const clang::FunctionDecl* callee = call_expr->getDirectCallee();
117 auto method = clang::dyn_cast<clang::CXXMethodDecl>(callee);
118 AddObjectsForArguments(call_expr, callee->getType(),
119 /*index_shift=*/method ? 1 : 0);
120 if (method) {
121 clang::QualType type = method->getThisType();
122 object_repository_.call_expr_this_pointers_[call_expr] =
123 CreateLocalObject(type);
124 }
125 } else {
126 // Always a function pointer.
127 clang::QualType callee_type = call_expr->getCallee()->getType();
128 AddObjectsForArguments(call_expr, callee_type, /*index_shift=*/0);
129 }
130
131 return true;
132 }
133
134 bool VisitCXXConstructExpr(clang::CXXConstructExpr* construct_expr) {
135 assert(InitializedObjectWasPropagatedTo(construct_expr));
136
137 // Create objects for constructor arguments.
138 const clang::FunctionDecl* constructor = construct_expr->getConstructor();
139 AddObjectsForArguments(construct_expr, constructor->getType(),
140 /*index_shift=*/0);
141 clang::QualType type = construct_expr->getConstructor()->getThisType();
142 object_repository_.call_expr_this_pointers_[construct_expr] =
143 CreateLocalObject(type);
144 return true;
145 }
146
147 bool VisitInitListExpr(clang::InitListExpr* init_list_expr) {
148 // We only want to visit in Semantic form, we ignore Syntactic form.
149 if (IsInitExprInitializingARecordObject(init_list_expr) &&
150 init_list_expr->isSemanticForm() && !init_list_expr->isTransparent()) {
151 assert(InitializedObjectWasPropagatedTo(init_list_expr));
152 }
153 return true;
154 }
155
156 bool VisitMaterializeTemporaryExpr(
157 clang::MaterializeTemporaryExpr* temporary_expr) {
158 object_repository_.temporary_objects_[temporary_expr] =
159 AddTemporaryObjectForExpression(temporary_expr->getSubExpr());
160 return true;
161 }
162
163 bool VisitCompoundStmt(clang::CompoundStmt* compound) {
164 // Create temporary objects for any top-level `CXXTemporaryObjectExpr`s,
165 // i.e. ones that are used as statements.
166 for (clang::Stmt* stmt : compound->body()) {
167 if (auto* temporary = clang::dyn_cast<CXXTemporaryObjectExpr>(stmt)) {
168 AddTemporaryObjectForExpression(temporary);
169 }
170 }
171 return true;
172 }
173
174 Object CreateLocalObject(clang::QualType type) {
175 Object object = Object::Create(Lifetime::CreateLocal(), type);
176 object_repository_.CreateObjects(
Martin Brænne03d93302022-06-23 00:23:38 -0700177 object, type,
178 [](const clang::Expr*) { return Lifetime::CreateVariable(); },
Luca Versari99fddff2022-05-25 10:22:32 -0700179 /*transitive=*/false);
180 return object;
181 }
182
183 void AddObjectsForArguments(const clang::Expr* expr,
184 clang::QualType callee_type, size_t index_shift) {
185 if (callee_type->isDependentType()) {
186 // TODO(veluca): the fact that we reach this point is a clang bug: it
187 // should not be possible to reach dependent types from a template
188 // instantiation. See also the following discussion, where richardsmith@
189 // agrees this looks like a Clang bug and suggests how it might be fixed:
190 // https://chat.google.com/room/AAAAb6i7WDQ/OvLC9NgO91A
191 return;
192 }
193 if (callee_type->isPointerType()) {
194 callee_type = callee_type->getPointeeType();
195 }
196 // TODO(veluca): figure out how to create a test where the callee is a
197 // ParenType.
198 // For reference, this was triggered in the implementation of `bsearch`.
199 callee_type = callee_type.IgnoreParens();
200 assert(callee_type->isFunctionType());
201 // TODO(veluca): could this be a clang::FunctionNoProtoType??
202 const auto* fn_type = clang::cast<clang::FunctionProtoType>(callee_type);
203 for (size_t i = 0; i < fn_type->getNumParams(); ++i) {
204 object_repository_
205 .call_expr_args_objects_[std::make_pair(expr, i + index_shift)] =
206 CreateLocalObject(fn_type->getParamType(i));
207 }
208 }
209
210 void AddObjectForVar(clang::VarDecl* var) {
211 if (object_repository_.object_repository_.count(var)) {
212 return;
213 }
214
215 Lifetime lifetime;
216 LifetimeFactory lifetime_factory;
217
218 switch (var->getStorageClass()) {
219 case clang::SC_Extern:
220 case clang::SC_Static:
221 case clang::SC_PrivateExtern:
222 lifetime = Lifetime::Static();
Martin Brænne03d93302022-06-23 00:23:38 -0700223 lifetime_factory = [](const clang::Expr*) {
224 return Lifetime::Static();
225 };
Luca Versari99fddff2022-05-25 10:22:32 -0700226 break;
227 default:
228 lifetime = Lifetime::CreateLocal();
Martin Brænne03d93302022-06-23 00:23:38 -0700229 lifetime_factory = [](const clang::Expr*) {
230 return Lifetime::CreateVariable();
231 };
Luca Versari99fddff2022-05-25 10:22:32 -0700232 break;
233 }
234
235 Object object = Object::Create(lifetime, var->getType());
236
237 object_repository_.CreateObjects(
238 object, var->getType(), lifetime_factory,
239 /*transitive=*/clang::isa<clang::ParmVarDecl>(var) ||
240 lifetime == Lifetime::Static());
241
242 object_repository_.object_repository_[var] = object;
243 object_repository_.lifetime_value_types_[object] =
244 var->getType()->isArrayType() ? ObjectValueType::kMultiValued
245 : ObjectValueType::kSingleValued;
246
247 // Remember the original value of function parameters.
248 if (auto parm_var_decl = clang::dyn_cast<const clang::ParmVarDecl>(var)) {
249 object_repository_.initial_parameter_object_[parm_var_decl] =
250 object_repository_.CloneObject(object);
251 }
252
253 if (var->hasInit() && var->getType()->isRecordType()) {
254 PropagateInitializedObject(var->getInit(), object);
255 }
256 }
257
258 void AddObjectForFunc(clang::FunctionDecl* func) {
259 if (object_repository_.object_repository_.count(func)) {
260 return;
261 }
262
263 object_repository_.object_repository_[func] =
264 Object::CreateFromFunctionDecl(*func);
265 }
266
267 Object AddTemporaryObjectForExpression(clang::Expr* expr) {
268 clang::QualType type = expr->getType().getCanonicalType();
269 Object object = Object::Create(Lifetime::CreateLocal(), type);
270
271 object_repository_.CreateObjects(
Martin Brænne03d93302022-06-23 00:23:38 -0700272 object, type,
273 [](const clang::Expr*) { return Lifetime::CreateVariable(); },
Luca Versari99fddff2022-05-25 10:22:32 -0700274 /*transitive=*/false);
275
276 if (type->isRecordType()) {
277 PropagateInitializedObject(expr, object);
278 }
279 return object;
280 }
281
282 // Propagates an `object` of record type that is to be initialized to the
283 // expressions that actually perform the initialization (we call these
284 // "terminating expressions").
285 //
286 // `expr` is the initializer for a variable; this will contain one or
287 // several terminating expressions (such as a CXXConstructExpr, InitListExpr,
288 // or CallExpr).
289 //
290 // Note that not all terminating expressions below `expr` necessarily
291 // initialize `object`; some of these terminating expressions may also
292 // initialize temporary objects. This function takes care to propagate
293 // `object` only to the appropriate terminating expressions.
294 //
295 // The mapping from a terminating expression to the object it initializes
296 // is stored in `object_repository_.initialized_objects_`.
297 void PropagateInitializedObject(const clang::Expr* expr, Object object) {
298 // TODO(danakj): Use StmtVisitor to implement this method.
299 // copybara:begin_strip
300 // Context and hints:
301 // http://cl/414017975/depot/lifetime_analysis/var_decl_objects.cc?version=s3#324
302 // copybara:end_strip
303
304 // Terminating expressions. Expressions that don't initialize a record
305 // object can not be such, and their existence is unexpected as we should
306 // be converting to and initializing a record object from such expressions
307 // further up in the initializer expression's AST. We will assert later in
308 // this function if we find this situation somehow due to incorrect
309 // expectations in this comment.
310 if (IsInitExprInitializingARecordObject(expr)) {
311 if (clang::isa<clang::CXXConstructExpr>(expr) ||
312 clang::isa<clang::CallExpr>(expr) ||
313 clang::isa<clang::ObjCMessageExpr>(expr) ||
314 clang::isa<clang::LambdaExpr>(expr)) {
315 object_repository_.initialized_objects_[expr] = object;
316 return;
317 }
318 if (auto* e = clang::dyn_cast<clang::InitListExpr>(expr)) {
319 if (!e->isSemanticForm()) return;
320 if (e->isTransparent()) {
321 // A field initializer like `S s{cond ? S{} : S{}}` is considered
322 // transparent, and the actual initializer is within.
323 for (const clang::Expr* init : e->inits()) {
324 PropagateInitializedObject(init, object);
325 }
326 } else {
327 object_repository_.initialized_objects_[e] = object;
328 }
329 return;
330 }
331 }
332
333 // Expressions to walk through. Logic is similar to the AggExprEmitter in
334 // clang third_party/llvm-project/clang/lib/CodeGen/CGExprAgg.cpp though we
335 // don't have to visit all the sub-expressions that clang codegen needs to,
336 // as we can stop at terminating expressions and ignore many expressions
337 // that don't occur in the code we're analyzing.
338 if (auto* e = clang::dyn_cast<clang::ParenExpr>(expr)) {
339 PropagateInitializedObject(e->getSubExpr(), object);
340 return;
341 }
342 if (auto* e = clang::dyn_cast<clang::UnaryOperator>(expr)) {
343 PropagateInitializedObject(e->getSubExpr(), object);
344 return;
345 }
346 if (auto* e = clang::dyn_cast<clang::SubstNonTypeTemplateParmExpr>(expr)) {
347 PropagateInitializedObject(e->getReplacement(), object);
348 return;
349 }
350 if (auto* e = clang::dyn_cast<clang::CastExpr>(expr)) {
351 PropagateInitializedObject(e->getSubExpr(), object);
352 return;
353 }
354 if (auto* e = clang::dyn_cast<clang::CXXDefaultArgExpr>(expr)) {
355 PropagateInitializedObject(e->getExpr(), object);
356 return;
357 }
358 if (auto* e = clang::dyn_cast<clang::CXXDefaultInitExpr>(expr)) {
359 PropagateInitializedObject(e->getExpr(), object);
360 return;
361 }
362 if (auto* e = clang::dyn_cast<clang::ExprWithCleanups>(expr)) {
363 PropagateInitializedObject(e->getSubExpr(), object);
364 return;
365 }
366
367 // Expressions that produce a temporary object.
368 if (auto* e = clang::dyn_cast<clang::BinaryOperator>(expr)) {
369 if (e->isCommaOp()) {
370 AddTemporaryObjectForExpression(e->getLHS());
371 PropagateInitializedObject(e->getRHS(), object);
372 return;
373 }
374
375 // Any other binary operator should not produce a record type, it would be
376 // used to construct a record further up the AST, so we should not arrive
377 // here.
378 expr->dump();
379 llvm::report_fatal_error(
380 "Unexpected binary operator in initializer expression tree");
381 }
382 if (auto* e = clang::dyn_cast<clang::AbstractConditionalOperator>(expr)) {
383 AddTemporaryObjectForExpression(e->getCond());
384 PropagateInitializedObject(e->getTrueExpr(), object);
385 PropagateInitializedObject(e->getFalseExpr(), object);
386 return;
387 }
388
389 expr->dump();
390 llvm::report_fatal_error(
391 "Unexpected expression in initializer expression tree");
392 }
393
394 bool InitializedObjectWasPropagatedTo(clang::Expr* terminating_expr) {
395 // An expression that initializes an object should have already been
396 // connected to the object it initializes. We should have walked down from
397 // the object which requires initialization to find its terminating
398 // expressions.
399 if (!object_repository_.initialized_objects_.count(terminating_expr)) {
400 llvm::errs() << "Missing initialized object for terminating expression, "
401 "we did not record it when we visited something earlier "
402 "in the tree yet?\n";
403 terminating_expr->dump();
404 return false;
405 } else {
406 return true;
407 }
408 }
409
410 void TraverseCXXMemberInitializers(
411 const clang::CXXConstructorDecl* constructor) {
412 // For constructors, we also need to create lifetimes for variables
413 // referenced by in-class member initializers; the visitor by default only
414 // visits expressions in the initializer list.
415 // We also need to associate member initializers with the members they
416 // initialize.
417 for (const auto* init : constructor->inits()) {
418 const auto* init_expr = init->getInit();
419 if (const auto* default_init =
420 clang::dyn_cast<clang::CXXDefaultInitExpr>(init_expr)) {
421 init_expr = default_init->getExpr();
422 }
423
424 if (init->getMember() && init->getMember()->getType()->isRecordType()) {
425 std::optional<Object> this_object = object_repository_.GetThisObject();
426 assert(this_object.has_value());
427
428 Object field_object =
429 object_repository_.GetFieldObject(*this_object, init->getMember());
430 PropagateInitializedObject(init_expr, field_object);
431 } else if (init->getBaseClass()) {
432 std::optional<Object> this_object = object_repository_.GetThisObject();
433 assert(this_object.has_value());
434
435 Object base_object = object_repository_.GetBaseClassObject(
436 *this_object, init->getBaseClass());
437 PropagateInitializedObject(init_expr, base_object);
438 }
439
440 // Traverse after finishing with the outer expression, including
441 // connecting the initializer (constructor) to its object.
442 TraverseStmt(const_cast<clang::Expr*>(init_expr));
443 }
444 }
445
446 ObjectRepository& object_repository_;
447};
448
449ObjectRepository::ObjectRepository(const clang::FunctionDecl* func) {
450 const auto* method_decl = clang::dyn_cast<clang::CXXMethodDecl>(func);
451
452 const auto* definition = func->getDefinition();
453 assert(definition || (method_decl && method_decl->isPure()));
454 if (definition) func = definition;
455
456 // For the return value, we only need to create field objects.
457 return_object_ =
458 Object::Create(Lifetime::CreateLocal(), func->getReturnType());
459 CreateObjects(
460 return_object_, func->getReturnType(),
Martin Brænne03d93302022-06-23 00:23:38 -0700461 [](const clang::Expr*) { return Lifetime::CreateLocal(); },
Luca Versari99fddff2022-05-25 10:22:32 -0700462 /*transitive=*/false);
463
464 if (method_decl) {
465 if (!method_decl->isStatic()) {
466 this_object_ = Object::Create(Lifetime::CreateVariable(),
467 method_decl->getThisObjectType());
468 CreateObjects(
469 *this_object_, method_decl->getThisObjectType(),
Martin Brænne03d93302022-06-23 00:23:38 -0700470 [](const clang::Expr*) { return Lifetime::CreateVariable(); },
Luca Versari99fddff2022-05-25 10:22:32 -0700471 /*transitive=*/true);
472 }
473 }
474
475 VarDeclVisitor decl_visitor(*this);
476 if (auto* constructor = clang::dyn_cast<clang::CXXConstructorDecl>(func)) {
477 decl_visitor.TraverseCXXMemberInitializers(constructor);
478 }
479 decl_visitor.TraverseFunctionDecl(const_cast<clang::FunctionDecl*>(func));
480}
481
482std::string ObjectRepository::DebugString() const {
483 std::string result;
484 llvm::raw_string_ostream os(result);
485
486 if (this_object_) {
487 os << "This " << this_object_->DebugString() << "\n";
488 }
489 for (const auto& [decl, object] : object_repository_) {
490 os << decl->getDeclKindName() << " " << decl << " (";
491 decl->printName(os);
492 os << ") object: " << object.DebugString() << "\n";
493 }
494 for (const auto& [expr_i, object] : call_expr_args_objects_) {
495 const auto& [expr, i] = expr_i;
496 os << "Call " << expr << " (arg " << i
497 << ") object: " << object.DebugString() << "\n";
498 }
499 for (const auto& [expr, object] : call_expr_this_pointers_) {
500 os << "Call " << expr << " (this) pointer: " << object.DebugString()
501 << "\n";
502 }
503 os << "InitialPointsToMap:\n" << initial_points_to_map_.DebugString() << "\n";
504 for (const auto& [field, object] : field_object_map_) {
505 os << "Field '";
506 field.second->printName(os);
507 os << "' on " << field.first.Type().getAsString()
508 << " object: " << object.DebugString() << "\n";
509 }
510 os << "Return " << return_object_.DebugString() << "\n";
511 os.flush();
512 return result;
513}
514
515Object ObjectRepository::GetDeclObject(const clang::ValueDecl* decl) const {
516 auto iter = object_repository_.find(decl);
517 if (iter == object_repository_.end()) {
518 llvm::errs() << "Didn't find object for Decl:\n";
519 decl->dump();
520 llvm::errs() << "\n" << DebugString();
521 llvm::report_fatal_error("Didn't find object for Decl");
522 }
523 return iter->second;
524}
525
526Object ObjectRepository::GetTemporaryObject(
527 const clang::MaterializeTemporaryExpr* expr) const {
528 auto iter = temporary_objects_.find(expr);
529 if (iter == temporary_objects_.end()) {
530 llvm::errs() << "Didn't find object for temporary expression:\n";
531 expr->dump();
532 llvm::errs() << "\n" << DebugString();
533 llvm::report_fatal_error("Didn't find object for temporary expression");
534 }
535 return iter->second;
536}
537
538Object ObjectRepository::GetOriginalParameterValue(
539 const clang::ParmVarDecl* var_decl) const {
540 auto iter = initial_parameter_object_.find(var_decl);
541 if (iter == initial_parameter_object_.end()) {
542 llvm::errs() << "Didn't find caller object for parameter:\n";
543 var_decl->dump();
544 llvm::errs() << "\n" << DebugString();
545 llvm::report_fatal_error("Didn't find caller object for parameter");
546 }
547 return iter->second;
548}
549
550Object ObjectRepository::GetCallExprArgumentObject(const clang::CallExpr* expr,
551 size_t arg_index) const {
552 auto iter = call_expr_args_objects_.find(std::make_pair(expr, arg_index));
553 if (iter == call_expr_args_objects_.end()) {
554 llvm::errs() << "Didn't find object for argument " << arg_index
555 << " of call:\n";
556 expr->dump();
557 llvm::errs() << "\n" << DebugString();
558 llvm::report_fatal_error("Didn't find object for argument");
559 }
560 return iter->second;
561}
562
563Object ObjectRepository::GetCallExprThisPointer(
564 const clang::CallExpr* expr) const {
565 auto iter = call_expr_this_pointers_.find(expr);
566 if (iter == call_expr_this_pointers_.end()) {
567 llvm::errs() << "Didn't find `this` object for call:\n";
568 expr->dump();
569 llvm::errs() << "\n" << DebugString();
570 llvm::report_fatal_error("Didn't find `this` object for call");
571 }
572 return iter->second;
573}
574
575Object ObjectRepository::GetCXXConstructExprArgumentObject(
576 const clang::CXXConstructExpr* expr, size_t arg_index) const {
577 auto iter = call_expr_args_objects_.find(std::make_pair(expr, arg_index));
578 if (iter == call_expr_args_objects_.end()) {
579 llvm::errs() << "Didn't find object for argument " << arg_index
580 << " of constructor call:\n";
581 expr->dump();
582 llvm::errs() << "\n" << DebugString();
583 llvm::report_fatal_error(
584 "Didn't find object for argument of constructor call");
585 }
586 return iter->second;
587}
588
589Object ObjectRepository::GetCXXConstructExprThisPointer(
590 const clang::CXXConstructExpr* expr) const {
591 auto iter = call_expr_this_pointers_.find(expr);
592 if (iter == call_expr_this_pointers_.end()) {
593 llvm::errs() << "Didn't find `this` object for constructor:\n";
594 expr->dump();
595 llvm::errs() << "\n" << DebugString();
596 llvm::report_fatal_error("Didn't find `this` object for constructor");
597 }
598 return iter->second;
599}
600
601Object ObjectRepository::GetInitializedObject(
602 const clang::Expr* initializer_expr) const {
603 assert(clang::isa<clang::CXXConstructExpr>(initializer_expr) ||
604 clang::isa<clang::InitListExpr>(initializer_expr) ||
605 clang::isa<clang::CallExpr>(initializer_expr));
606
607 auto iter = initialized_objects_.find(initializer_expr);
608 if (iter == initialized_objects_.end()) {
609 llvm::errs() << "Didn't find object for initializer:\n";
610 initializer_expr->dump();
611 llvm::errs() << "\n" << DebugString();
612 llvm::report_fatal_error("Didn't find object for initializer");
613 }
614 return iter->second;
615}
616
617ObjectRepository::ObjectValueType ObjectRepository::GetObjectValueType(
618 Object object) const {
619 auto iter = lifetime_value_types_.find(object);
620 // If we don't know this lifetime, we conservatively assume it to be
621 // multi-valued.
622 if (iter == lifetime_value_types_.end()) {
623 return ObjectValueType::kMultiValued;
624 }
625 return iter->second;
626}
627
628Object ObjectRepository::GetFieldObject(Object struct_object,
629 const clang::FieldDecl* field) const {
630 std::optional<Object> field_object =
631 GetFieldObjectInternal(struct_object, field);
632 if (!field_object.has_value()) {
633 llvm::errs() << "On an object of type "
634 << struct_object.Type().getAsString()
635 << ", trying to get field:\n";
636 field->dump();
637 llvm::errs() << "\n" << DebugString();
638 llvm::report_fatal_error("Didn't find field object");
639 }
640 return *field_object;
641}
642
643ObjectSet ObjectRepository::GetFieldObject(
644 const ObjectSet& struct_objects, const clang::FieldDecl* field) const {
645 ObjectSet ret;
646 for (Object object : struct_objects) {
647 ret.Add(GetFieldObject(object, field));
648 }
649 return ret;
650}
651
652Object ObjectRepository::GetBaseClassObject(Object struct_object,
653 const clang::Type* base) const {
654 base = base->getCanonicalTypeInternal().getTypePtr();
655 auto iter = base_object_map_.find(std::make_pair(struct_object, base));
656 if (iter == base_object_map_.end()) {
657 llvm::errs() << "On object " << struct_object.DebugString()
658 << ", trying to get base:\n";
659 base->dump();
660 llvm::errs() << "\n" << DebugString();
661 llvm::report_fatal_error("Didn't find base object");
662 }
663 return iter->second;
664}
665
666ObjectSet ObjectRepository::GetBaseClassObject(const ObjectSet& struct_objects,
667 const clang::Type* base) const {
668 ObjectSet ret;
669 for (Object object : struct_objects) {
670 ret.Add(GetBaseClassObject(object, base));
671 }
672 return ret;
673}
674
675Object ObjectRepository::CreateStaticObject(clang::QualType type) {
676 auto iter = static_objects_.find(type);
677 if (iter != static_objects_.end()) {
678 return iter->second;
679 }
680
681 Object object = Object::Create(Lifetime::Static(), type);
682 static_objects_[type] = object;
683
684 CreateObjects(
Martin Brænne03d93302022-06-23 00:23:38 -0700685 object, type, [](const clang::Expr*) { return Lifetime::Static(); },
686 true);
Luca Versari99fddff2022-05-25 10:22:32 -0700687
688 return object;
689}
690
691void ObjectRepository::CreateObjects(Object root_object, clang::QualType type,
692 LifetimeFactory lifetime_factory,
693 bool transitive) {
694 class Visitor : public LifetimeVisitor {
695 public:
696 Visitor(ObjectRepository::FieldObjects& field_object_map,
697 ObjectRepository::BaseObjects& base_object_map,
698 PointsToMap& initial_points_to_map, bool create_transitive_objects)
699 : field_object_map_(field_object_map),
700 base_object_map_(base_object_map),
701 initial_points_to_map_(initial_points_to_map),
702 create_transitive_objects_(create_transitive_objects) {}
703
704 Object GetFieldObject(const ObjectSet& objects,
705 const clang::FieldDecl* field) override {
706 assert(!objects.empty());
707 std::optional<Object> field_object = std::nullopt;
708
709 for (Object object : objects) {
710 if (auto iter = field_object_map_.find(std::make_pair(object, field));
711 iter != field_object_map_.end()) {
712 field_object = iter->second;
713 }
714 }
715 if (!field_object.has_value()) {
716 field_object =
717 Object::Create((*objects.begin()).GetLifetime(), field->getType());
718 }
719 for (Object object : objects) {
720 field_object_map_[std::make_pair(object, field)] = *field_object;
721 }
722 return *field_object;
723 }
724
725 Object GetBaseClassObject(const ObjectSet& objects,
726 clang::QualType base) override {
727 assert(!objects.empty());
728 base = base.getCanonicalType();
729 std::optional<Object> base_object = std::nullopt;
730
731 for (Object object : objects) {
732 if (auto iter = base_object_map_.find(std::make_pair(object, &*base));
733 iter != base_object_map_.end()) {
734 base_object = iter->second;
735 }
736 }
737 if (!base_object.has_value()) {
738 base_object = Object::Create((*objects.begin()).GetLifetime(), base);
739 }
740 for (Object object : objects) {
741 base_object_map_[std::make_pair(object, &*base)] = *base_object;
742 }
743 return *base_object;
744 }
745
746 ObjectSet Traverse(const ObjectLifetimes& lifetimes,
747 const ObjectSet& objects,
748 int /*pointee_depth*/) override {
749 if (!create_transitive_objects_) return {};
750 if (PointeeType(lifetimes.GetValueLifetimes().Type()).isNull()) {
751 return {};
752 }
753
754 const auto& cache_key =
755 lifetimes.GetValueLifetimes().GetPointeeLifetimes();
756
757 Object child_pointee;
758 if (auto iter = object_cache_.find(cache_key);
759 iter == object_cache_.end()) {
760 child_pointee = Object::Create(
761 lifetimes.GetValueLifetimes().GetPointeeLifetimes().GetLifetime(),
762 PointeeType(lifetimes.GetValueLifetimes().Type()));
763 object_cache_[cache_key] = child_pointee;
764 } else {
765 child_pointee = iter->second;
766 }
767
768 initial_points_to_map_.SetPointerPointsToSet(objects, {child_pointee});
769 return ObjectSet{child_pointee};
770 }
771
772 private:
773 ObjectRepository::FieldObjects& field_object_map_;
774 ObjectRepository::BaseObjects& base_object_map_;
775 PointsToMap& initial_points_to_map_;
776 bool create_transitive_objects_;
777 // Inside of a given VarDecl, we re-use the same Object for all the
778 // sub-objects with the same type and lifetimes. This avoids infinite loops
779 // in the case of structs like lists.
780 llvm::DenseMap<ObjectLifetimes, Object> object_cache_;
781 };
782 Visitor visitor(field_object_map_, base_object_map_, initial_points_to_map_,
783 transitive);
784 VisitLifetimes(
785 {root_object}, type,
786 ObjectLifetimes(root_object.GetLifetime(),
787 ValueLifetimes::Create(type, lifetime_factory).get()),
788 visitor);
789}
790
791// Clones an object and its base classes and fields, if any.
792Object ObjectRepository::CloneObject(Object object) {
793 struct ObjectPair {
794 Object orig_object;
795 Object new_object;
796 };
797 auto clone = [this](Object obj) {
798 auto new_obj = Object::Create(obj.GetLifetime(), obj.Type());
799 initial_points_to_map_.SetPointerPointsToSet(
800 new_obj, initial_points_to_map_.GetPointerPointsToSet(obj));
801 return new_obj;
802 };
803 Object new_root = clone(object);
804 std::vector<ObjectPair> object_stack{{object, new_root}};
805 while (!object_stack.empty()) {
806 auto [orig_object, new_object] = object_stack.back();
807 assert(orig_object.Type() == new_object.Type());
808 object_stack.pop_back();
809 auto record_type = orig_object.Type()->getAs<clang::RecordType>();
810 if (!record_type) {
811 continue;
812 }
813
814 // Base classes.
815 if (auto* cxxrecord =
816 clang::dyn_cast<clang::CXXRecordDecl>(record_type->getDecl())) {
817 for (const clang::CXXBaseSpecifier& base : cxxrecord->bases()) {
818 auto base_obj = GetBaseClassObject(orig_object, base.getType());
819 Object new_base_obj = clone(base_obj);
820 base_object_map_[std::make_pair(
821 new_object, base.getType().getCanonicalType().getTypePtr())] =
822 new_base_obj;
823 object_stack.push_back(ObjectPair{base_obj, new_base_obj});
824 }
825 }
826
827 // Fields.
828 for (auto f : record_type->getDecl()->fields()) {
829 auto field_obj = GetFieldObject(orig_object, f);
830 Object new_field_obj = clone(field_obj);
831 field_object_map_[std::make_pair(new_object, f)] = new_field_obj;
832 object_stack.push_back(ObjectPair{field_obj, new_field_obj});
833 }
834 }
835 return new_root;
836}
837
838std::optional<Object> ObjectRepository::GetFieldObjectInternal(
839 Object struct_object, const clang::FieldDecl* field) const {
840 auto iter = field_object_map_.find(std::make_pair(struct_object, field));
841 if (iter != field_object_map_.end()) {
842 return iter->second;
843 }
844 if (auto* cxxrecord = clang::dyn_cast<clang::CXXRecordDecl>(
845 struct_object.Type()->getAs<clang::RecordType>()->getDecl())) {
846 for (const clang::CXXBaseSpecifier& base : cxxrecord->bases()) {
847 std::optional<Object> field_object = GetFieldObjectInternal(
848 GetBaseClassObject(struct_object, base.getType()), field);
849 if (field_object.has_value()) {
850 return field_object;
851 }
852 }
853 }
854 return std::nullopt;
855}
856
857} // namespace lifetimes
858} // namespace tidy
859} // namespace clang