https://github.com/Xazax-hun created https://github.com/llvm/llvm-project/pull/114830
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. From 69cdbc8d9ba737ecc524f0a8cfb61018268c160a Mon Sep 17 00:00:00 2001 From: Gabor Horvath <gab...@apple.com> Date: Mon, 4 Nov 2024 16:28:31 +0000 Subject: [PATCH] Add preliminary lifetimebound support to APINotes 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. --- clang/include/clang/APINotes/Types.h | 29 ++++++++++++++++++- clang/lib/APINotes/APINotesFormat.h | 2 +- clang/lib/APINotes/APINotesReader.cpp | 3 ++ clang/lib/APINotes/APINotesTypes.cpp | 2 ++ clang/lib/APINotes/APINotesWriter.cpp | 6 ++++ clang/lib/APINotes/APINotesYAMLCompiler.cpp | 3 ++ clang/lib/Sema/SemaAPINotes.cpp | 25 ++++++++++------ .../Inputs/Headers/Lifetimebound.apinotes | 14 +++++++++ .../APINotes/Inputs/Headers/Lifetimebound.h | 8 +++++ .../APINotes/Inputs/Headers/module.modulemap | 5 ++++ clang/test/APINotes/lifetimebound.cpp | 13 +++++++++ 11 files changed, 99 insertions(+), 11 deletions(-) create mode 100644 clang/test/APINotes/Inputs/Headers/Lifetimebound.apinotes create mode 100644 clang/test/APINotes/Inputs/Headers/Lifetimebound.h create mode 100644 clang/test/APINotes/lifetimebound.cpp 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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits