Open-source lifetime inference/verification code.
PiperOrigin-RevId: 450954978
diff --git a/lifetime_analysis/object_repository.h b/lifetime_analysis/object_repository.h
new file mode 100644
index 0000000..6a36522
--- /dev/null
+++ b/lifetime_analysis/object_repository.h
@@ -0,0 +1,235 @@
+// Part of the Crubit project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef DEVTOOLS_RUST_CC_INTEROP_LIFETIME_ANALYSIS_OBJECT_REPOSITORY_H_
+#define DEVTOOLS_RUST_CC_INTEROP_LIFETIME_ANALYSIS_OBJECT_REPOSITORY_H_
+
+#include <functional>
+#include <optional>
+#include <string>
+#include <variant>
+
+#include "lifetime_analysis/object.h"
+#include "lifetime_analysis/object_set.h"
+#include "lifetime_analysis/points_to_map.h"
+#include "lifetime_annotations/type_lifetimes.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "llvm/ADT/DenseMap.h"
+
+namespace clang {
+namespace tidy {
+namespace lifetimes {
+
+// A record-type expression has 2 modes:
+// 1. If it's being assigned to a reference, then the contents of the expression
+// are a glvalue. This is because references require an object to point to.
+// 2. If it's being assigned to a record object, then the expression itself is
+// not creating an object, but initializing it. So the expression's type is
+// a pure value, and it acts _on_ the initializing object instead of
+// producing an object.
+inline bool IsInitExprInitializingARecordObject(const clang::Expr* expr) {
+ return expr->getType()->isRecordType() && expr->isPRValue();
+}
+
+// A repository for the objects used in the lifetime analysis of a single
+// function.
+class ObjectRepository {
+ public:
+ // An `Object` might represent objects that have either a single value (such
+ // as plain variables) or multiple ones (such as arrays, or structs).
+ // Assignment behaves differently in the two cases.
+ enum class ObjectValueType {
+ kSingleValued,
+ kMultiValued,
+ };
+
+ // Tag struct for InitializedObject: the object being initialized is the
+ // return value of the function.
+ struct ReturnValue {};
+
+ // Maps a given struct-Object to the Object for each of its fields.
+ // TODO(veluca): this approach does not produce correct results when
+ // diamond-problem-style multiple inheritance happens.
+ using FieldObjects =
+ llvm::DenseMap<std::pair<Object, const clang::FieldDecl*>, Object>;
+
+ // Maps a given struct-Object to the Object for each of its bases.
+ using BaseObjects =
+ llvm::DenseMap<std::pair<Object, const clang::Type*>, Object>;
+
+ private:
+ using MapType = llvm::DenseMap<const clang::ValueDecl*, Object>;
+ // Map from each variable declaration to the object which it declares.
+ MapType object_repository_;
+
+ // Map from each materialized temporary to the object which it declares.
+ llvm::DenseMap<const clang::MaterializeTemporaryExpr*, Object>
+ temporary_objects_;
+
+ // Map from each function parameter to an object representing its initial
+ // value at function entry.
+ llvm::DenseMap<const clang::ParmVarDecl*, Object> initial_parameter_object_;
+
+ // Map from each initializer (constructors or initializer lists) to the object
+ // which it initializes.
+ //
+ // An object in this map may occur in other places too: `object_repository_`
+ // if it is an lvalue, or `return_object_`. Or it may be a temporary in which
+ // case it is only found in this map.
+ llvm::DenseMap<const clang::Expr*, Object> initialized_objects_;
+
+ std::optional<Object> this_object_;
+ Object return_object_;
+
+ llvm::DenseMap<Object, ObjectValueType> lifetime_value_types_;
+
+ class VarDeclVisitor;
+
+ PointsToMap initial_points_to_map_;
+ FieldObjects field_object_map_;
+ BaseObjects base_object_map_;
+
+ llvm::DenseMap<std::pair<const clang::Expr*, size_t>, Object>
+ call_expr_args_objects_;
+
+ llvm::DenseMap<const clang::Expr*, Object> call_expr_this_pointers_;
+
+ llvm::DenseMap<clang::QualType, Object> static_objects_;
+
+ public:
+ using const_iterator = MapType::const_iterator;
+ using value_type = MapType::value_type;
+
+ // Initializes the map with objects for all variables that are declared or
+ // referenced in `func`.
+ explicit ObjectRepository(const clang::FunctionDecl* func);
+
+ // Move-only.
+ ObjectRepository(ObjectRepository&&) = default;
+ ObjectRepository& operator=(ObjectRepository&&) = default;
+
+ // Returns a human-readable representation of the mapping.
+ std::string DebugString() const;
+
+ const_iterator begin() const { return object_repository_.begin(); }
+ const_iterator end() const { return object_repository_.end(); }
+
+ // Returns the object associated with a variable or function.
+ Object GetDeclObject(const clang::ValueDecl* decl) const;
+
+ // Returns the object associated with a materialize temporary expression.
+ Object GetTemporaryObject(const clang::MaterializeTemporaryExpr* expr) const;
+
+ // Returns the object representing the value of a function parameter at
+ // function entry.
+ // Note: This `Object` does not represent the parameter variable itself;
+ // use GetDeclObject() to retrieve that. We're using an `Object` here
+ // because we don't have a dedicated "value" class, but you should not
+ // use this object's identity in any way; i.e. no other `Object` in the
+ // points-to map should ever point to the object returned by this
+ // function.
+ Object GetOriginalParameterValue(const clang::ParmVarDecl* var_decl) const;
+
+ // Returns the object associated with an argument to a CallExpr.
+ Object GetCallExprArgumentObject(const clang::CallExpr* expr,
+ size_t arg_index) const;
+
+ // Returns the object associated with the `this` argument to a CallExpr that
+ // represents a method call. Note that this object represents the `this`
+ // pointer, not the object that the method is being called on.
+ Object GetCallExprThisPointer(const clang::CallExpr* expr) const;
+
+ // Returns the object associated with an argument to a CXXConstructExpr.
+ Object GetCXXConstructExprArgumentObject(const clang::CXXConstructExpr* expr,
+ size_t arg_index) const;
+
+ // Returns the object associated with the `this` argument to a
+ // CXXConstructExpr. Note that this object represents the `this` pointer, not
+ // the object that the method is being called on (which is represnted by the
+ // object from GetInitializedObject()).
+ Object GetCXXConstructExprThisPointer(
+ const clang::CXXConstructExpr* expr) const;
+
+ // Returns the object associated with, and initialized by, a constructor call
+ // (CXXConstructExpr) or a initializer list (CXXInitListExpr). Note that this
+ // represents the actual class object being initialized, not the `this`
+ // pointer to it that is passed to methods of the class, and which is
+ // represented by the object from GetCXXConstructExprThisPointer().
+ Object GetInitializedObject(const clang::Expr* initializer_expr) const;
+
+ // Returns what kind of values the given object represents.
+ ObjectValueType GetObjectValueType(Object object) const;
+
+ // Returns the object that represents `*this`, if in a member function.
+ std::optional<Object> GetThisObject() const { return this_object_; }
+
+ // Returns the `Object` associated with the return value of the function.
+ // Unlike the `Object`s for variables, the "return value object" is a fiction
+ // -- there is not, in general, going to be a single object associated with
+ // the return value, and it will not, in general, be possible to take the
+ // address of the return value object. It's still a useful fiction, however,
+ // because it allows us to treat return values the same way as other values.
+ Object GetReturnObject() const { return return_object_; }
+
+ // Returns the object associated with a given field in the struct
+ // represented by `struct_object`.
+ Object GetFieldObject(Object struct_object,
+ const clang::FieldDecl* field) const;
+
+ // Returns the objects associated with a given field in the structs
+ // represented by `struct_objects`.
+ ObjectSet GetFieldObject(const ObjectSet& struct_objects,
+ const clang::FieldDecl* field) const;
+
+ // Returns FieldObjects; useful for producing debugging output.
+ const FieldObjects& GetFieldObjects() const { return field_object_map_; }
+
+ // Returns the object associated with a given base of the struct
+ // represented by `struct_object`.
+ Object GetBaseClassObject(Object struct_object,
+ const clang::Type* base) const;
+ Object GetBaseClassObject(Object struct_object,
+ const clang::QualType base) const {
+ return GetBaseClassObject(struct_object, base.getTypePtr());
+ }
+
+ // Returns the objects associated with a given base of the structs
+ // represented by `struct_object`.
+ ObjectSet GetBaseClassObject(const ObjectSet& struct_objects,
+ const clang::Type* base) const;
+
+ // Returns BaseObjects; useful for producing debugging output.
+ const BaseObjects& GetBaseObjects() const { return base_object_map_; }
+
+ // Returns the PointsToMap implied by variable declarations, i.e. assuming
+ // that no code has been executed yet.
+ const PointsToMap& InitialPointsToMap() const {
+ return initial_points_to_map_;
+ }
+
+ // Creates and returns an object with static lifetime of the given type.
+ // Also creates any transitive objects if required.
+ // When called multiple times with the same `type`, this function always
+ // returns the same object. This is to guarantee that the number of objects
+ // used in the analysis is bounded and that therefore the lattice is finite
+ // and the analysis terminates.
+ Object CreateStaticObject(clang::QualType type);
+
+ private:
+ void CreateObjects(Object root_object, clang::QualType type,
+ LifetimeFactory lifetime_factory, bool transitive);
+
+ Object CloneObject(Object object);
+
+ std::optional<Object> GetFieldObjectInternal(
+ Object struct_object, const clang::FieldDecl* field) const;
+};
+
+} // namespace lifetimes
+} // namespace tidy
+} // namespace clang
+
+#endif // DEVTOOLS_RUST_CC_INTEROP_LIFETIME_ANALYSIS_OBJECT_REPOSITORY_H_