Lifetime elision for implicitly defaulted constructors.
Implicitly defaulted constructors return null from their
`getTypeSourceInfo()`, but we still need lifetime elision (because
without lifetimes `rs_bindings_from_cc` won't use Rust references in the
generated bindings and constructors won't be translated into
Default::default, etc).
To provide lifetime elision in this scenario, this CL tweaks
lifetime_annotations.cc to work with `QualType` rather than `TypeLoc`.
PiperOrigin-RevId: 424360966
diff --git a/lifetime_annotations/lifetime_annotations_test.cc b/lifetime_annotations/lifetime_annotations_test.cc
index e590c6a..af9d50c 100644
--- a/lifetime_annotations/lifetime_annotations_test.cc
+++ b/lifetime_annotations/lifetime_annotations_test.cc
@@ -13,6 +13,7 @@
#include "testing/base/public/gunit.h"
#include "third_party/llvm/llvm-project/clang/include/clang/ASTMatchers/ASTMatchFinder.h"
#include "third_party/llvm/llvm-project/clang/include/clang/ASTMatchers/ASTMatchers.h"
+#include "third_party/llvm/llvm-project/llvm/include/llvm/Support/FormatVariadic.h"
namespace devtools_rust {
namespace {
@@ -21,10 +22,17 @@
using testing::status::IsOkAndHolds;
using testing::status::StatusIs;
+bool IsOverloaded(const clang::FunctionDecl* func) {
+ return !func->getDeclContext()->lookup(func->getDeclName()).isSingleResult();
+}
+
std::string QualifiedName(const clang::FunctionDecl* func) {
std::string str;
llvm::raw_string_ostream ostream(str);
func->printQualifiedName(ostream);
+ if (IsOverloaded(func)) {
+ ostream << "[" << func->getType().getAsString() << "]";
+ }
ostream.flush();
return str;
}
@@ -58,9 +66,25 @@
llvm::toString(func_lifetimes.takeError()));
return;
}
- named_func_lifetimes.Add(
- QualifiedName(func),
- NameLifetimes(*func_lifetimes, symbol_table));
+ std::string func_name = QualifiedName(func);
+ std::string new_entry =
+ NameLifetimes(*func_lifetimes, symbol_table);
+ std::optional<llvm::StringRef> old_entry =
+ named_func_lifetimes.Get(func_name);
+ if (old_entry.has_value()) {
+ if (new_entry != old_entry.value()) {
+ result = absl::UnknownError(
+ llvm::formatv(
+ "Unexpectedly different lifetimes for function '{0}'."
+ "Old: '{1}'. New: '{2}'.",
+ func_name, old_entry.value(), new_entry)
+ .str());
+ return;
+ }
+ } else {
+ named_func_lifetimes.Add(std::move(func_name),
+ std::move(new_entry));
+ }
}
}
@@ -260,5 +284,27 @@
StartsWith("Invalid lifetime annotation")));
}
+TEST_F(LifetimeAnnotationsTest, LifetimeElision_ExplicitlyDefaultedCtor) {
+ EXPECT_THAT(GetNamedLifetimeAnnotations(R"(
+ #pragma clang lifetime_elision
+ struct S {
+ S() = default;
+ };)"),
+ IsOkAndHolds(LifetimesAre({{"S::S", "a:"}})));
+}
+
+TEST_F(LifetimeAnnotationsTest, LifetimeElision_ImplicitlyDefaultedCtor) {
+ // Implicitly-defaulted constructors don't have associated `TypeSourceInfo`.
+ EXPECT_THAT(
+ GetNamedLifetimeAnnotations(R"(
+ #pragma clang lifetime_elision
+ struct S {};
+ // We need to use the implicitly-defaulted constructors to make
+ // them appear in the AST so that we can process them.
+ void foo() { S s; }
+ )"),
+ IsOkAndHolds(LifetimesContain({{"S::S[void (void) noexcept]", "a:"}})));
+}
+
} // namespace
} // namespace devtools_rust