| // 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 <cstddef> |
| #include <optional> |
| #include <string> |
| #include <utility> |
| |
| #include "lifetime_analysis/object.h" |
| #include "lifetime_analysis/object_set.h" |
| #include "lifetime_analysis/points_to_map.h" |
| #include "lifetime_annotations/function_lifetimes.h" |
| #include "lifetime_annotations/lifetime.h" |
| #include "lifetime_annotations/type_lifetimes.h" |
| #include "clang/AST/Decl.h" |
| #include "clang/AST/Expr.h" |
| #include "clang/AST/ExprCXX.h" |
| #include "clang/AST/Type.h" |
| #include "llvm/ADT/DenseMap.h" |
| #include "llvm/Support/Allocator.h" |
| #include "llvm/Support/Error.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(); |
| } |
| |
| // Returns the lifetimes of the given FunctionDecl, or an error if they are |
| // unknown or analysis failed on that FunctionDecl. |
| FunctionLifetimesOrError GetFunctionLifetimes( |
| const FunctionDecl* decl, |
| const llvm::DenseMap<const FunctionDecl*, FunctionLifetimesOrError>& |
| known_lifetimes); |
| |
| // A repository for the objects used in the lifetime analysis of a single |
| // function. |
| // This class establishes a relationship between AST nodes (e.g. variable |
| // declarations) and the objects that represent them. It also stores additional |
| // information about objects that does not change during the analysis. |
| // The `ObjectRepository` only stores state that does not change during the |
| // analysis; it is therefore not part of the lattice. |
| class ObjectRepository { |
| private: |
| using MapType = llvm::DenseMap<const clang::ValueDecl*, const Object*>; |
| |
| public: |
| // 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<const Object*, const clang::FieldDecl*>, |
| const Object*>; |
| |
| // Maps a given struct-Object to the Object for each of its bases. |
| using BaseObjects = |
| llvm::DenseMap<std::pair<const Object*, const clang::Type*>, |
| const Object*>; |
| |
| // Iterator refers to a pair consisting of a variable declaration and the |
| // object representing that variable. |
| using const_iterator = MapType::const_iterator; |
| using value_type = MapType::value_type; |
| |
| // Creates an ObjectRepository with all relevant objects for the analysis |
| // of `func`. This includes: |
| // - Variables that are declared or referenced in `func`. |
| // - Functions that have their address taken in `func`. |
| // - String literals. |
| static llvm::Expected<ObjectRepository> Create( |
| const clang::FunctionDecl* func, |
| const llvm::DenseMap<const clang::FunctionDecl*, |
| FunctionLifetimesOrError>& callee_lifetimes); |
| |
| // 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. |
| const Object* GetDeclObject(const clang::ValueDecl* decl) const; |
| |
| // Returns the object associated with a materialize temporary expression. |
| const 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. |
| const Object* GetOriginalParameterValue( |
| const clang::ParmVarDecl* var_decl) const; |
| |
| // Synthetizes a FunctionLifetimes that has as lifetimes the initial lifetimes |
| // of the function currently being analyzed, before any analysis runs. |
| FunctionLifetimes GetOriginalFunctionLifetimes() const; |
| |
| // Returns the object associated with an argument to a CallExpr. |
| const Object* GetCallExprArgumentObject(const clang::CallExpr* expr, |
| size_t arg_index) const; |
| |
| // Returns the object associated with the return value of a CallExpr. |
| const Object* GetCallExprRetObject(const clang::Expr* expr) const; |
| |
| // Returns the "virtual" lifetimes for a given function call. |
| const FunctionLifetimes& GetCallExprVirtualLifetimes( |
| const clang::Expr* expr) 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. |
| const Object* GetCallExprThisPointer(const clang::CallExpr* expr) const; |
| |
| // Returns the object associated with an argument to a CXXConstructExpr. |
| const 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 represented by the |
| // object from GetResultObject()). |
| const 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(). |
| const Object* GetResultObject(const clang::Expr* initializer_expr) const; |
| |
| // Returns the object that represents `*this`, if in a member function. |
| std::optional<const 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. |
| const Object* GetReturnObject() const { return return_object_; } |
| |
| // Returns an object that represents (the lifetimes of) any string literal. |
| const Object* GetStringLiteralObject() const { |
| return string_literal_object_; |
| } |
| |
| // Returns the object associated with a given field in the struct |
| // represented by `struct_object`. |
| const Object* GetFieldObject(const 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`. |
| const Object* GetBaseClassObject(const Object* struct_object, |
| const clang::Type* base) const; |
| const Object* GetBaseClassObject(const 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; |
| ObjectSet GetBaseClassObject(const ObjectSet& struct_objects, |
| const clang::QualType base) const { |
| return GetBaseClassObject(struct_objects, base.getTypePtr()); |
| } |
| |
| // 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_; |
| } |
| |
| // Returns the single-valued object set implied by variable declarations, i.e. |
| // assuming that no code has been executed yet. |
| const ObjectSet& InitialSingleValuedObjects() const { |
| return initial_single_valued_objects_; |
| } |
| |
| // Creates an object (and its related objects, such as objects for its fields, |
| // bases or pointees) with the given lifetimes. Points-to relations will be |
| // added to the given points_to_map. |
| // The returned Object will live as long as this ObjectRepository. |
| // TODO(veluca): this is currently public just because of |
| // TransferDefaultConstructor in analyze.cc; in principle such objects |
| // could/should be created in advance. |
| const Object* CreateObject(const ObjectLifetimes& object_lifetimes, |
| PointsToMap& points_to_map); |
| |
| private: |
| ObjectRepository() = default; |
| |
| // Creates an object (and related objects) for a given `type`; lifetimes of |
| // created objects are defined by the given |
| // `lifetime_factory`/`root_object_lifetime`, and the |
| // resulting points-to relations are stored in the ObjectRepository's |
| // initial_points_to_map_. |
| const Object* CreateObject(clang::QualType type, |
| Lifetime root_object_lifetime, |
| LifetimeFactory lifetime_factory); |
| |
| template <typename... Args> |
| const Object* ConstructObject(Args&&... args); |
| |
| const Object* CloneObject(const Object* object); |
| |
| std::optional<const Object*> GetFieldObjectInternal( |
| const Object* struct_object, const clang::FieldDecl* field) const; |
| |
| // Owns all the `const Object*` members of the object repository. |
| llvm::SpecificBumpPtrAllocator<Object> object_allocator_; |
| |
| // 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*, const Object*> |
| temporary_objects_; |
| |
| // Map from each function parameter to an object representing its initial |
| // value at function entry. |
| llvm::DenseMap<const clang::ParmVarDecl*, const Object*> |
| initial_parameter_object_; |
| |
| // Map from each initializer (constructors or initializer lists) to the result |
| // 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*, const Object*> result_objects_; |
| |
| std::optional<const Object*> this_object_; |
| const Object* return_object_; |
| |
| ObjectSet initial_single_valued_objects_; |
| |
| class VarDeclVisitor; |
| |
| PointsToMap initial_points_to_map_; |
| FieldObjects field_object_map_; |
| BaseObjects base_object_map_; |
| |
| llvm::DenseMap<std::pair<const clang::Expr*, size_t>, const Object*> |
| call_expr_args_objects_; |
| |
| llvm::DenseMap<const clang::Expr*, const Object*> call_expr_this_pointers_; |
| |
| llvm::DenseMap<const clang::Expr*, const Object*> call_expr_ret_objects_; |
| |
| llvm::DenseMap<const clang::Expr*, FunctionLifetimes> |
| call_expr_virtual_lifetimes_; |
| |
| const Object* string_literal_object_ = nullptr; |
| |
| const clang::FunctionDecl* func_ = nullptr; |
| |
| llvm::DenseMap<const Object*, ObjectLifetimes> initial_object_lifetimes_; |
| |
| class ObjectCreator; |
| }; |
| |
| } // namespace lifetimes |
| } // namespace tidy |
| } // namespace clang |
| |
| #endif // DEVTOOLS_RUST_CC_INTEROP_LIFETIME_ANALYSIS_OBJECT_REPOSITORY_H_ |