Skip to content

[clang] Add preliminary lifetimebound support to APINotes #114830

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion clang/include/clang/APINotes/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -425,14 +425,24 @@ 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.
unsigned RawRetainCountConvention : 3;

public:
ParamInfo()
: NoEscapeSpecified(false), NoEscape(false), RawRetainCountConvention() {}
: NoEscapeSpecified(false), NoEscape(false),
LifetimeboundSpecified(false), Lifetimebound(false),
RawRetainCountConvention() {}

std::optional<bool> isNoEscape() const {
if (!NoEscapeSpecified)
Expand All @@ -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;
Comment on lines +458 to +460
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (!LifetimeboundSpecified)
return std::nullopt;
return Lifetimebound;
return LifetimeboundSpecified ? Lifetimebound : std::nullopt;

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh, sorry. I forgot about this comment. I will incorporate this into a follow up PR. Thanks a lot for the review!

}
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;
Expand All @@ -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;

Expand All @@ -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;
}

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/APINotes/APINotesFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/APINotes/APINotesReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/APINotes/APINotesTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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';
}
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/APINotes/APINotesWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/APINotes/APINotesYAMLCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(""));
}
};
Expand Down Expand Up @@ -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)
Expand Down
25 changes: 16 additions & 9 deletions clang/lib/Sema/SemaAPINotes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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())) {
Expand Down Expand Up @@ -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());
Expand All @@ -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> {
Expand Down Expand Up @@ -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) &&
Expand Down
14 changes: 14 additions & 0 deletions clang/test/APINotes/Inputs/Headers/Lifetimebound.apinotes
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions clang/test/APINotes/Inputs/Headers/Lifetimebound.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
int *funcToAnnotate(int *p);

// TODO: support annotating ctors and 'this'.
struct MyClass {
MyClass(int*);
int *annotateThis();
int *methodToAnnotate(int *p);
};
5 changes: 5 additions & 0 deletions clang/test/APINotes/Inputs/Headers/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ module Fields {
export *
}

module Lifetimebound {
header "Lifetimebound.h"
export *
}

module HeaderLib {
header "HeaderLib.h"
}
Expand Down
13 changes: 13 additions & 0 deletions clang/test/APINotes/lifetimebound.cpp
Original file line number Diff line number Diff line change
@@ -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
Loading