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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits