Remove TypeLifetimes.
This still might need modifications for supporting annotate_type (in particular, potentially changing Type to TypeLoc).
PiperOrigin-RevId: 436992434
diff --git a/lifetime_annotations/function_lifetimes.cc b/lifetime_annotations/function_lifetimes.cc
index 85e00f3..0f8d0cc 100644
--- a/lifetime_annotations/function_lifetimes.cc
+++ b/lifetime_annotations/function_lifetimes.cc
@@ -11,11 +11,14 @@
#include "lifetime_annotations/lifetime.h"
#include "lifetime_annotations/type_lifetimes.h"
#include "third_party/llvm/llvm-project/clang/include/clang/AST/DeclCXX.h"
+#include "third_party/llvm/llvm-project/clang/include/clang/AST/Type.h"
#include "third_party/llvm/llvm-project/clang/include/clang/Basic/LLVM.h"
+#include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/SmallVector.h"
+#include "third_party/llvm/llvm-project/llvm/include/llvm/Support/Error.h"
namespace devtools_rust {
-namespace {
+namespace {
// Track a bijective mapping between 2 sets of Lifetimes.
class LifetimeBijection {
public:
@@ -40,50 +43,114 @@
} // namespace
-bool FunctionLifetimes::ContainsLifetimes() {
- for (const auto& param : param_lifetimes) {
- if (!param.empty()) {
- return true;
+llvm::Expected<FunctionLifetimes> FunctionLifetimes::CreateForDecl(
+ const clang::FunctionDecl* func,
+ const FunctionLifetimeFactory& lifetime_factory) {
+ clang::QualType this_type;
+ if (auto method = clang::dyn_cast<clang::CXXMethodDecl>(func);
+ method && !method->isStatic()) {
+ this_type = method->getThisType();
+ }
+ return Create(func->getType()->getAs<clang::FunctionProtoType>(), this_type,
+ lifetime_factory);
+}
+
+bool FunctionLifetimes::IsValidForDecl(const clang::FunctionDecl* function) {
+ // TODO(veluca): also validate the types of the arguments, and/or the type of
+ // the function itself.
+ if (auto method = clang::dyn_cast<clang::CXXMethodDecl>(function);
+ method && !method->isStatic()) {
+ if (!this_lifetimes_.has_value()) return false;
+ }
+ return param_lifetimes_.size() == function->param_size();
+}
+
+llvm::Expected<FunctionLifetimes> FunctionLifetimes::Create(
+ const clang::FunctionProtoType* type, const clang::QualType this_type,
+ const FunctionLifetimeFactory& lifetime_factory) {
+ FunctionLifetimes ret;
+
+ if (!this_type.isNull()) {
+ ValueLifetimes tmp;
+ if (llvm::Error err =
+ ValueLifetimes::Create(this_type, [&](clang::QualType type,
+ llvm::StringRef param) {
+ return lifetime_factory.CreateParamLifetime(type, param);
+ }).moveInto(tmp)) {
+ return std::move(err);
}
+ ret.this_lifetimes_ = std::move(tmp);
}
- return !return_lifetimes.empty() || !this_lifetimes.empty();
+ ret.param_lifetimes_.reserve(type->getNumParams());
+ for (size_t i = 0; i < type->getNumParams(); i++) {
+ ValueLifetimes tmp;
+ if (llvm::Error err = ValueLifetimes::Create(
+ type->getParamType(i),
+ [&](clang::QualType type, llvm::StringRef param) {
+ return lifetime_factory.CreateParamLifetime(
+ type, param);
+ })
+ .moveInto(tmp)) {
+ return std::move(err);
+ }
+ ret.param_lifetimes_.push_back(std::move(tmp));
+ }
+
+ if (llvm::Error err = ValueLifetimes::Create(
+ type->getReturnType(),
+ [&](clang::QualType type, llvm::StringRef param) {
+ return lifetime_factory.CreateReturnLifetime(
+ type, param, ret.param_lifetimes_,
+ ret.this_lifetimes_);
+ })
+ .moveInto(ret.return_lifetimes_)) {
+ return std::move(err);
+ }
+
+ return ret;
+}
+
+void FunctionLifetimes::Traverse(
+ std::function<void(Lifetime&, Variance)> visitor) {
+ for (auto& param : param_lifetimes_) {
+ param.Traverse(visitor);
+ }
+ return_lifetimes_.Traverse(visitor);
+ if (this_lifetimes_.has_value()) {
+ this_lifetimes_->Traverse(visitor);
+ }
+}
+
+void FunctionLifetimes::Traverse(
+ std::function<void(const Lifetime&, Variance)> visitor) const {
+ const_cast<FunctionLifetimes*>(this)->Traverse(
+ [visitor](Lifetime& l, Variance v) { visitor(l, v); });
}
bool FunctionLifetimes::IsIsomorphic(const FunctionLifetimes& other) const {
// We expect this function to only be called for 2 FunctionLifetime objects
// that are for the same function, thus the number of parameters, and the
// number of Lifetimes for each type, should always match.
- assert(param_lifetimes.size() == other.param_lifetimes.size());
- assert(this_lifetimes.size() == other.this_lifetimes.size());
- assert(return_lifetimes.size() == other.return_lifetimes.size());
- for (size_t i = 0; i < param_lifetimes.size(); ++i) {
- assert(param_lifetimes[i].size() == other.param_lifetimes[i].size());
- }
+ assert(param_lifetimes_.size() == other.param_lifetimes_.size());
+ assert(this_lifetimes_.has_value() == other.this_lifetimes_.has_value());
// Map of equivalent lifetimes between `*this` and `other`.
LifetimeBijection bijection;
- auto compare_type_lifetimes = [&bijection](const TypeLifetimes& a,
- const TypeLifetimes& b) {
- assert(a.size() == b.size());
- for (size_t i = 0; i < a.size(); ++i) {
- if (!bijection.Add(a[i], b[i])) {
- return false;
- }
- }
- return true;
- };
+ llvm::SmallVector<Lifetime> my_lifetimes;
+ Traverse(
+ [&my_lifetimes](Lifetime l, Variance) { my_lifetimes.push_back(l); });
+ llvm::SmallVector<Lifetime> other_lifetimes;
+ other.Traverse([&other_lifetimes](Lifetime l, Variance) {
+ other_lifetimes.push_back(l);
+ });
- if (!compare_type_lifetimes(return_lifetimes, other.return_lifetimes)) {
- return false;
- }
- if (!compare_type_lifetimes(this_lifetimes, other.this_lifetimes)) {
- return false;
- }
- for (size_t i = 0; i < param_lifetimes.size(); ++i) {
- if (!compare_type_lifetimes(param_lifetimes[i], other.param_lifetimes[i]))
+ assert(my_lifetimes.size() == other_lifetimes.size());
+ for (size_t i = 0; i < my_lifetimes.size(); ++i) {
+ if (!bijection.Add(my_lifetimes[i], other_lifetimes[i])) {
return false;
+ }
}
return true;
}
@@ -91,58 +158,29 @@
std::string FunctionLifetimes::DebugString(LifetimeFormatter formatter) const {
std::vector<std::string> formatted_param_lifetimes;
- for (const auto& param : param_lifetimes) {
- formatted_param_lifetimes.push_back(
- ::devtools_rust::DebugString(param, formatter));
+ for (const auto& param : param_lifetimes_) {
+ formatted_param_lifetimes.push_back(param.DebugString(formatter));
}
std::string result;
- if (!this_lifetimes.empty()) {
- result = absl::StrCat(
- ::devtools_rust::DebugString(this_lifetimes, formatter), ":");
+ if (this_lifetimes_.has_value()) {
+ result = absl::StrCat(this_lifetimes_->DebugString(formatter), ":");
}
if (!result.empty() && !formatted_param_lifetimes.empty()) {
absl::StrAppend(&result, " ");
}
absl::StrAppend(&result, absl::StrJoin(formatted_param_lifetimes, ", "));
- if (!return_lifetimes.empty()) {
+ if (return_lifetimes_.HasLifetimes()) {
if (!result.empty()) {
absl::StrAppend(&result, " ");
}
- absl::StrAppend(&result, "-> ",
- ::devtools_rust::DebugString(return_lifetimes, formatter));
+ absl::StrAppend(&result, "-> ", return_lifetimes_.DebugString(formatter));
}
return result;
}
-bool FunctionLifetimes::Validate(const clang::FunctionDecl* func) const {
- if (auto method = clang::dyn_cast<clang::CXXMethodDecl>(func)) {
- if (CreateLifetimesForType(method->getThisType(), Lifetime::CreateLocal)
- .size() != this_lifetimes.size()) {
- return false;
- }
- } else if (!this_lifetimes.empty()) {
- return false;
- }
- if (CreateLifetimesForType(func->getReturnType(), Lifetime::CreateLocal)
- .size() != return_lifetimes.size()) {
- return false;
- }
- if (param_lifetimes.size() != func->getNumParams()) {
- return false;
- }
- for (size_t i = 0; i < param_lifetimes.size(); i++) {
- if (CreateLifetimesForType(func->getParamDecl(i)->getType(),
- Lifetime::CreateLocal)
- .size() != param_lifetimes[i].size()) {
- return false;
- }
- }
- return true;
-}
-
std::ostream& operator<<(std::ostream& os,
const FunctionLifetimes& func_lifetimes) {
return os << func_lifetimes.DebugString();
diff --git a/lifetime_annotations/function_lifetimes.h b/lifetime_annotations/function_lifetimes.h
index 0c80c0c..375058a 100644
--- a/lifetime_annotations/function_lifetimes.h
+++ b/lifetime_annotations/function_lifetimes.h
@@ -11,26 +11,76 @@
#include "lifetime_annotations/type_lifetimes.h"
#include "third_party/llvm/llvm-project/clang/include/clang/AST/Decl.h"
+#include "third_party/llvm/llvm-project/clang/include/clang/AST/Type.h"
#include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/SmallVector.h"
+#include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/StringRef.h"
+#include "third_party/llvm/llvm-project/llvm/include/llvm/Support/Error.h"
namespace devtools_rust {
+// Interface used to create lifetimes in FunctionLifetimes::CreateForDecl.
+// CreateReturnLifetime will be called with the ValueLifetimes that were created
+// through calls to CreateParamLifetimes.
+class FunctionLifetimeFactory {
+ public:
+ virtual ~FunctionLifetimeFactory() {}
+ virtual llvm::Expected<Lifetime> CreateParamLifetime(
+ clang::QualType type, llvm::StringRef ref) const = 0;
+ virtual llvm::Expected<Lifetime> CreateReturnLifetime(
+ clang::QualType type, llvm::StringRef ref,
+ const llvm::SmallVector<ValueLifetimes>& param_lifetimes,
+ const std::optional<ValueLifetimes>& this_lifetimes) const = 0;
+};
+
+// Implementation of FunctionLifetimeFactory that just uses a single callback.
+class FunctionLifetimeFactorySingleCallback : public FunctionLifetimeFactory {
+ public:
+ FunctionLifetimeFactorySingleCallback(LifetimeFactory factory)
+ : factory_(std::move(factory)) {}
+ llvm::Expected<Lifetime> CreateParamLifetime(
+ clang::QualType type, llvm::StringRef ref) const override {
+ return factory_(type, ref);
+ }
+ llvm::Expected<Lifetime> CreateReturnLifetime(
+ clang::QualType type, llvm::StringRef ref,
+ const llvm::SmallVector<ValueLifetimes>& /*param_lifetimes*/,
+ const std::optional<ValueLifetimes>& /*this_lifetimes*/) const override {
+ return factory_(type, ref);
+ }
+
+ private:
+ LifetimeFactory factory_;
+};
+
// Lifetimes for the signature of a function.
-struct FunctionLifetimes {
- // Lifetimes for the parameters.
+class FunctionLifetimes {
+ public:
+ // Returns lifetimes for the `i`-th parameter.
// These are the same number and order as FunctionDecl::parameters().
- llvm::SmallVector<TypeLifetimes> param_lifetimes;
+ const ValueLifetimes& GetParamLifetimes(size_t i) const {
+ return param_lifetimes_[i];
+ }
// Lifetimes for the return type.
- TypeLifetimes return_lifetimes;
+ const ValueLifetimes& GetReturnLifetimes() const { return return_lifetimes_; }
// Lifetimes for the `this` parameter for non-static member functions.
- TypeLifetimes this_lifetimes;
+ const ValueLifetimes& GetThisLifetimes() const {
+ assert(this_lifetimes_.has_value());
+ return *this_lifetimes_;
+ }
- // Returns whether this object contains any lifetimes.
- // Note that this returns false if `param_lifetimes` is non-empty but only
- // contains empty TypeLifetimes.
- bool ContainsLifetimes();
+ // 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);
+
+ // Checks if this FunctionLifetimes represents valid lifetimes for the given
+ // Decl.
+ bool IsValidForDecl(const clang::FunctionDecl* function);
// Returns if the two FunctionLifetimes have the same structures, without
// requiring them to have the same exact Lifetimes. They have the same
@@ -38,16 +88,26 @@
// in the same positions.
bool IsIsomorphic(const FunctionLifetimes& other) const;
- // Returns true if this FunctionLifetimes object is valid for the given
- // function.
- bool Validate(const clang::FunctionDecl* func) const;
-
// 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;
+
+ // 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_;
+
+ static llvm::Expected<FunctionLifetimes> Create(
+ const clang::FunctionProtoType* type, const clang::QualType this_type,
+ const FunctionLifetimeFactory& lifetime_factory);
};
std::ostream& operator<<(std::ostream& os,
@@ -55,7 +115,8 @@
// An error that occurred while analyzing a function.
struct FunctionAnalysisError {
- explicit FunctionAnalysisError(llvm::StringRef message) : message(message) {}
+ explicit FunctionAnalysisError(llvm::StringRef message = "")
+ : message(message) {}
explicit FunctionAnalysisError(const llvm::Error& err) {
::llvm::raw_string_ostream stream(message);
@@ -72,7 +133,7 @@
// 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<FunctionLifetimes, FunctionAnalysisError>;
+ std::variant<FunctionAnalysisError, FunctionLifetimes>;
} // namespace devtools_rust
diff --git a/lifetime_annotations/function_lifetimes_test.cc b/lifetime_annotations/function_lifetimes_test.cc
deleted file mode 100644
index da3a7d2..0000000
--- a/lifetime_annotations/function_lifetimes_test.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-// 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
-
-#include "lifetime_annotations/function_lifetimes.h"
-
-#include "testing/base/public/gmock.h"
-#include "testing/base/public/gunit.h"
-
-namespace devtools_rust {
-namespace {
-
-TEST(FunctionLifetimesTest, ContainsLifetimes) {
- EXPECT_FALSE(FunctionLifetimes().ContainsLifetimes());
-
- {
- FunctionLifetimes lifetimes;
- lifetimes.param_lifetimes.push_back({});
- EXPECT_FALSE(lifetimes.ContainsLifetimes());
- }
-
- {
- FunctionLifetimes lifetimes;
- lifetimes.param_lifetimes.push_back({Lifetime::Static()});
- EXPECT_TRUE(lifetimes.ContainsLifetimes());
- }
-
- {
- FunctionLifetimes lifetimes;
- lifetimes.this_lifetimes = {Lifetime::Static()};
- EXPECT_TRUE(lifetimes.ContainsLifetimes());
- }
-
- {
- FunctionLifetimes lifetimes;
- lifetimes.return_lifetimes = {Lifetime::Static()};
- EXPECT_TRUE(lifetimes.ContainsLifetimes());
- }
-}
-
-} // namespace
-} // namespace devtools_rust
diff --git a/lifetime_annotations/lifetime_annotations.cc b/lifetime_annotations/lifetime_annotations.cc
index cf5dd6f..f198ce0 100644
--- a/lifetime_annotations/lifetime_annotations.cc
+++ b/lifetime_annotations/lifetime_annotations.cc
@@ -12,112 +12,80 @@
#include "third_party/absl/strings/str_cat.h"
#include "lifetime_annotations/function_lifetimes.h"
+#include "lifetime_annotations/lifetime_symbol_table.h"
#include "lifetime_annotations/pointee_type.h"
#include "lifetime_annotations/type_lifetimes.h"
#include "third_party/llvm/llvm-project/clang/include/clang/AST/APValue.h"
#include "third_party/llvm/llvm-project/clang/include/clang/AST/ASTContext.h"
#include "third_party/llvm/llvm-project/clang/include/clang/AST/Attr.h"
#include "third_party/llvm/llvm-project/clang/include/clang/AST/Attrs.inc"
+#include "third_party/llvm/llvm-project/clang/include/clang/AST/Decl.h"
#include "third_party/llvm/llvm-project/clang/include/clang/AST/DeclCXX.h"
#include "third_party/llvm/llvm-project/clang/include/clang/AST/PrettyPrinter.h"
+#include "third_party/llvm/llvm-project/clang/include/clang/AST/Type.h"
#include "third_party/llvm/llvm-project/clang/include/clang/Basic/LangOptions.h"
+#include "third_party/llvm/llvm-project/clang/include/clang/Basic/SourceLocation.h"
#include "third_party/llvm/llvm-project/clang/include/clang/Lex/Pragma.h"
#include "third_party/llvm/llvm-project/clang/include/clang/Lex/Preprocessor.h"
+#include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/DenseSet.h"
#include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/SmallVector.h"
#include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/StringRef.h"
+#include "third_party/llvm/llvm-project/llvm/include/llvm/Support/Error.h"
namespace devtools_rust {
-
namespace {
-llvm::Expected<llvm::SmallVector<Lifetime>> GetAnnotatedOrElidedLifetimes(
- llvm::ArrayRef<const clang::Attr*> /*attrs*/, size_t num_expected,
- LifetimeSymbolTable& /*symbol_table*/,
- const std::function<llvm::Expected<Lifetime>()>& elided_lifetime_factory,
- const clang::ASTContext& /*ast_context*/) {
- assert(num_expected > 0);
+llvm::Expected<FunctionLifetimes> ParseLifetimeAnnotations(
+ const clang::FunctionDecl* func, LifetimeSymbolTable& symbol_table,
+ const std::string& lifetimes_str) {
+ clang::LangOptions lang_opts;
+ clang::Lexer lexer(clang::SourceLocation(), lang_opts, lifetimes_str.data(),
+ lifetimes_str.data(),
+ lifetimes_str.data() + lifetimes_str.size());
- llvm::SmallVector<Lifetime> lifetimes;
- lifetimes.reserve(num_expected);
+ const char* end = lifetimes_str.data() + lifetimes_str.size();
- // TODO(mboehme): Extract lifetime annotations from `attrs` if present.
-
- // No lifetime annotations: Use elided lifetimes.
- for (size_t i = 0; i < num_expected; ++i) {
- llvm::Expected<Lifetime> maybe_lifetime = elided_lifetime_factory();
- if (maybe_lifetime) {
- lifetimes.push_back(maybe_lifetime.get());
- } else {
- return maybe_lifetime.takeError();
+ auto tok = [&lexer, &lifetimes_str, end]() -> llvm::StringRef {
+ clang::Token token;
+ if (lexer.getBufferLocation() != end) {
+ lexer.LexFromRawLexer(token);
+ return llvm::StringRef(
+ lifetimes_str.data() + token.getLocation().getRawEncoding(),
+ token.getLength());
}
- }
- return lifetimes;
-}
+ return "";
+ };
-llvm::Error AddLifetimeAnnotationsForType(
- clang::QualType type, LifetimeSymbolTable& symbol_table,
- const std::function<llvm::Expected<Lifetime>()>& elided_lifetime_factory,
- const clang::ASTContext& ast_context, TypeLifetimes& lifetimes) {
- assert(!type.isNull());
- assert(elided_lifetime_factory);
-
- llvm::SmallVector<const clang::Attr*> attrs;
-
- size_t num_lifetime_params = GetLifetimeParameters(type).size();
- if (num_lifetime_params > 0) {
- llvm::Expected<llvm::SmallVector<Lifetime>> maybe_lifetime_args =
- GetAnnotatedOrElidedLifetimes(attrs, num_lifetime_params, symbol_table,
- elided_lifetime_factory, ast_context);
- if (maybe_lifetime_args) {
- lifetimes.append(maybe_lifetime_args.get());
- } else {
- return maybe_lifetime_args.takeError();
+ // TODO(veluca): this is too permissive.
+ auto next_lifetime = [&]() {
+ llvm::StringRef next = tok();
+ while (next == "(" || next == ")" || next == "," || next == "->" ||
+ next == ":") {
+ next = tok();
}
+ return next;
+ };
+
+ FunctionLifetimeFactorySingleCallback factory(
+ [&symbol_table, &next_lifetime](
+ clang::QualType, llvm::StringRef) -> llvm::Expected<Lifetime> {
+ llvm::StringRef next = next_lifetime();
+ if (next.empty()) {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Invalid lifetime annotation: too few lifetimes");
+ }
+ return symbol_table.LookupNameAndMaybeDeclare(next);
+ });
+
+ auto ret = FunctionLifetimes::CreateForDecl(func, factory);
+
+ if (!next_lifetime().empty()) {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ "Invalid lifetime annotation: too many lifetimes");
}
-
- const llvm::ArrayRef<clang::TemplateArgument> template_args =
- GetTemplateArgs(type);
- if (!template_args.empty()) {
- for (const clang::TemplateArgument& template_arg : template_args) {
- if (llvm::Error err = AddLifetimeAnnotationsForType(
- template_arg.getAsType(), symbol_table, elided_lifetime_factory,
- ast_context, lifetimes)) {
- return err;
- }
- }
- return llvm::Error::success();
- }
-
- if (clang::QualType pointee_type = PointeeType(type);
- !pointee_type.isNull()) {
- if (llvm::Error err = AddLifetimeAnnotationsForType(
- pointee_type, symbol_table, elided_lifetime_factory, ast_context,
- lifetimes)) {
- return err;
- }
-
- llvm::Expected<llvm::SmallVector<Lifetime>> maybe_pointee_lifetime =
- GetAnnotatedOrElidedLifetimes(attrs, 1, symbol_table,
- elided_lifetime_factory, ast_context);
- if (maybe_pointee_lifetime) {
- lifetimes.append(maybe_pointee_lifetime.get());
- } else {
- return maybe_pointee_lifetime.takeError();
- }
- }
-
- return llvm::Error::success();
-}
-
-llvm::Expected<llvm::SmallVector<Lifetime>> GetThisLifetimes(
- const clang::CXXMethodDecl* method, LifetimeSymbolTable& symbol_table,
- const std::function<llvm::Expected<Lifetime>()>& elided_lifetime_factory) {
- llvm::ArrayRef<const clang::Attr*> attrs;
- if (method->hasAttrs()) {
- attrs = method->getAttrs();
- }
- return GetAnnotatedOrElidedLifetimes(
- attrs, 1, symbol_table, elided_lifetime_factory, method->getASTContext());
+ return ret;
}
// Parse a "(a, b): (a, b), (), a -> b"-style annotation into a
@@ -147,88 +115,12 @@
.moveInto(lifetimes)) {
return error(toString(std::move(err)));
}
- std::string lifetimes_str = lifetimes.str();
- clang::LangOptions lang_opts;
- clang::Lexer lexer(attr->getLoc(), lang_opts, lifetimes_str.data(),
- lifetimes_str.data(),
- lifetimes_str.data() + lifetimes_str.size());
-
- const char* end = lifetimes_str.data() + lifetimes_str.size();
-
- auto tok = [&lexer, &lifetimes_str, end, attr]() -> llvm::StringRef {
- clang::Token token;
- if (lexer.getBufferLocation() != end) {
- lexer.LexFromRawLexer(token);
- return llvm::StringRef(lifetimes_str.data() +
- token.getLocation().getRawEncoding() -
- attr->getLoc().getRawEncoding(),
- token.getLength());
- }
- return "";
- };
-
- llvm::SmallVector<TypeLifetimes> fn_lifetimes;
- bool has_this_lifetimes = false;
- bool has_return_lifetimes = false;
-
- for (llvm::StringRef token; !(token = tok()).empty();) {
- if (token == ",") {
- continue;
- }
- if (has_return_lifetimes) {
- return error();
- }
- if (token == ":") {
- if (has_this_lifetimes || fn_lifetimes.size() != 1) {
- return error();
- }
- has_this_lifetimes = true;
- continue;
- }
- // Skip the -> and parse return lifetimes. No more lifetimes should be
- // parsed afterwards.
- if (token == "->") {
- has_return_lifetimes = true;
- token = tok();
- }
- fn_lifetimes.emplace_back();
- if (token == "(") {
- for (; (token = tok()) != ")" && !token.empty();) {
- if (token == ",") continue;
- fn_lifetimes.back().push_back(
- symbol_table.LookupNameAndMaybeDeclare(token));
- }
- } else {
- fn_lifetimes.back().push_back(
- symbol_table.LookupNameAndMaybeDeclare(token));
- }
- }
-
- FunctionLifetimes function_lifetimes;
- size_t param_start = 0;
- if (has_this_lifetimes) {
- function_lifetimes.this_lifetimes = fn_lifetimes[0];
- param_start = 1;
- }
- size_t param_end = fn_lifetimes.size();
- if (has_return_lifetimes) {
- function_lifetimes.return_lifetimes = fn_lifetimes.back();
- param_end -= 1;
- }
- function_lifetimes.param_lifetimes.assign(fn_lifetimes.begin() + param_start,
- fn_lifetimes.begin() + param_end);
-
- if (function_lifetimes.Validate(func)) {
- return function_lifetimes;
- }
- return error();
+ return ParseLifetimeAnnotations(func, symbol_table, lifetimes.str());
}
llvm::Expected<FunctionLifetimes> GetLifetimeAnnotationsInternal(
const clang::FunctionDecl* func, LifetimeSymbolTable& symbol_table,
bool elision_enabled) {
- FunctionLifetimes result;
-
const clang::AnnotateAttr* lifetime_annotation = nullptr;
for (const clang::Attr* attr : func->attrs()) {
if (auto annotate = clang::dyn_cast<clang::AnnotateAttr>(attr)) {
@@ -248,89 +140,77 @@
return ParseLifetimeAnnotations(func, symbol_table, lifetime_annotation);
}
- std::function<llvm::Expected<Lifetime>()> elided_lifetime_factory;
- if (elision_enabled) {
- elided_lifetime_factory = [&symbol_table]() -> llvm::Expected<Lifetime> {
+ class Factory : public FunctionLifetimeFactory {
+ public:
+ Factory(bool elision_enabled, const clang::FunctionDecl* func,
+ LifetimeSymbolTable& symbol_table)
+ : elision_enabled(elision_enabled),
+ func(func),
+ symbol_table(symbol_table) {}
+
+ private:
+ llvm::Expected<Lifetime> CreateParamLifetime(
+ clang::QualType, llvm::StringRef) const override {
+ // TODO(mboehme): parse lifetime annotations from `type` if present.
+ if (!elision_enabled) {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ absl::StrCat("Lifetime elision not enabled for '",
+ func->getNameAsString(), "'"));
+ }
Lifetime lifetime = Lifetime::CreateVariable();
symbol_table.LookupLifetimeAndMaybeDeclare(lifetime);
return lifetime;
- };
- } else {
- elided_lifetime_factory = [func]() -> llvm::Expected<Lifetime> {
- return llvm::createStringError(
- llvm::inconvertibleErrorCode(),
- absl::StrCat("Lifetime elision not enabled for '",
- func->getNameAsString(), "'"));
- };
- }
-
- if (const auto* method = clang::dyn_cast<clang::CXXMethodDecl>(func)) {
- if (llvm::Error err =
- GetThisLifetimes(method, symbol_table, elided_lifetime_factory)
- .moveInto(result.this_lifetimes)) {
- return std::move(err);
- }
- }
-
- llvm::SmallVector<Lifetime> all_input_lifetimes;
- result.param_lifetimes.resize(func->getNumParams());
- for (unsigned i = 0; i < func->getNumParams(); i++) {
- const clang::ParmVarDecl* param = func->getParamDecl(i);
- if (llvm::Error err = AddLifetimeAnnotationsForType(
- param->getType(), symbol_table, elided_lifetime_factory,
- func->getASTContext(), result.param_lifetimes[i])) {
- return std::move(err);
}
- all_input_lifetimes.append(result.param_lifetimes[i]);
- }
+ llvm::Expected<Lifetime> CreateReturnLifetime(
+ clang::QualType, llvm::StringRef,
+ const llvm::SmallVector<ValueLifetimes>& param_lifetimes,
+ const std::optional<ValueLifetimes>& this_lifetimes) const override {
+ // TODO(mboehme): parse lifetime annotations from `type` if present.
+ if (!elision_enabled) {
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ absl::StrCat("Lifetime elision not enabled for '",
+ func->getNameAsString(), "'"));
+ }
- std::function<llvm::Expected<Lifetime>()> elided_return_lifetime_factory;
- if (!elision_enabled) {
- elided_return_lifetime_factory = [func]() -> llvm::Expected<Lifetime> {
- return llvm::createStringError(
- llvm::inconvertibleErrorCode(),
- absl::StrCat("Lifetime elision not enabled for '",
- func->getNameAsString(), "'"));
- };
- } else if (!result.this_lifetimes.empty()) {
- // If we have an implicit `this` parameter, its lifetime is assigned to all
- // output lifetimes.
- elided_return_lifetime_factory =
- [this_lifetime =
- result.this_lifetimes.back()]() -> llvm::Expected<Lifetime> {
- return this_lifetime;
- };
- } else if (all_input_lifetimes.size() == 1) {
- // If we have a single input lifetime, its lifetime is assigned to all
- // output lifetimes.
- // Otherwise, elided_return_lifetime_factory remains empty, and we get an
- // error if there are any elided lifetimes in the return type.
- elided_return_lifetime_factory =
- [return_lifetime =
- all_input_lifetimes[0]]() -> llvm::Expected<Lifetime> {
- return return_lifetime;
- };
- } else {
- elided_return_lifetime_factory = [func]() -> llvm::Expected<Lifetime> {
- return llvm::createStringError(
- llvm::inconvertibleErrorCode(),
- absl::StrCat(
- "Cannot elide output lifetimes for '", func->getNameAsString(),
- "' because it is a non-member function that does not have "
- "exactly one input lifetime"));
- };
- }
+ // If we have an implicit `this` parameter, its lifetime is assigned to
+ // all lifetimes in the return type.
+ if (this_lifetimes.has_value()) {
+ return this_lifetimes->GetPointeeLifetimes().GetLifetime();
+ }
- if (llvm::Error err = AddLifetimeAnnotationsForType(
- func->getReturnType(), symbol_table, elided_return_lifetime_factory,
- func->getASTContext(), result.return_lifetimes)) {
- return std::move(err);
- }
+ llvm::DenseSet<Lifetime> all_input_lifetimes;
+ for (const ValueLifetimes& v : param_lifetimes) {
+ v.Traverse([&all_input_lifetimes](Lifetime l, Variance) {
+ all_input_lifetimes.insert(l);
+ });
+ }
- return result;
+ if (all_input_lifetimes.size() == 1) {
+ // If we have a single input lifetime, its lifetime is assigned to all
+ // output lifetimes.
+ return *all_input_lifetimes.begin();
+ } else {
+ // Otherwise, we don't know how to elide the output lifetime.
+ return llvm::createStringError(
+ llvm::inconvertibleErrorCode(),
+ absl::StrCat(
+ "Cannot elide output lifetimes for '", func->getNameAsString(),
+ "' because it is a non-member function that does not have "
+ "exactly one input lifetime"));
+ }
+ }
+
+ bool elision_enabled;
+ const clang::FunctionDecl* func;
+ LifetimeSymbolTable& symbol_table;
+ };
+
+ Factory factory(elision_enabled, func, symbol_table);
+ return FunctionLifetimes::CreateForDecl(func, factory);
}
-
} // namespace
llvm::Expected<FunctionLifetimes> GetLifetimeAnnotations(
@@ -355,6 +235,16 @@
return GetLifetimeAnnotationsInternal(func, *symbol_table, elision_enabled);
}
+llvm::Expected<FunctionLifetimes> ParseLifetimeAnnotations(
+ const clang::FunctionDecl* func, const std::string& lifetimes_str,
+ LifetimeSymbolTable* symbol_table) {
+ LifetimeSymbolTable throw_away_symbol_table;
+ if (!symbol_table) {
+ symbol_table = &throw_away_symbol_table;
+ }
+ return ParseLifetimeAnnotations(func, *symbol_table, lifetimes_str);
+}
+
namespace {
class LifetimeElisionPragmaHandler : public clang::PragmaHandler {
diff --git a/lifetime_annotations/lifetime_annotations.h b/lifetime_annotations/lifetime_annotations.h
index 1c3f218..e87c88e 100644
--- a/lifetime_annotations/lifetime_annotations.h
+++ b/lifetime_annotations/lifetime_annotations.h
@@ -36,6 +36,13 @@
const clang::FunctionDecl* func, const LifetimeAnnotationContext& context,
LifetimeSymbolTable* symbol_table = nullptr);
+// Parses "a: b, a -> b"-style lifetime annotations from `lifetimes_str` for the
+// function declaration `func`. Lifetimes are inserted into the given
+// `symbol_table`, or used from there if already known.
+llvm::Expected<FunctionLifetimes> ParseLifetimeAnnotations(
+ const clang::FunctionDecl* func, const std::string& lifetimes_str,
+ LifetimeSymbolTable* symbol_table = nullptr);
+
// Adds handlers to `preprocessor` to populate `context`.
// To be able to use GetLifetimeAnnotations(), call this function to add the
// necessary handlers before compiling any code.
diff --git a/lifetime_annotations/lifetime_annotations_test.cc b/lifetime_annotations/lifetime_annotations_test.cc
index c3ea44e..b1ada7b 100644
--- a/lifetime_annotations/lifetime_annotations_test.cc
+++ b/lifetime_annotations/lifetime_annotations_test.cc
@@ -381,6 +381,16 @@
IsOkAndHolds(LifetimesAre({{"S::f", "a: b -> a"}})));
}
+TEST_F(LifetimeAnnotationsTest, LifetimeAnnotation_MethodWithLifetimeParams) {
+ EXPECT_THAT(GetNamedLifetimeAnnotations(R"(
+ struct [[clang::annotate("lifetime_params", "x", "y")]] S {
+ [[clang::annotate("lifetimes", "x, y, a: -> x")]]
+ int* f();
+ };
+ )"),
+ IsOkAndHolds(LifetimesAre({{"S::f", "(x, y, a): -> x"}})));
+}
+
TEST_F(LifetimeAnnotationsTest, LifetimeAnnotation_Invalid_MissingThis) {
EXPECT_THAT(GetNamedLifetimeAnnotations(R"(
struct S {
diff --git a/lifetime_annotations/lifetime_symbol_table.cc b/lifetime_annotations/lifetime_symbol_table.cc
index 4b9ec89..e17f7cd 100644
--- a/lifetime_annotations/lifetime_symbol_table.cc
+++ b/lifetime_annotations/lifetime_symbol_table.cc
@@ -102,4 +102,14 @@
lifetime_to_name_[lifetime] = name;
}
+void LifetimeSymbolTable::Rebind(llvm::StringRef name, Lifetime lifetime) {
+ auto iter = name_to_lifetime_.find(name);
+ if (iter == name_to_lifetime_.end()) {
+ llvm::report_fatal_error("invalid call to rebind");
+ }
+ lifetime_to_name_.erase(iter->second);
+ lifetime_to_name_[lifetime] = name;
+ iter->second = lifetime;
+}
+
} // namespace devtools_rust
diff --git a/lifetime_annotations/lifetime_symbol_table.h b/lifetime_annotations/lifetime_symbol_table.h
index 952aebe..60d3ea5 100644
--- a/lifetime_annotations/lifetime_symbol_table.h
+++ b/lifetime_annotations/lifetime_symbol_table.h
@@ -49,6 +49,10 @@
// to LookupLifetime() to have undefined behaviour.
void Add(llvm::StringRef name, Lifetime lifetime);
+ // Re-binds a name to a different lifetime.
+ // The name *must* be already used.
+ void Rebind(llvm::StringRef name, Lifetime lifetime);
+
// Accessor for hashing/debugging purposes.
const llvm::StringMap<Lifetime>& GetMapping() const {
return name_to_lifetime_;
diff --git a/lifetime_annotations/lifetime_symbol_table_test.cc b/lifetime_annotations/lifetime_symbol_table_test.cc
index 9e2f6b9..a3f3bd5 100644
--- a/lifetime_annotations/lifetime_symbol_table_test.cc
+++ b/lifetime_annotations/lifetime_symbol_table_test.cc
@@ -40,6 +40,20 @@
EXPECT_EQ(table.LookupLifetime(b), "b");
}
+TEST(LifetimeSymbolTableTest, RebindLifetime) {
+ LifetimeSymbolTable table;
+
+ Lifetime a = table.LookupNameAndMaybeDeclare("a");
+ Lifetime b = Lifetime::CreateVariable();
+
+ EXPECT_EQ(table.LookupLifetime(a), "a");
+
+ table.Rebind("a", b);
+ EXPECT_EQ(table.LookupName("a"), b);
+ EXPECT_EQ(table.LookupLifetime(a), std::nullopt);
+ EXPECT_EQ(table.LookupLifetime(b), "a");
+}
+
TEST(LifetimeSymbolTableTest, LookupLifetimeAndMaybeDeclare) {
{
LifetimeSymbolTable table;
diff --git a/lifetime_annotations/type_lifetimes.cc b/lifetime_annotations/type_lifetimes.cc
index 66cca13..54c38b7 100644
--- a/lifetime_annotations/type_lifetimes.cc
+++ b/lifetime_annotations/type_lifetimes.cc
@@ -22,31 +22,18 @@
#include "third_party/llvm/llvm-project/clang/include/clang/AST/DeclTemplate.h"
#include "third_party/llvm/llvm-project/clang/include/clang/AST/TemplateBase.h"
#include "third_party/llvm/llvm-project/clang/include/clang/AST/Type.h"
+#include "third_party/llvm/llvm-project/clang/include/clang/Basic/LLVM.h"
#include "third_party/llvm/llvm-project/clang/include/clang/Basic/SourceLocation.h"
#include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/ArrayRef.h"
#include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/DenseMapInfo.h"
#include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/Hashing.h"
#include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/SmallVector.h"
#include "third_party/llvm/llvm-project/llvm/include/llvm/ADT/StringRef.h"
+#include "third_party/llvm/llvm-project/llvm/include/llvm/Support/Error.h"
#include "third_party/llvm/llvm-project/llvm/include/llvm/Support/ErrorHandling.h"
namespace devtools_rust {
-std::string DebugString(const TypeLifetimes& lifetimes,
- const LifetimeFormatter& formatter) {
- std::vector<std::string> parts;
- parts.reserve(lifetimes.size());
-
- for (Lifetime l : lifetimes) {
- parts.push_back(formatter(l));
- }
-
- if (parts.size() == 1) {
- return parts[0];
- }
- return absl::StrFormat("(%s)", absl::StrJoin(parts, ", "));
-}
-
llvm::SmallVector<std::string> GetLifetimeParameters(clang::QualType type) {
// TODO(mboehme):
// - Add support for type aliases with lifetime parameters
@@ -90,40 +77,6 @@
return ret;
}
-TypeLifetimes CreateLifetimesForType(
- clang::QualType type, std::function<Lifetime()> lifetime_factory) {
- assert(!type.isNull());
- TypeLifetimes ret;
-
- for (const auto& lftm_param : GetLifetimeParameters(type)) {
- (void)lftm_param;
- ret.push_back(lifetime_factory());
- }
-
- // Add implicit lifetime parameters for type template parameters.
- llvm::ArrayRef<clang::TemplateArgument> template_args = GetTemplateArgs(type);
- if (!template_args.empty()) {
- for (const clang::TemplateArgument& arg : template_args) {
- if (arg.getKind() == clang::TemplateArgument::Type) {
- ret.append(CreateLifetimesForType(arg.getAsType(), lifetime_factory));
- } else if (arg.getKind() == clang::TemplateArgument::Pack) {
- for (const clang::TemplateArgument& inner_arg : arg.getPackAsArray()) {
- if (inner_arg.getKind() == clang::TemplateArgument::Type) {
- ret.append(CreateLifetimesForType(inner_arg.getAsType(),
- lifetime_factory));
- }
- }
- }
- }
- return ret;
- }
- clang::QualType pointee = PointeeType(type);
- if (pointee.isNull()) return ret;
- ret = CreateLifetimesForType(pointee, lifetime_factory);
- ret.push_back(lifetime_factory());
- return ret;
-}
-
ValueLifetimes& ValueLifetimes::operator=(const ValueLifetimes& other) {
type_ = other.type_;
template_argument_lifetimes_ = other.template_argument_lifetimes_;
@@ -135,85 +88,74 @@
return *this;
}
-std::string ValueLifetimes::DebugString() const {
- std::string ret;
- if (!lifetime_parameters_by_name_.GetMapping().empty()) {
- std::vector<std::string> lftm_args_strings;
- for (const auto& lftm_arg : lifetime_parameters_by_name_.GetMapping()) {
- lftm_args_strings.push_back(lftm_arg.second.DebugString());
- }
- absl::StrAppend(&ret, "(", absl::StrJoin(lftm_args_strings, ", "), ")");
- }
- if (!template_argument_lifetimes_.empty()) {
- std::vector<std::string> tmpl_arg_strings;
- for (const std::optional<ValueLifetimes>& tmpl_arg :
- template_argument_lifetimes_) {
- if (tmpl_arg) {
- tmpl_arg_strings.push_back(tmpl_arg->DebugString());
- } else {
- tmpl_arg_strings.push_back("");
- }
- }
- absl::StrAppend(&ret, "<", absl::StrJoin(tmpl_arg_strings, ", "), ">");
- }
- if (pointee_lifetimes_) {
- absl::StrAppend(&ret, " -> ", pointee_lifetimes_->DebugString());
- }
- return ret;
-}
-
-void ValueLifetimes::ReverseVisitTemplateArgs(
- llvm::ArrayRef<clang::TemplateArgument> template_args,
- TypeLifetimesRef& type_lifetimes, ValueLifetimes& out) {
- for (size_t i = template_args.size(); i-- > 0;) {
- const clang::TemplateArgument& arg = template_args[i];
- if (arg.getKind() == clang::TemplateArgument::Type) {
- out.template_argument_lifetimes_.push_back(
- FromTypeLifetimes(type_lifetimes, arg.getAsType()));
- } else if (arg.getKind() == clang::TemplateArgument::Pack) {
- ReverseVisitTemplateArgs(arg.getPackAsArray(), type_lifetimes, out);
- } else {
- out.template_argument_lifetimes_.push_back(std::nullopt);
- }
- }
-}
-
-// Here, `type_lifetimes` are the lifetimes of a prvalue of the given `type`,
-// unlike ObjectLifetimes::FromTypeLifetimes, which assumes a glvalue.
-ValueLifetimes ValueLifetimes::FromTypeLifetimes(
- TypeLifetimesRef& type_lifetimes, clang::QualType type) {
+llvm::Expected<ValueLifetimes> ValueLifetimes::Create(
+ clang::QualType type, LifetimeFactory lifetime_factory) {
assert(!type.isNull());
-
ValueLifetimes ret(type);
- llvm::SmallVector<std::string> params = GetLifetimeParameters(type);
- // Visit in reverse order, as we are doing a post-order traversal.
- for (size_t i = params.size(); i-- > 0;) {
- if (ret.lifetime_parameters_by_name_.LookupName(params[i])) {
- llvm::report_fatal_error("duplicate lifetime parameter name");
+ for (const auto& lftm_param : GetLifetimeParameters(type)) {
+ Lifetime l;
+ if (llvm::Error err = lifetime_factory(type, lftm_param).moveInto(l)) {
+ return std::move(err);
}
- ret.lifetime_parameters_by_name_.Add(params[i], type_lifetimes.back());
- type_lifetimes = type_lifetimes.drop_back();
+ ret.lifetime_parameters_by_name_.Add(lftm_param, l);
}
+ // Add implicit lifetime parameters for type template parameters.
llvm::ArrayRef<clang::TemplateArgument> template_args = GetTemplateArgs(type);
if (!template_args.empty()) {
- // Since we are simulating reversing a post-order visit, we need to
- // extract template arguments in reverse order.
- ReverseVisitTemplateArgs(template_args, type_lifetimes, ret);
- std::reverse(ret.template_argument_lifetimes_.begin(),
- ret.template_argument_lifetimes_.end());
+ for (const clang::TemplateArgument& arg : template_args) {
+ if (arg.getKind() == clang::TemplateArgument::Type) {
+ ValueLifetimes template_arg_lifetime;
+ if (llvm::Error err =
+ ValueLifetimes::Create(arg.getAsType(), lifetime_factory)
+ .moveInto(template_arg_lifetime)) {
+ return std::move(err);
+ }
+ ret.template_argument_lifetimes_.push_back(template_arg_lifetime);
+ } else if (arg.getKind() == clang::TemplateArgument::Pack) {
+ for (const clang::TemplateArgument& inner_arg : arg.getPackAsArray()) {
+ if (inner_arg.getKind() == clang::TemplateArgument::Type) {
+ ValueLifetimes template_arg_lifetime;
+ if (llvm::Error err = ValueLifetimes::Create(inner_arg.getAsType(),
+ lifetime_factory)
+ .moveInto(template_arg_lifetime)) {
+ return std::move(err);
+ }
+ ret.template_argument_lifetimes_.push_back(template_arg_lifetime);
+ }
+ }
+ }
+ }
return ret;
}
- clang::QualType pointee_type = PointeeType(type);
- if (!pointee_type.isNull()) {
- ret.pointee_lifetimes_ = std::make_unique<ObjectLifetimes>(
- ObjectLifetimes::FromTypeLifetimes(type_lifetimes, pointee_type));
+ clang::QualType pointee = PointeeType(type);
+ if (pointee.isNull()) return ret;
+ ObjectLifetimes obj_lftm;
+ if (llvm::Error err = ObjectLifetimes::Create(pointee, lifetime_factory)
+ .moveInto(obj_lftm)) {
+ return std::move(err);
}
+ ret.pointee_lifetimes_ =
+ std::make_unique<ObjectLifetimes>(std::move(obj_lftm));
return ret;
}
+llvm::Expected<ObjectLifetimes> ObjectLifetimes::Create(
+ clang::QualType type, LifetimeFactory lifetime_factory) {
+ ValueLifetimes v;
+ if (llvm::Error err =
+ ValueLifetimes::Create(type, lifetime_factory).moveInto(v)) {
+ return std::move(err);
+ }
+ Lifetime l;
+ if (llvm::Error err = lifetime_factory(type, "").moveInto(l)) {
+ return std::move(err);
+ }
+ return ObjectLifetimes(l, v);
+}
+
ValueLifetimes ValueLifetimes::ForLifetimeLessType(clang::QualType type) {
assert(PointeeType(type).isNull() && !type->isRecordType());
return ValueLifetimes(type);
@@ -240,23 +182,56 @@
return result;
}
+// TODO(veluca): improve the formatting here.
+std::string ValueLifetimes::DebugString(
+ const LifetimeFormatter& formatter) const {
+ std::vector<std::string> lifetimes;
+ Traverse([&](Lifetime l, Variance) { lifetimes.push_back(formatter(l)); });
+ if (lifetimes.size() == 1) {
+ return lifetimes[0];
+ } else {
+ return absl::StrCat("(", absl::StrJoin(lifetimes, ", "), ")");
+ }
+}
+
const ObjectLifetimes& ValueLifetimes::GetPointeeLifetimes() const {
assert(!PointeeType(type_).isNull());
return *pointee_lifetimes_;
}
-ObjectLifetimes ObjectLifetimes::FromTypeLifetimes(
- TypeLifetimesRef& type_lifetimes, clang::QualType type) {
- assert(!type_lifetimes.empty());
- assert(!type.isNull());
- Lifetime self_lifetime = type_lifetimes.back();
- type_lifetimes = type_lifetimes.drop_back();
- return ObjectLifetimes(
- self_lifetime, ValueLifetimes::FromTypeLifetimes(type_lifetimes, type));
+void ValueLifetimes::Traverse(std::function<void(Lifetime&, Variance)> visitor,
+ Variance variance) {
+ for (std::optional<ValueLifetimes>& tmpl_arg : template_argument_lifetimes_) {
+ if (tmpl_arg) {
+ tmpl_arg->Traverse(visitor, kInvariant);
+ }
+ }
+ if (pointee_lifetimes_) {
+ pointee_lifetimes_->Traverse(visitor, variance, Type());
+ }
+ for (const auto& lftm_arg : GetLifetimeParameters(type_)) {
+ std::optional<Lifetime> lifetime =
+ lifetime_parameters_by_name_.LookupName(lftm_arg);
+ assert(lifetime.has_value());
+ Lifetime new_lifetime = *lifetime;
+ visitor(new_lifetime, variance);
+
+ // Note that this check is not an optimization, but a guard against UB:
+ // if one calls the const version of Traverse, with a callback that does not
+ // mutate the lifetime, on a const instance of the Value/ObjectLifetimes,
+ // then calling Rebind would be UB, even if Rebind would do nothing in
+ // practice.
+ if (new_lifetime != lifetime) {
+ lifetime_parameters_by_name_.Rebind(lftm_arg, new_lifetime);
+ }
+ }
}
-std::string ObjectLifetimes::DebugString() const {
- return absl::StrCat(lifetime_.DebugString(), value_lifetimes_.DebugString());
+void ValueLifetimes::Traverse(
+ std::function<void(const Lifetime&, Variance)> visitor,
+ Variance variance) const {
+ const_cast<ValueLifetimes*>(this)->Traverse(
+ [&visitor](Lifetime& l, Variance v) { visitor(l, v); }, variance);
}
const llvm::ArrayRef<clang::TemplateArgument> GetTemplateArgs(
@@ -343,12 +318,11 @@
// Create a new ValueLifetimes of the type of the template parameter,
// with lifetime `lifetime_`.
// TODO(veluca): we need to propagate lifetime parameters here.
- TypeLifetimes type_lifetimes = CreateLifetimesForType(
- arg.getAsType(), [this]() { return this->lifetime_; });
- TypeLifetimesRef type_lifetimes_ref(type_lifetimes);
template_argument_lifetimes.push_back(
- ValueLifetimes::FromTypeLifetimes(type_lifetimes_ref,
- arg.getAsType()));
+ ValueLifetimes::Create(arg.getAsType(), [this](clang::QualType,
+ llvm::StringRef) {
+ return this->lifetime_;
+ }).get());
}
} else {
template_argument_lifetimes.push_back(std::nullopt);
@@ -378,6 +352,17 @@
ValueLifetimes::ForLifetimeLessType(type));
}
+std::string ObjectLifetimes::DebugString(
+ const LifetimeFormatter& formatter) const {
+ std::vector<std::string> lifetimes;
+ Traverse([&](Lifetime l, Variance) { lifetimes.push_back(formatter(l)); });
+ if (lifetimes.size() == 1) {
+ return lifetimes[0];
+ } else {
+ return absl::StrCat("(", absl::StrJoin(lifetimes, ", "), ")");
+ }
+}
+
ObjectLifetimes ObjectLifetimes::GetFieldOrBaseLifetimes(
clang::QualType type,
llvm::SmallVector<std::string> type_lifetime_args) const {
@@ -385,6 +370,26 @@
"");
}
+void ObjectLifetimes::Traverse(std::function<void(Lifetime&, Variance)> visitor,
+ Variance variance,
+ clang::QualType indirection_type) {
+ assert(indirection_type.isNull() ||
+ indirection_type->getPointeeType() == Type());
+ value_lifetimes_.Traverse(
+ visitor, indirection_type.isNull() || indirection_type.isConstQualified()
+ ? kCovariant
+ : kInvariant);
+ visitor(lifetime_, variance);
+}
+
+void ObjectLifetimes::Traverse(
+ std::function<void(const Lifetime&, Variance)> visitor, Variance variance,
+ clang::QualType indirection_type) const {
+ const_cast<ObjectLifetimes*>(this)->Traverse(
+ [&visitor](Lifetime& l, Variance v) { visitor(l, v); }, variance,
+ indirection_type);
+}
+
llvm::Expected<llvm::StringRef> EvaluateAsStringLiteral(
const clang::Expr* expr, const clang::ASTContext& ast_context) {
auto error = []() {
diff --git a/lifetime_annotations/type_lifetimes.h b/lifetime_annotations/type_lifetimes.h
index 69d431d..95a1a8b 100644
--- a/lifetime_annotations/type_lifetimes.h
+++ b/lifetime_annotations/type_lifetimes.h
@@ -23,15 +23,6 @@
namespace devtools_rust {
-// Lifetimes for a `QualType`.
-//
-// These are ordered according to a post-order traversal of the type.
-//
-// TODO(mboehme): Replace occurrences of this type with ObjectLifetimes
-// or otherwise clarify the relationship between the two types.
-using TypeLifetimes = llvm::SmallVector<Lifetime>;
-using TypeLifetimesRef = llvm::ArrayRef<Lifetime>;
-
// Returns a lifetime in some human-readable format.
using LifetimeFormatter = std::function<std::string(Lifetime)>;
@@ -41,30 +32,30 @@
kInvariant,
};
-// Returns a human-readable representation of `lifetimes`.
-std::string DebugString(
- const TypeLifetimes& lifetimes,
- const LifetimeFormatter& formatter = [](Lifetime l) {
- return l.DebugString();
- });
-
// Extracts the lifetime parameters of the given type.
llvm::SmallVector<std::string> GetLifetimeParameters(clang::QualType type);
-TypeLifetimes CreateLifetimesForType(
- clang::QualType type, std::function<Lifetime()> lifetime_factory);
+// Takes as input the type of the object that the lifetime is being created for
+// and the name of the lifetime parameter, if any.
+using LifetimeFactory =
+ std::function<llvm::Expected<Lifetime>(clang::QualType, llvm::StringRef)>;
class ObjectLifetimes;
// Represents the lifetimes of a value; these may be 0 for non-reference-like
// types, 1 for pointers/references, and an arbitrary number for structs with
// template arguments/lifetime parameters.
-// This is a more structured representation than TypeLifetimes that is easier
-// to query.
class ValueLifetimes {
public:
- static ValueLifetimes FromTypeLifetimes(TypeLifetimesRef& type_lifetimes,
- clang::QualType type);
+ // Creates an invalid ValueLifetimes, which should not be used. This is
+ // provided only for usage with functions with output parameters.
+ ValueLifetimes() : type_(clang::QualType()) {}
+
+ // Creates a ValueLifetimes for a *value* of a given type.
+ // Only fails if lifetime_factory fails.
+ // Lifetimes will be created in post-order in the tree of lifetimes.
+ static llvm::Expected<ValueLifetimes> Create(
+ clang::QualType type, LifetimeFactory lifetime_factory);
// Returns a ValueLifetimes for a lifetime-less type.
// `type` must not be a pointer-like type or a record type.
@@ -88,7 +79,9 @@
ValueLifetimes& operator=(const ValueLifetimes& other);
- std::string DebugString() const;
+ std::string DebugString(const LifetimeFormatter& formatter = [](Lifetime l) {
+ return l.DebugString();
+ }) const;
// Returns the type of the value.
clang::QualType Type() const { return type_; }
@@ -117,13 +110,28 @@
return ret.value();
}
+ bool HasLifetimes() const {
+ return pointee_lifetimes_ != nullptr ||
+ !lifetime_parameters_by_name_.GetMapping().empty() ||
+ !template_argument_lifetimes_.empty();
+ }
+
+ // Traverses all the lifetimes in the object, recursively. The
+ // visit is done in post-order on the lifetime tree of this type.
+ // The callback may mutate the lifetime in an arbitrary way; `variance` will
+ // be set to signal the variance of the lifetime in the position it appears
+ // in; this is decided with the `variance` parameter to Traverse, which
+ // signals the variance of the current Value. Note that the visitor may be
+ // called multiple times with the same lifetime (but in different positions).
+ // TODO(veluca): verify that the handling of variance is correct in all cases.
+ void Traverse(std::function<void(Lifetime&, Variance)> visitor,
+ Variance variance = kCovariant);
+ void Traverse(std::function<void(const Lifetime&, Variance)> visitor,
+ Variance variance = kCovariant) const;
+
private:
explicit ValueLifetimes(clang::QualType type) : type_(type) {}
- static void ReverseVisitTemplateArgs(
- llvm::ArrayRef<clang::TemplateArgument> template_args,
- TypeLifetimesRef& type_lifetimes, ValueLifetimes& out);
-
// Note: only one of `pointee_lifetime` or `template_argument_lifetimes`
// is non-empty.
std::unique_ptr<ObjectLifetimes> pointee_lifetimes_;
@@ -151,17 +159,23 @@
friend class llvm::DenseMapInfo<devtools_rust::ValueLifetimes>;
};
-// Represents all the lifetimes of an object.
-// This is a more structured representation than TypeLifetimes that is easier
-// to query.
+// Represents all the lifetimes of an object. The object itself always has
+// a lifetime; in addition, there may be lifetimes associated with the value
+// of the object.
class ObjectLifetimes {
public:
- // Constructs the ObjectLifetimes corresponding to the given `type_lifetimes`
- // when interpreted as the lifetimes of a glvalue of the given
- // `type`. Removes the consumed lifetimes from `type_lifetimes` (which
- // simulates undoing a post-order visit of the lifetime_ tree).
- static ObjectLifetimes FromTypeLifetimes(TypeLifetimesRef& type_lifetimes,
- clang::QualType type);
+ // Creates an invalid ObjectLifetimes, which should not be used. This is
+ // provided only for usage with functions with output parameters.
+ ObjectLifetimes() {}
+
+ ObjectLifetimes(Lifetime lifetime, ValueLifetimes value_lifetimes)
+ : lifetime_(lifetime), value_lifetimes_(value_lifetimes) {}
+
+ // Creates lifetimes for an *object* of a given type.
+ // Only fails if lifetime_factory fails.
+ // Lifetimes will be created in post-order in the tree of lifetimes.
+ static llvm::Expected<ObjectLifetimes> Create(
+ clang::QualType type, LifetimeFactory lifetime_factory);
// Returns the lifetime of the object itself.
Lifetime GetLifetime() const { return lifetime_; }
@@ -169,13 +183,34 @@
// Returns the lifetime of the contained value.
const ValueLifetimes& GetValueLifetimes() const { return value_lifetimes_; }
- std::string DebugString() const;
+ clang::QualType Type() const { return value_lifetimes_.Type(); }
+
+ std::string DebugString(const LifetimeFormatter& formatter = [](Lifetime l) {
+ return l.DebugString();
+ }) const;
// Returns the ObjectLifetimes for a base class or a field of the given type.
ObjectLifetimes GetFieldOrBaseLifetimes(
clang::QualType type,
llvm::SmallVector<std::string> type_lifetime_args) const;
+ // Traverses all the lifetimes in the object, recursively. The
+ // visit is done in post-order on the lifetime tree of this type.
+ // The callback may mutate the lifetime in an arbitrary way; `variance` will
+ // be set to signal the variance of the lifetime in the position it appears
+ // in; this is decided with the `variance` parameter to Traverse, which
+ // signals the variance of the current Object. Note that the visitor may be
+ // called multiple times with the same lifetime (but in different positions).
+ // `indirection_type` defines the type of the pointer (or reference) to this
+ // object; this is used to determine variance of its pointees.
+ // TODO(veluca): verify that the handling of variance is correct in all cases.
+ void Traverse(std::function<void(Lifetime&, Variance)> visitor,
+ Variance variance = kCovariant,
+ clang::QualType indirection_type = clang::QualType());
+ void Traverse(std::function<void(const Lifetime&, Variance)> visitor,
+ Variance variance = kCovariant,
+ clang::QualType indirection_type = clang::QualType()) const;
+
private:
// Returns the ObjectLifetimes for an object of a given type, whose lifetimes
// are scoped within or derived from the object that `this`
@@ -187,9 +222,6 @@
clang::QualType type, llvm::SmallVector<std::string> type_lifetime_args,
llvm::StringRef object_lifetime_parameter) const;
- ObjectLifetimes(Lifetime lifetime, ValueLifetimes value_lifetimes)
- : lifetime_(lifetime), value_lifetimes_(value_lifetimes) {}
-
friend class llvm::DenseMapInfo<devtools_rust::ObjectLifetimes>;
Lifetime lifetime_;
ValueLifetimes value_lifetimes_;
diff --git a/rs_bindings_from_cc/importer.cc b/rs_bindings_from_cc/importer.cc
index 4f35b30..ceeeedd 100644
--- a/rs_bindings_from_cc/importer.cc
+++ b/rs_bindings_from_cc/importer.cc
@@ -25,6 +25,7 @@
#include "third_party/absl/strings/str_join.h"
#include "third_party/absl/strings/string_view.h"
#include "third_party/absl/strings/substitute.h"
+#include "lifetime_annotations/type_lifetimes.h"
#include "rs_bindings_from_cc/ast_convert.h"
#include "rs_bindings_from_cc/bazel_types.h"
#include "rs_bindings_from_cc/ir.h"
@@ -488,7 +489,6 @@
devtools_rust::GetLifetimeAnnotations(function_decl,
*invocation_.lifetime_context_,
&lifetime_symbol_table);
- llvm::DenseSet<devtools_rust::Lifetime> all_lifetimes;
std::vector<FuncParam> params;
std::set<std::string> errors;
@@ -505,10 +505,9 @@
// non-static member functions receive an implicit `this` parameter.
if (method_decl->isInstance()) {
- std::optional<devtools_rust::TypeLifetimes> this_lifetimes;
+ std::optional<devtools_rust::ValueLifetimes> this_lifetimes;
if (lifetimes) {
- this_lifetimes = lifetimes->this_lifetimes;
- all_lifetimes.insert(this_lifetimes->begin(), this_lifetimes->end());
+ this_lifetimes = lifetimes->GetThisLifetimes();
}
auto param_type =
ConvertQualType(method_decl->getThisType(), this_lifetimes,
@@ -523,15 +522,14 @@
}
if (lifetimes) {
- CRUBIT_CHECK(lifetimes->param_lifetimes.size() ==
- function_decl->getNumParams());
+ CRUBIT_CHECK(lifetimes->IsValidForDecl(function_decl));
}
+
for (unsigned i = 0; i < function_decl->getNumParams(); ++i) {
const clang::ParmVarDecl* param = function_decl->getParamDecl(i);
- std::optional<devtools_rust::TypeLifetimes> param_lifetimes;
+ std::optional<devtools_rust::ValueLifetimes> param_lifetimes;
if (lifetimes) {
- param_lifetimes = lifetimes->param_lifetimes[i];
- all_lifetimes.insert(param_lifetimes->begin(), param_lifetimes->end());
+ param_lifetimes = lifetimes->GetParamLifetimes(i);
}
auto param_type = ConvertQualType(param->getType(), param_lifetimes);
if (!param_type.ok()) {
@@ -577,11 +575,11 @@
}
}
- std::optional<devtools_rust::TypeLifetimes> return_lifetimes;
+ std::optional<devtools_rust::ValueLifetimes> return_lifetimes;
if (lifetimes) {
- return_lifetimes = lifetimes->return_lifetimes;
- all_lifetimes.insert(return_lifetimes->begin(), return_lifetimes->end());
+ return_lifetimes = lifetimes->GetReturnLifetimes();
}
+
auto return_type =
ConvertQualType(function_decl->getReturnType(), return_lifetimes);
if (!return_type.ok()) {
@@ -589,6 +587,14 @@
return_type.status().message()));
}
+ llvm::DenseSet<devtools_rust::Lifetime> all_lifetimes;
+ if (lifetimes) {
+ lifetimes->Traverse(
+ [&all_lifetimes](devtools_rust::Lifetime l, devtools_rust::Variance) {
+ all_lifetimes.insert(l);
+ });
+ }
+
std::vector<Lifetime> lifetime_params;
for (devtools_rust::Lifetime lifetime : all_lifetimes) {
std::optional<llvm::StringRef> name =
@@ -827,7 +833,7 @@
enum_decl,
"Forward declared enums without type specifiers are not supported");
}
- std::optional<devtools_rust::TypeLifetimes> no_lifetimes;
+ std::optional<devtools_rust::ValueLifetimes> no_lifetimes;
absl::StatusOr<MappedType> type = ConvertQualType(cc_type, no_lifetimes);
if (!type.ok()) {
return ImportUnsupportedItem(enum_decl, type.status().ToString());
@@ -901,7 +907,7 @@
std::optional<Identifier> identifier =
GetTranslatedIdentifier(typedef_name_decl);
CRUBIT_CHECK(identifier.has_value()); // This should never happen.
- std::optional<devtools_rust::TypeLifetimes> no_lifetimes;
+ std::optional<devtools_rust::ValueLifetimes> no_lifetimes;
absl::StatusOr<MappedType> underlying_type =
ConvertQualType(typedef_name_decl->getUnderlyingType(), no_lifetimes);
if (underlying_type.ok()) {
@@ -974,7 +980,7 @@
absl::StatusOr<MappedType> Importer::ConvertType(
const clang::Type* type,
- std::optional<devtools_rust::TypeLifetimes>& lifetimes,
+ std::optional<devtools_rust::ValueLifetimes>& lifetimes,
bool nullable) const {
// Qualifiers are handled separately in ConvertQualType().
std::string type_string = clang::QualType(type, 0).getAsString();
@@ -986,9 +992,9 @@
clang::QualType pointee_type = type->getPointeeType();
std::optional<LifetimeId> lifetime;
if (lifetimes.has_value()) {
- CRUBIT_CHECK(!lifetimes->empty());
- lifetime = LifetimeId(lifetimes->back().Id());
- lifetimes->pop_back();
+ lifetime =
+ LifetimeId(lifetimes->GetPointeeLifetimes().GetLifetime().Id());
+ lifetimes = lifetimes->GetPointeeLifetimes().GetValueLifetimes();
}
if (const auto* func_type =
pointee_type->getAs<clang::FunctionProtoType>()) {
@@ -1079,7 +1085,7 @@
absl::StatusOr<MappedType> Importer::ConvertQualType(
clang::QualType qual_type,
- std::optional<devtools_rust::TypeLifetimes>& lifetimes,
+ std::optional<devtools_rust::ValueLifetimes>& lifetimes,
bool nullable) const {
std::string type_string = qual_type.getAsString();
absl::StatusOr<MappedType> type =
@@ -1108,7 +1114,7 @@
std::vector<Field> fields;
const clang::ASTRecordLayout& layout = ctx_.getASTRecordLayout(record_decl);
for (const clang::FieldDecl* field_decl : record_decl->fields()) {
- std::optional<devtools_rust::TypeLifetimes> no_lifetimes;
+ std::optional<devtools_rust::ValueLifetimes> no_lifetimes;
auto type = ConvertQualType(field_decl->getType(), no_lifetimes);
if (!type.ok()) {
return absl::UnimplementedError(absl::Substitute(
diff --git a/rs_bindings_from_cc/importer.h b/rs_bindings_from_cc/importer.h
index 54cd6b2..4ea7052 100644
--- a/rs_bindings_from_cc/importer.h
+++ b/rs_bindings_from_cc/importer.h
@@ -18,6 +18,7 @@
#include "third_party/absl/status/statusor.h"
#include "third_party/absl/types/span.h"
#include "lifetime_annotations/lifetime_annotations.h"
+#include "lifetime_annotations/type_lifetimes.h"
#include "rs_bindings_from_cc/bazel_types.h"
#include "rs_bindings_from_cc/ir.h"
#include "rs_bindings_from_cc/util/check.h"
@@ -162,11 +163,11 @@
// nullability annotations.
absl::StatusOr<MappedType> ConvertQualType(
clang::QualType qual_type,
- std::optional<devtools_rust::TypeLifetimes>& lifetimes,
+ std::optional<devtools_rust::ValueLifetimes>& lifetimes,
bool nullable = true) const;
absl::StatusOr<MappedType> ConvertType(
const clang::Type* type,
- std::optional<devtools_rust::TypeLifetimes>& lifetimes,
+ std::optional<devtools_rust::ValueLifetimes>& lifetimes,
bool nullable) const;
absl::StatusOr<MappedType> ConvertTypeDecl(const clang::TypeDecl* decl) const;