[clang] 7ac78f1 - [clang] Add preliminary lifetimebound support to APINotes (#114830)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Nov 4 12:30:00 PST 2024
Author: Gábor Horváth
Date: 2024-11-04T20:29:56Z
New Revision: 7ac78f13421c6e5dee4655211fc35225bb8812bc
URL: https://github.com/llvm/llvm-project/commit/7ac78f13421c6e5dee4655211fc35225bb8812bc
DIFF: https://github.com/llvm/llvm-project/commit/7ac78f13421c6e5dee4655211fc35225bb8812bc.diff
LOG: [clang] Add preliminary lifetimebound support to APINotes (#114830)
Added:
clang/test/APINotes/Inputs/Headers/Lifetimebound.apinotes
clang/test/APINotes/Inputs/Headers/Lifetimebound.h
clang/test/APINotes/lifetimebound.cpp
Modified:
clang/include/clang/APINotes/Types.h
clang/lib/APINotes/APINotesFormat.h
clang/lib/APINotes/APINotesReader.cpp
clang/lib/APINotes/APINotesTypes.cpp
clang/lib/APINotes/APINotesWriter.cpp
clang/lib/APINotes/APINotesYAMLCompiler.cpp
clang/lib/Sema/SemaAPINotes.cpp
clang/test/APINotes/Inputs/Headers/module.modulemap
Removed:
################################################################################
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
More information about the cfe-commits
mailing list