[clang] Add preliminary lifetimebound support to APINotes (PR #114830)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Nov 4 08:59:14 PST 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Gábor Horváth (Xazax-hun)
<details>
<summary>Changes</summary>
This patch adds the ability to mark function and method parameters as lifetimebound. Unfortunately, this does not support lifetimebound annotating 'this' (putting the annotation on the method type instead of on the parameters), or annotating constructors. For the latter, we need to support to annotate overloaded functions are ctors are always overloaded.
---
Full diff: https://github.com/llvm/llvm-project/pull/114830.diff
11 Files Affected:
- (modified) clang/include/clang/APINotes/Types.h (+28-1)
- (modified) clang/lib/APINotes/APINotesFormat.h (+1-1)
- (modified) clang/lib/APINotes/APINotesReader.cpp (+3)
- (modified) clang/lib/APINotes/APINotesTypes.cpp (+2)
- (modified) clang/lib/APINotes/APINotesWriter.cpp (+6)
- (modified) clang/lib/APINotes/APINotesYAMLCompiler.cpp (+3)
- (modified) clang/lib/Sema/SemaAPINotes.cpp (+16-9)
- (added) clang/test/APINotes/Inputs/Headers/Lifetimebound.apinotes (+14)
- (added) clang/test/APINotes/Inputs/Headers/Lifetimebound.h (+8)
- (modified) clang/test/APINotes/Inputs/Headers/module.modulemap (+5)
- (added) clang/test/APINotes/lifetimebound.cpp (+13)
``````````diff
diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h
index 89889910d1a073..6327b7d75486fc 100644
--- a/clang/include/clang/APINotes/Types.h
+++ b/clang/include/clang/APINotes/Types.h
@@ -425,6 +425,14 @@ class ParamInfo : public VariableInfo {
LLVM_PREFERRED_TYPE(bool)
unsigned NoEscape : 1;
+ /// Whether lifetimebound was specified.
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned LifetimeboundSpecified : 1;
+
+ /// Whether the this parameter has the 'lifetimebound' attribute.
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned Lifetimebound : 1;
+
/// A biased RetainCountConventionKind, where 0 means "unspecified".
///
/// Only relevant for out-parameters.
@@ -432,7 +440,9 @@ class ParamInfo : public VariableInfo {
public:
ParamInfo()
- : NoEscapeSpecified(false), NoEscape(false), RawRetainCountConvention() {}
+ : NoEscapeSpecified(false), NoEscape(false),
+ LifetimeboundSpecified(false), Lifetimebound(false),
+ RawRetainCountConvention() {}
std::optional<bool> isNoEscape() const {
if (!NoEscapeSpecified)
@@ -444,6 +454,16 @@ class ParamInfo : public VariableInfo {
NoEscape = Value.value_or(false);
}
+ std::optional<bool> isLifetimebound() const {
+ if (!LifetimeboundSpecified)
+ return std::nullopt;
+ return Lifetimebound;
+ }
+ void setLifetimebound(std::optional<bool> Value) {
+ LifetimeboundSpecified = Value.has_value();
+ Lifetimebound = Value.value_or(false);
+ }
+
std::optional<RetainCountConventionKind> getRetainCountConvention() const {
if (!RawRetainCountConvention)
return std::nullopt;
@@ -463,6 +483,11 @@ class ParamInfo : public VariableInfo {
NoEscape = RHS.NoEscape;
}
+ if (!LifetimeboundSpecified && RHS.LifetimeboundSpecified) {
+ LifetimeboundSpecified = true;
+ Lifetimebound = RHS.Lifetimebound;
+ }
+
if (!RawRetainCountConvention)
RawRetainCountConvention = RHS.RawRetainCountConvention;
@@ -478,6 +503,8 @@ inline bool operator==(const ParamInfo &LHS, const ParamInfo &RHS) {
return static_cast<const VariableInfo &>(LHS) == RHS &&
LHS.NoEscapeSpecified == RHS.NoEscapeSpecified &&
LHS.NoEscape == RHS.NoEscape &&
+ LHS.LifetimeboundSpecified == RHS.LifetimeboundSpecified &&
+ LHS.Lifetimebound == RHS.Lifetimebound &&
LHS.RawRetainCountConvention == RHS.RawRetainCountConvention;
}
diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h
index d724a9ea471b54..014ee7e2e3d397 100644
--- a/clang/lib/APINotes/APINotesFormat.h
+++ b/clang/lib/APINotes/APINotesFormat.h
@@ -24,7 +24,7 @@ const uint16_t VERSION_MAJOR = 0;
/// API notes file minor version number.
///
/// When the format changes IN ANY WAY, this number should be incremented.
-const uint16_t VERSION_MINOR = 30; // fields
+const uint16_t VERSION_MINOR = 31; // lifetimebound
const uint8_t kSwiftCopyable = 1;
const uint8_t kSwiftNonCopyable = 2;
diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp
index da8a86f7adb16e..1bde8482fce033 100644
--- a/clang/lib/APINotes/APINotesReader.cpp
+++ b/clang/lib/APINotes/APINotesReader.cpp
@@ -331,6 +331,9 @@ void ReadParamInfo(const uint8_t *&Data, ParamInfo &Info) {
Info.setRetainCountConvention(Convention);
}
Payload >>= 3;
+ if (Payload & 0x01)
+ Info.setLifetimebound(Payload & 0x02);
+ Payload >>= 2;
if (Payload & 0x01)
Info.setNoEscape(Payload & 0x02);
Payload >>= 2;
diff --git a/clang/lib/APINotes/APINotesTypes.cpp b/clang/lib/APINotes/APINotesTypes.cpp
index a87ecb3bc30eec..be7dec3295b4ce 100644
--- a/clang/lib/APINotes/APINotesTypes.cpp
+++ b/clang/lib/APINotes/APINotesTypes.cpp
@@ -65,6 +65,8 @@ LLVM_DUMP_METHOD void ParamInfo::dump(llvm::raw_ostream &OS) const {
static_cast<const VariableInfo &>(*this).dump(OS);
if (NoEscapeSpecified)
OS << (NoEscape ? "[NoEscape] " : "");
+ if (LifetimeboundSpecified)
+ OS << (Lifetimebound ? "[Lifetimebound] " : "");
OS << "RawRetainCountConvention: " << RawRetainCountConvention << ' ';
OS << '\n';
}
diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp
index a2b3669a314476..d81394edfde304 100644
--- a/clang/lib/APINotes/APINotesWriter.cpp
+++ b/clang/lib/APINotes/APINotesWriter.cpp
@@ -1052,6 +1052,12 @@ void emitParamInfo(raw_ostream &OS, const ParamInfo &PI) {
if (*noescape)
flags |= 0x02;
}
+ flags <<= 2;
+ if (auto lifetimebound = PI.isLifetimebound()) {
+ flags |= 0x01;
+ if (*lifetimebound)
+ flags |= 0x02;
+ }
flags <<= 3;
if (auto RCC = PI.getRetainCountConvention())
flags |= static_cast<uint8_t>(RCC.value()) + 1;
diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
index f72a1d65b5456f..11578e4d054b7f 100644
--- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp
+++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -70,6 +70,7 @@ namespace {
struct Param {
unsigned Position;
std::optional<bool> NoEscape = false;
+ std::optional<bool> Lifetimebound = false;
std::optional<NullabilityKind> Nullability;
std::optional<RetainCountConventionKind> RetainCountConvention;
StringRef Type;
@@ -121,6 +122,7 @@ template <> struct MappingTraits<Param> {
IO.mapOptional("Nullability", P.Nullability, std::nullopt);
IO.mapOptional("RetainCountConvention", P.RetainCountConvention);
IO.mapOptional("NoEscape", P.NoEscape);
+ IO.mapOptional("Lifetimebound", P.Lifetimebound);
IO.mapOptional("Type", P.Type, StringRef(""));
}
};
@@ -734,6 +736,7 @@ class YAMLConverter {
if (P.Nullability)
PI.setNullabilityAudited(*P.Nullability);
PI.setNoEscape(P.NoEscape);
+ PI.setLifetimebound(P.Lifetimebound);
PI.setType(std::string(P.Type));
PI.setRetainCountConvention(P.RetainCountConvention);
if (OutInfo.Params.size() <= P.Position)
diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp
index ec43a0def9c1e6..edb4c19d59745c 100644
--- a/clang/lib/Sema/SemaAPINotes.cpp
+++ b/clang/lib/Sema/SemaAPINotes.cpp
@@ -12,6 +12,7 @@
#include "clang/APINotes/APINotesReader.h"
#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/Lexer.h"
@@ -415,6 +416,13 @@ static void ProcessAPINotes(Sema &S, ParmVarDecl *D,
return new (S.Context) NoEscapeAttr(S.Context, getPlaceholderAttrInfo());
});
+ if (auto Lifetimebound = Info.isLifetimebound())
+ handleAPINotedAttribute<LifetimeBoundAttr>(
+ S, D, *Lifetimebound, Metadata, [&] {
+ return new (S.Context)
+ LifetimeBoundAttr(S.Context, getPlaceholderAttrInfo());
+ });
+
// Retain count convention
handleAPINotedRetainCountConvention(S, D, Metadata,
Info.getRetainCountConvention());
@@ -860,13 +868,12 @@ void Sema::ProcessAPINotes(Decl *D) {
if (!D)
return;
+ auto *DC = D->getDeclContext();
// Globals.
- if (D->getDeclContext()->isFileContext() ||
- D->getDeclContext()->isNamespace() ||
- D->getDeclContext()->isExternCContext() ||
- D->getDeclContext()->isExternCXXContext()) {
+ if (DC->isFileContext() || DC->isNamespace() || DC->isExternCContext() ||
+ DC->isExternCXXContext()) {
std::optional<api_notes::Context> APINotesContext =
- UnwindNamespaceContext(D->getDeclContext(), APINotes);
+ UnwindNamespaceContext(DC, APINotes);
// Global variables.
if (auto VD = dyn_cast<VarDecl>(D)) {
for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
@@ -967,8 +974,8 @@ void Sema::ProcessAPINotes(Decl *D) {
}
// Enumerators.
- if (D->getDeclContext()->getRedeclContext()->isFileContext() ||
- D->getDeclContext()->getRedeclContext()->isExternCContext()) {
+ if (DC->getRedeclContext()->isFileContext() ||
+ DC->getRedeclContext()->isExternCContext()) {
if (auto EnumConstant = dyn_cast<EnumConstantDecl>(D)) {
for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
auto Info = Reader->lookupEnumConstant(EnumConstant->getName());
@@ -979,7 +986,7 @@ void Sema::ProcessAPINotes(Decl *D) {
}
}
- if (auto ObjCContainer = dyn_cast<ObjCContainerDecl>(D->getDeclContext())) {
+ if (auto ObjCContainer = dyn_cast<ObjCContainerDecl>(DC)) {
// Location function that looks up an Objective-C context.
auto GetContext = [&](api_notes::APINotesReader *Reader)
-> std::optional<api_notes::ContextID> {
@@ -1063,7 +1070,7 @@ void Sema::ProcessAPINotes(Decl *D) {
}
}
- if (auto TagContext = dyn_cast<TagDecl>(D->getDeclContext())) {
+ if (auto TagContext = dyn_cast<TagDecl>(DC)) {
if (auto CXXMethod = dyn_cast<CXXMethodDecl>(D)) {
if (!isa<CXXConstructorDecl>(CXXMethod) &&
!isa<CXXDestructorDecl>(CXXMethod) &&
diff --git a/clang/test/APINotes/Inputs/Headers/Lifetimebound.apinotes b/clang/test/APINotes/Inputs/Headers/Lifetimebound.apinotes
new file mode 100644
index 00000000000000..d07d87cf02f025
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Headers/Lifetimebound.apinotes
@@ -0,0 +1,14 @@
+---
+Name: Lifetimebound
+Functions:
+ - Name: funcToAnnotate
+ Parameters:
+ - Position: 0
+ Lifetimebound: true
+Tags:
+- Name: MyClass
+ Methods:
+ - Name: methodToAnnotate
+ Parameters:
+ - Position: 0
+ Lifetimebound: true
diff --git a/clang/test/APINotes/Inputs/Headers/Lifetimebound.h b/clang/test/APINotes/Inputs/Headers/Lifetimebound.h
new file mode 100644
index 00000000000000..2ec302f7801a77
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Headers/Lifetimebound.h
@@ -0,0 +1,8 @@
+int *funcToAnnotate(int *p);
+
+// TODO: support annotating ctors and 'this'.
+struct MyClass {
+ MyClass(int*);
+ int *annotateThis();
+ int *methodToAnnotate(int *p);
+};
diff --git a/clang/test/APINotes/Inputs/Headers/module.modulemap b/clang/test/APINotes/Inputs/Headers/module.modulemap
index 7d304863398ecf..31f7d36356d83e 100644
--- a/clang/test/APINotes/Inputs/Headers/module.modulemap
+++ b/clang/test/APINotes/Inputs/Headers/module.modulemap
@@ -17,6 +17,11 @@ module Fields {
export *
}
+module Lifetimebound {
+ header "Lifetimebound.h"
+ export *
+}
+
module HeaderLib {
header "HeaderLib.h"
}
diff --git a/clang/test/APINotes/lifetimebound.cpp b/clang/test/APINotes/lifetimebound.cpp
new file mode 100644
index 00000000000000..3e5ce9df895b70
--- /dev/null
+++ b/clang/test/APINotes/lifetimebound.cpp
@@ -0,0 +1,13 @@
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Lifetimebound -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers %s -x c++
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Lifetimebound -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter funcToAnnotate -x c++ | FileCheck --check-prefix=CHECK-PARAM %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/Lifetimebound -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter methodToAnnotate -x c++ | FileCheck --check-prefix=CHECK-METHOD %s
+#include "Lifetimebound.h"
+
+// CHECK-PARAM: FunctionDecl {{.+}} funcToAnnotate
+// CHECK-PARAM-NEXT: ParmVarDecl {{.+}} p
+// CHECK-PARAM-NEXT: LifetimeBoundAttr
+
+// CHECK-METHOD: CXXMethodDecl {{.+}} methodToAnnotate
+// CHECK-METHOD-NEXT: ParmVarDecl {{.+}} p
+// CHECK-METHOD-NEXT: LifetimeBoundAttr
``````````
</details>
https://github.com/llvm/llvm-project/pull/114830
More information about the cfe-commits
mailing list