Skip to content

Commit 7ac78f1

Browse files
authored
[clang] Add preliminary lifetimebound support to APINotes (llvm#114830)
1 parent 3e15454 commit 7ac78f1

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
}

clang/test/APINotes/lifetimebound.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: rm -rf %t && mkdir -p %t
2+
// 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++
3+
// 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
4+
// 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
5+
#include "Lifetimebound.h"
6+
7+
// CHECK-PARAM: FunctionDecl {{.+}} funcToAnnotate
8+
// CHECK-PARAM-NEXT: ParmVarDecl {{.+}} p
9+
// CHECK-PARAM-NEXT: LifetimeBoundAttr
10+
11+
// CHECK-METHOD: CXXMethodDecl {{.+}} methodToAnnotate
12+
// CHECK-METHOD-NEXT: ParmVarDecl {{.+}} p
13+
// CHECK-METHOD-NEXT: LifetimeBoundAttr

0 commit comments

Comments
 (0)