Skip to content

Commit 69cdbc8

Browse files
author
Gabor Horvath
committed
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.
1 parent b5dc7b8 commit 69cdbc8

File tree

11 files changed

+99
-11
lines changed

11 files changed

+99
-11
lines changed

clang/include/clang/APINotes/Types.h

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,14 +425,24 @@ class ParamInfo : public VariableInfo {
425425
LLVM_PREFERRED_TYPE(bool)
426426
unsigned NoEscape : 1;
427427

428+
/// Whether lifetimebound was specified.
429+
LLVM_PREFERRED_TYPE(bool)
430+
unsigned LifetimeboundSpecified : 1;
431+
432+
/// Whether the this parameter has the 'lifetimebound' attribute.
433+
LLVM_PREFERRED_TYPE(bool)
434+
unsigned Lifetimebound : 1;
435+
428436
/// A biased RetainCountConventionKind, where 0 means "unspecified".
429437
///
430438
/// Only relevant for out-parameters.
431439
unsigned RawRetainCountConvention : 3;
432440

433441
public:
434442
ParamInfo()
435-
: NoEscapeSpecified(false), NoEscape(false), RawRetainCountConvention() {}
443+
: NoEscapeSpecified(false), NoEscape(false),
444+
LifetimeboundSpecified(false), Lifetimebound(false),
445+
RawRetainCountConvention() {}
436446

437447
std::optional<bool> isNoEscape() const {
438448
if (!NoEscapeSpecified)
@@ -444,6 +454,16 @@ class ParamInfo : public VariableInfo {
444454
NoEscape = Value.value_or(false);
445455
}
446456

457+
std::optional<bool> isLifetimebound() const {
458+
if (!LifetimeboundSpecified)
459+
return std::nullopt;
460+
return Lifetimebound;
461+
}
462+
void setLifetimebound(std::optional<bool> Value) {
463+
LifetimeboundSpecified = Value.has_value();
464+
Lifetimebound = Value.value_or(false);
465+
}
466+
447467
std::optional<RetainCountConventionKind> getRetainCountConvention() const {
448468
if (!RawRetainCountConvention)
449469
return std::nullopt;
@@ -463,6 +483,11 @@ class ParamInfo : public VariableInfo {
463483
NoEscape = RHS.NoEscape;
464484
}
465485

486+
if (!LifetimeboundSpecified && RHS.LifetimeboundSpecified) {
487+
LifetimeboundSpecified = true;
488+
Lifetimebound = RHS.Lifetimebound;
489+
}
490+
466491
if (!RawRetainCountConvention)
467492
RawRetainCountConvention = RHS.RawRetainCountConvention;
468493

@@ -478,6 +503,8 @@ inline bool operator==(const ParamInfo &LHS, const ParamInfo &RHS) {
478503
return static_cast<const VariableInfo &>(LHS) == RHS &&
479504
LHS.NoEscapeSpecified == RHS.NoEscapeSpecified &&
480505
LHS.NoEscape == RHS.NoEscape &&
506+
LHS.LifetimeboundSpecified == RHS.LifetimeboundSpecified &&
507+
LHS.Lifetimebound == RHS.Lifetimebound &&
481508
LHS.RawRetainCountConvention == RHS.RawRetainCountConvention;
482509
}
483510

clang/lib/APINotes/APINotesFormat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const uint16_t VERSION_MAJOR = 0;
2424
/// API notes file minor version number.
2525
///
2626
/// When the format changes IN ANY WAY, this number should be incremented.
27-
const uint16_t VERSION_MINOR = 30; // fields
27+
const uint16_t VERSION_MINOR = 31; // lifetimebound
2828

2929
const uint8_t kSwiftCopyable = 1;
3030
const uint8_t kSwiftNonCopyable = 2;

clang/lib/APINotes/APINotesReader.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,9 @@ void ReadParamInfo(const uint8_t *&Data, ParamInfo &Info) {
331331
Info.setRetainCountConvention(Convention);
332332
}
333333
Payload >>= 3;
334+
if (Payload & 0x01)
335+
Info.setLifetimebound(Payload & 0x02);
336+
Payload >>= 2;
334337
if (Payload & 0x01)
335338
Info.setNoEscape(Payload & 0x02);
336339
Payload >>= 2;

clang/lib/APINotes/APINotesTypes.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ LLVM_DUMP_METHOD void ParamInfo::dump(llvm::raw_ostream &OS) const {
6565
static_cast<const VariableInfo &>(*this).dump(OS);
6666
if (NoEscapeSpecified)
6767
OS << (NoEscape ? "[NoEscape] " : "");
68+
if (LifetimeboundSpecified)
69+
OS << (Lifetimebound ? "[Lifetimebound] " : "");
6870
OS << "RawRetainCountConvention: " << RawRetainCountConvention << ' ';
6971
OS << '\n';
7072
}

clang/lib/APINotes/APINotesWriter.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,12 @@ void emitParamInfo(raw_ostream &OS, const ParamInfo &PI) {
10521052
if (*noescape)
10531053
flags |= 0x02;
10541054
}
1055+
flags <<= 2;
1056+
if (auto lifetimebound = PI.isLifetimebound()) {
1057+
flags |= 0x01;
1058+
if (*lifetimebound)
1059+
flags |= 0x02;
1060+
}
10551061
flags <<= 3;
10561062
if (auto RCC = PI.getRetainCountConvention())
10571063
flags |= static_cast<uint8_t>(RCC.value()) + 1;

clang/lib/APINotes/APINotesYAMLCompiler.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ namespace {
7070
struct Param {
7171
unsigned Position;
7272
std::optional<bool> NoEscape = false;
73+
std::optional<bool> Lifetimebound = false;
7374
std::optional<NullabilityKind> Nullability;
7475
std::optional<RetainCountConventionKind> RetainCountConvention;
7576
StringRef Type;
@@ -121,6 +122,7 @@ template <> struct MappingTraits<Param> {
121122
IO.mapOptional("Nullability", P.Nullability, std::nullopt);
122123
IO.mapOptional("RetainCountConvention", P.RetainCountConvention);
123124
IO.mapOptional("NoEscape", P.NoEscape);
125+
IO.mapOptional("Lifetimebound", P.Lifetimebound);
124126
IO.mapOptional("Type", P.Type, StringRef(""));
125127
}
126128
};
@@ -734,6 +736,7 @@ class YAMLConverter {
734736
if (P.Nullability)
735737
PI.setNullabilityAudited(*P.Nullability);
736738
PI.setNoEscape(P.NoEscape);
739+
PI.setLifetimebound(P.Lifetimebound);
737740
PI.setType(std::string(P.Type));
738741
PI.setRetainCountConvention(P.RetainCountConvention);
739742
if (OutInfo.Params.size() <= P.Position)

clang/lib/Sema/SemaAPINotes.cpp

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "clang/APINotes/APINotesReader.h"
1414
#include "clang/AST/Decl.h"
15+
#include "clang/AST/DeclCXX.h"
1516
#include "clang/AST/DeclObjC.h"
1617
#include "clang/Basic/SourceLocation.h"
1718
#include "clang/Lex/Lexer.h"
@@ -415,6 +416,13 @@ static void ProcessAPINotes(Sema &S, ParmVarDecl *D,
415416
return new (S.Context) NoEscapeAttr(S.Context, getPlaceholderAttrInfo());
416417
});
417418

419+
if (auto Lifetimebound = Info.isLifetimebound())
420+
handleAPINotedAttribute<LifetimeBoundAttr>(
421+
S, D, *Lifetimebound, Metadata, [&] {
422+
return new (S.Context)
423+
LifetimeBoundAttr(S.Context, getPlaceholderAttrInfo());
424+
});
425+
418426
// Retain count convention
419427
handleAPINotedRetainCountConvention(S, D, Metadata,
420428
Info.getRetainCountConvention());
@@ -860,13 +868,12 @@ void Sema::ProcessAPINotes(Decl *D) {
860868
if (!D)
861869
return;
862870

871+
auto *DC = D->getDeclContext();
863872
// Globals.
864-
if (D->getDeclContext()->isFileContext() ||
865-
D->getDeclContext()->isNamespace() ||
866-
D->getDeclContext()->isExternCContext() ||
867-
D->getDeclContext()->isExternCXXContext()) {
873+
if (DC->isFileContext() || DC->isNamespace() || DC->isExternCContext() ||
874+
DC->isExternCXXContext()) {
868875
std::optional<api_notes::Context> APINotesContext =
869-
UnwindNamespaceContext(D->getDeclContext(), APINotes);
876+
UnwindNamespaceContext(DC, APINotes);
870877
// Global variables.
871878
if (auto VD = dyn_cast<VarDecl>(D)) {
872879
for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
@@ -967,8 +974,8 @@ void Sema::ProcessAPINotes(Decl *D) {
967974
}
968975

969976
// Enumerators.
970-
if (D->getDeclContext()->getRedeclContext()->isFileContext() ||
971-
D->getDeclContext()->getRedeclContext()->isExternCContext()) {
977+
if (DC->getRedeclContext()->isFileContext() ||
978+
DC->getRedeclContext()->isExternCContext()) {
972979
if (auto EnumConstant = dyn_cast<EnumConstantDecl>(D)) {
973980
for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
974981
auto Info = Reader->lookupEnumConstant(EnumConstant->getName());
@@ -979,7 +986,7 @@ void Sema::ProcessAPINotes(Decl *D) {
979986
}
980987
}
981988

982-
if (auto ObjCContainer = dyn_cast<ObjCContainerDecl>(D->getDeclContext())) {
989+
if (auto ObjCContainer = dyn_cast<ObjCContainerDecl>(DC)) {
983990
// Location function that looks up an Objective-C context.
984991
auto GetContext = [&](api_notes::APINotesReader *Reader)
985992
-> std::optional<api_notes::ContextID> {
@@ -1063,7 +1070,7 @@ void Sema::ProcessAPINotes(Decl *D) {
10631070
}
10641071
}
10651072

1066-
if (auto TagContext = dyn_cast<TagDecl>(D->getDeclContext())) {
1073+
if (auto TagContext = dyn_cast<TagDecl>(DC)) {
10671074
if (auto CXXMethod = dyn_cast<CXXMethodDecl>(D)) {
10681075
if (!isa<CXXConstructorDecl>(CXXMethod) &&
10691076
!isa<CXXDestructorDecl>(CXXMethod) &&
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
Name: Lifetimebound
3+
Functions:
4+
- Name: funcToAnnotate
5+
Parameters:
6+
- Position: 0
7+
Lifetimebound: true
8+
Tags:
9+
- Name: MyClass
10+
Methods:
11+
- Name: methodToAnnotate
12+
Parameters:
13+
- Position: 0
14+
Lifetimebound: true
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
int *funcToAnnotate(int *p);
2+
3+
// TODO: support annotating ctors and 'this'.
4+
struct MyClass {
5+
MyClass(int*);
6+
int *annotateThis();
7+
int *methodToAnnotate(int *p);
8+
};

clang/test/APINotes/Inputs/Headers/module.modulemap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ module Fields {
1717
export *
1818
}
1919

20+
module Lifetimebound {
21+
header "Lifetimebound.h"
22+
export *
23+
}
24+
2025
module HeaderLib {
2126
header "HeaderLib.h"
2227
}

0 commit comments

Comments
 (0)