| // 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 CRUBIT_LIFETIME_ANNOTATIONS_FUNCTION_LIFETIMES_H_ | |
| #define CRUBIT_LIFETIME_ANNOTATIONS_FUNCTION_LIFETIMES_H_ | |
| #include <cassert> | |
| #include <cstddef> | |
| #include <functional> | |
| #include <iosfwd> | |
| #include <optional> | |
| #include <string> | |
| #include <utility> | |
| #include <variant> | |
| #include "lifetime_annotations/lifetime.h" | |
| #include "lifetime_annotations/lifetime_substitutions.h" | |
| #include "lifetime_annotations/type_lifetimes.h" | |
| #include "clang/AST/Decl.h" | |
| #include "clang/AST/DeclCXX.h" | |
| #include "clang/AST/Type.h" | |
| #include "clang/AST/TypeLoc.h" | |
| #include "llvm/ADT/DenseMap.h" | |
| #include "llvm/ADT/DenseSet.h" | |
| #include "llvm/ADT/SmallVector.h" | |
| #include "llvm/ADT/StringRef.h" | |
| #include "llvm/Support/Error.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| namespace clang { | |
| namespace tidy { | |
| namespace lifetimes { | |
| // Interface used to create lifetimes in FunctionLifetimes::CreateForDecl. | |
| // CreateReturnLifetimes will be called with the ValueLifetimes that were | |
| // created through calls to CreateParamLifetimes. | |
| class FunctionLifetimeFactory { | |
| public: | |
| virtual ~FunctionLifetimeFactory() = default; | |
| virtual llvm::Expected<ValueLifetimes> CreateThisLifetimes( | |
| clang::QualType type, const clang::Expr* lifetime_name) const = 0; | |
| // Note: The `type_loc` parameter passed into `CreateParamLifetimes` and | |
| // `CreateReturnLifetimes` may be null if no type location is available. | |
| virtual llvm::Expected<ValueLifetimes> CreateParamLifetimes( | |
| clang::QualType type, clang::TypeLoc type_loc) const = 0; | |
| virtual llvm::Expected<ValueLifetimes> CreateReturnLifetimes( | |
| clang::QualType type, clang::TypeLoc type_loc, | |
| const llvm::SmallVector<ValueLifetimes>& param_lifetimes, | |
| const std::optional<ValueLifetimes>& this_lifetimes) const = 0; | |
| }; | |
| // Implementation of FunctionLifetimeFactory that defers to a LifetimeFactory. | |
| class FunctionLifetimeFactorySingleCallback : public FunctionLifetimeFactory { | |
| public: | |
| explicit FunctionLifetimeFactorySingleCallback(LifetimeFactory factory) | |
| : factory_(std::move(factory)) {} | |
| llvm::Expected<ValueLifetimes> CreateThisLifetimes( | |
| clang::QualType type, | |
| const clang::Expr* /*lifetime_name*/) const override { | |
| // TODO(mboehme): There's currently no way for us to pass `lifetime_name` on | |
| // into `ValueLifetimes::Create()`. We may need to add another overload of | |
| // `ValueLifetimes::Create()` if we ever need this. | |
| return ValueLifetimes::Create(type, TypeLoc(), factory_); | |
| } | |
| llvm::Expected<ValueLifetimes> CreateParamLifetimes( | |
| clang::QualType type, clang::TypeLoc type_loc) const override { | |
| return ValueLifetimes::Create(type, type_loc, factory_); | |
| } | |
| llvm::Expected<ValueLifetimes> CreateReturnLifetimes( | |
| clang::QualType type, clang::TypeLoc type_loc, | |
| const llvm::SmallVector<ValueLifetimes>& /*param_lifetimes*/, | |
| const std::optional<ValueLifetimes>& /*this_lifetimes*/) const override { | |
| return ValueLifetimes::Create(type, type_loc, factory_); | |
| } | |
| private: | |
| LifetimeFactory factory_; | |
| }; | |
| // Lifetimes for the signature of a function. | |
| class FunctionLifetimes { | |
| public: | |
| // Returns lifetimes for the `i`-th parameter. | |
| // These are the same number and order as FunctionDecl::parameters(). | |
| const ValueLifetimes& GetParamLifetimes(size_t i) const { | |
| return param_lifetimes_[i]; | |
| } | |
| // Returns the number of function parameters (excluding the implicit `this). | |
| size_t GetNumParams() const { return param_lifetimes_.size(); } | |
| // Lifetimes for the return type. | |
| const ValueLifetimes& GetReturnLifetimes() const { return return_lifetimes_; } | |
| // Lifetimes for the `this` parameter for non-static member functions. | |
| const ValueLifetimes& GetThisLifetimes() const { | |
| assert(this_lifetimes_.has_value()); | |
| return *this_lifetimes_; | |
| } | |
| // Returns whether this FunctionLifetimes represents a non-static method. | |
| bool IsNonStaticMethod() const { return this_lifetimes_.has_value(); } | |
| // Creates lifetimes for a function with a given decl. | |
| // Only fails if lifetime_factory fails. | |
| // Lifetimes will be created first for the object parameter, if any, then for | |
| // parameters in increasing order, and finally for the return type. | |
| static llvm::Expected<FunctionLifetimes> CreateForDecl( | |
| const clang::FunctionDecl* function, | |
| const FunctionLifetimeFactory& lifetime_factory); | |
| static llvm::Expected<FunctionLifetimes> CreateForFunctionType( | |
| const clang::FunctionProtoType* function, clang::TypeLoc func_type_loc, | |
| const FunctionLifetimeFactory& lifetime_factory); | |
| static llvm::Expected<FunctionLifetimes> CreateForFunctionType( | |
| const clang::FunctionProtoType* function, | |
| const FunctionLifetimeFactory& lifetime_factory); | |
| // TODO(veluca): add support for pointer-to-member-fn. | |
| // Creates a copy of this FunctionLifetimes with the same structure, but | |
| // fresh, unrelated lifetimes independently of whether the lifetimes where | |
| // identical in this FunctionLifetimes. | |
| // TODO(veluca): remove this method once FunctionLifetimes keeps track of its | |
| // own type, and replace it with an appropriate call to Create(). | |
| llvm::Expected<FunctionLifetimes> CreateCopy( | |
| const LifetimeFactory& factory) const; | |
| // Returns FunctionLifetimes for a method that this method overrides. | |
| // Precondition: `IsNonStaticMethod()` is true, | |
| // `method`'s signature is compatible with this FunctionLifetimes except for | |
| // the `this` parameter. | |
| FunctionLifetimes ForOverriddenMethod(const clang::CXXMethodDecl* method); | |
| // Checks if this FunctionLifetimes represents valid lifetimes for the given | |
| // Decl. | |
| bool IsValidForDecl(const clang::FunctionDecl* function); | |
| // Returns a human-readable representation of `func_lifetimes`. Formats | |
| // lifetimes using `formatter`, or Lifetime::DebugString() if `formatter` is | |
| // null. | |
| std::string DebugString(LifetimeFormatter formatter = [](Lifetime l) { | |
| return l.DebugString(); | |
| }) const; | |
| // Returns true if `predicate` returns true for any lifetime that appears in | |
| // the `FunctionLifetimes`. | |
| bool HasAny(const std::function<bool(Lifetime)>& predicate) const; | |
| // Returns the set of all lifetimes that are either lifetime parameters of | |
| // this function, or (if this FunctionLifetimes is a declaration of a method) | |
| // of the enclosing class. | |
| llvm::DenseSet<Lifetime> AllFreeLifetimes() const; | |
| // Applies `subst` to all lifetimes in this FunctionLifetimes. | |
| // Any lifetime parameter declarations will moved to the innermost location | |
| // that is valid for the new lifetimes. Note that this operation is | |
| // well-defined and declarations of lifetime parameters can only "move up"; | |
| // in particular, it results lifetime parameters being as tightly bound as | |
| // possible, which is what we want inference to infer. | |
| void SubstituteLifetimes(const LifetimeSubstitutions& subst); | |
| // Traverses all the lifetimes in the function signature, recursively. The | |
| // visit is done in post-order on the lifetime tree of this type. | |
| void Traverse(std::function<void(Lifetime&, Variance)> visitor); | |
| void Traverse(std::function<void(const Lifetime&, Variance)> visitor) const; | |
| private: | |
| llvm::SmallVector<ValueLifetimes> param_lifetimes_; | |
| ValueLifetimes return_lifetimes_; | |
| std::optional<ValueLifetimes> this_lifetimes_; | |
| friend class ObjectRepository; | |
| static llvm::Expected<FunctionLifetimes> Create( | |
| const clang::FunctionProtoType* type, clang::TypeLoc type_loc, | |
| clang::QualType this_type, | |
| const FunctionLifetimeFactory& lifetime_factory); | |
| }; | |
| std::ostream& operator<<(std::ostream& os, | |
| const FunctionLifetimes& func_lifetimes); | |
| // An error that occurred while analyzing a function. | |
| struct FunctionAnalysisError { | |
| explicit FunctionAnalysisError(llvm::StringRef message = "") | |
| : message(message) {} | |
| explicit FunctionAnalysisError(const llvm::Error& err) { | |
| ::llvm::raw_string_ostream stream(message); | |
| stream << err; | |
| } | |
| // Human-readable description of the error. | |
| std::string message; | |
| }; | |
| // Lifetimes for a function, or an error if we couldn't analyze the function. | |
| // We can't use llvm::Expected<FunctionLifetimes> for this because: | |
| // - llvm::Expected doesn't allow us to check for an error state without moving | |
| // the error out of the llvm::Expected | |
| // - llvm::Expected asserts in the destructor if we didn't check for an error | |
| using FunctionLifetimesOrError = | |
| std::variant<FunctionAnalysisError, FunctionLifetimes>; | |
| using FunctionLifetimesMap = | |
| llvm::DenseMap<const FunctionDecl*, FunctionLifetimesOrError>; | |
| } // namespace lifetimes | |
| } // namespace tidy | |
| } // namespace clang | |
| #endif // CRUBIT_LIFETIME_ANNOTATIONS_FUNCTION_LIFETIMES_H_ |