blob: fcc4b986ea2391320e4dfa86646e60da1f5d1ed7 [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 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_