blob: f0e7d07c5ea32141d98ac42cb65672552bbbaf51 [file] [log] [blame]
// 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_