Skip to content

Commit 5f4e3a3

Browse files
authored
[clang] Support 'this' position for lifetimebound attribute (#115021)
This patch makes the position -1 interpreted as the position for 'this'. Adds some basic infrastructure and support for lifetimebound attribute.
1 parent 6ca50a2 commit 5f4e3a3

File tree

10 files changed

+114
-21
lines changed

10 files changed

+114
-21
lines changed

clang/include/clang/APINotes/Types.h

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -445,19 +445,16 @@ class ParamInfo : public VariableInfo {
445445
RawRetainCountConvention() {}
446446

447447
std::optional<bool> isNoEscape() const {
448-
if (!NoEscapeSpecified)
449-
return std::nullopt;
450-
return NoEscape;
448+
return NoEscapeSpecified ? std::optional<bool>(NoEscape) : std::nullopt;
451449
}
452450
void setNoEscape(std::optional<bool> Value) {
453451
NoEscapeSpecified = Value.has_value();
454452
NoEscape = Value.value_or(false);
455453
}
456454

457455
std::optional<bool> isLifetimebound() const {
458-
if (!LifetimeboundSpecified)
459-
return std::nullopt;
460-
return Lifetimebound;
456+
return LifetimeboundSpecified ? std::optional<bool>(Lifetimebound)
457+
: std::nullopt;
461458
}
462459
void setLifetimebound(std::optional<bool> Value) {
463460
LifetimeboundSpecified = Value.has_value();
@@ -643,6 +640,8 @@ class ObjCMethodInfo : public FunctionInfo {
643640
LLVM_PREFERRED_TYPE(bool)
644641
unsigned RequiredInit : 1;
645642

643+
std::optional<ParamInfo> Self;
644+
646645
ObjCMethodInfo() : DesignatedInit(false), RequiredInit(false) {}
647646

648647
friend bool operator==(const ObjCMethodInfo &, const ObjCMethodInfo &);
@@ -664,7 +663,7 @@ class ObjCMethodInfo : public FunctionInfo {
664663
inline bool operator==(const ObjCMethodInfo &LHS, const ObjCMethodInfo &RHS) {
665664
return static_cast<const FunctionInfo &>(LHS) == RHS &&
666665
LHS.DesignatedInit == RHS.DesignatedInit &&
667-
LHS.RequiredInit == RHS.RequiredInit;
666+
LHS.RequiredInit == RHS.RequiredInit && LHS.Self == RHS.Self;
668667
}
669668

670669
inline bool operator!=(const ObjCMethodInfo &LHS, const ObjCMethodInfo &RHS) {
@@ -693,8 +692,20 @@ class FieldInfo : public VariableInfo {
693692
class CXXMethodInfo : public FunctionInfo {
694693
public:
695694
CXXMethodInfo() {}
695+
696+
std::optional<ParamInfo> This;
697+
698+
LLVM_DUMP_METHOD void dump(llvm::raw_ostream &OS);
696699
};
697700

701+
inline bool operator==(const CXXMethodInfo &LHS, const CXXMethodInfo &RHS) {
702+
return static_cast<const FunctionInfo &>(LHS) == RHS && LHS.This == RHS.This;
703+
}
704+
705+
inline bool operator!=(const CXXMethodInfo &LHS, const CXXMethodInfo &RHS) {
706+
return !(LHS == RHS);
707+
}
708+
698709
/// Describes API notes data for an enumerator.
699710
class EnumConstantInfo : public CommonEntityInfo {
700711
public:

clang/lib/APINotes/APINotesFormat.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ 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 = 31; // lifetimebound
27+
const uint16_t VERSION_MINOR =
28+
32; // implicit parameter support (at position -1)
2829

2930
const uint8_t kSwiftCopyable = 1;
3031
const uint8_t kSwiftNonCopyable = 2;

clang/lib/APINotes/APINotesReader.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
//===----------------------------------------------------------------------===//
1515
#include "clang/APINotes/APINotesReader.h"
1616
#include "APINotesFormat.h"
17+
#include "clang/APINotes/Types.h"
1718
#include "llvm/ADT/Hashing.h"
1819
#include "llvm/ADT/StringExtras.h"
1920
#include "llvm/Bitstream/BitstreamReader.h"
@@ -396,12 +397,19 @@ class ObjCMethodTableInfo
396397
const uint8_t *&Data) {
397398
ObjCMethodInfo Info;
398399
uint8_t Payload = *Data++;
400+
bool HasSelf = Payload & 0x01;
401+
Payload >>= 1;
399402
Info.RequiredInit = Payload & 0x01;
400403
Payload >>= 1;
401404
Info.DesignatedInit = Payload & 0x01;
402405
Payload >>= 1;
406+
assert(Payload == 0 && "Unable to fully decode 'Payload'.");
403407

404408
ReadFunctionInfo(Data, Info);
409+
if (HasSelf) {
410+
Info.Self = ParamInfo{};
411+
ReadParamInfo(Data, *Info.Self);
412+
}
405413
return Info;
406414
}
407415
};
@@ -516,7 +524,17 @@ class CXXMethodTableInfo
516524
static CXXMethodInfo readUnversioned(internal_key_type Key,
517525
const uint8_t *&Data) {
518526
CXXMethodInfo Info;
527+
528+
uint8_t Payload = *Data++;
529+
bool HasThis = Payload & 0x01;
530+
Payload >>= 1;
531+
assert(Payload == 0 && "Unable to fully decode 'Payload'.");
532+
519533
ReadFunctionInfo(Data, Info);
534+
if (HasThis) {
535+
Info.This = ParamInfo{};
536+
ReadParamInfo(Data, *Info.This);
537+
}
520538
return Info;
521539
}
522540
};

clang/lib/APINotes/APINotesTypes.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,18 @@ LLVM_DUMP_METHOD void FunctionInfo::dump(llvm::raw_ostream &OS) const {
8585

8686
LLVM_DUMP_METHOD void ObjCMethodInfo::dump(llvm::raw_ostream &OS) {
8787
static_cast<FunctionInfo &>(*this).dump(OS);
88+
if (Self)
89+
Self->dump(OS);
8890
OS << (DesignatedInit ? "[DesignatedInit] " : "")
8991
<< (RequiredInit ? "[RequiredInit] " : "") << '\n';
9092
}
9193

94+
LLVM_DUMP_METHOD void CXXMethodInfo::dump(llvm::raw_ostream &OS) {
95+
static_cast<FunctionInfo &>(*this).dump(OS);
96+
if (This)
97+
This->dump(OS);
98+
}
99+
92100
LLVM_DUMP_METHOD void TagInfo::dump(llvm::raw_ostream &OS) {
93101
static_cast<CommonTypeInfo &>(*this).dump(OS);
94102
if (HasFlagEnum)

clang/lib/APINotes/APINotesWriter.cpp

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,7 @@ namespace {
649649
unsigned getVariableInfoSize(const VariableInfo &VI) {
650650
return 2 + getCommonEntityInfoSize(VI) + 2 + VI.getType().size();
651651
}
652+
unsigned getParamInfoSize(const ParamInfo &PI);
652653

653654
/// Emit a serialized representation of the variable information.
654655
void emitVariableInfo(raw_ostream &OS, const VariableInfo &VI) {
@@ -737,6 +738,7 @@ void APINotesWriter::Implementation::writeObjCPropertyBlock(
737738
namespace {
738739
unsigned getFunctionInfoSize(const FunctionInfo &);
739740
void emitFunctionInfo(llvm::raw_ostream &, const FunctionInfo &);
741+
void emitParamInfo(raw_ostream &OS, const ParamInfo &PI);
740742

741743
/// Used to serialize the on-disk Objective-C method table.
742744
class ObjCMethodTableInfo
@@ -760,17 +762,24 @@ class ObjCMethodTableInfo
760762
}
761763

762764
unsigned getUnversionedInfoSize(const ObjCMethodInfo &OMI) {
763-
return getFunctionInfoSize(OMI) + 1;
765+
auto size = getFunctionInfoSize(OMI) + 1;
766+
if (OMI.Self)
767+
size += getParamInfoSize(*OMI.Self);
768+
return size;
764769
}
765770

766771
void emitUnversionedInfo(raw_ostream &OS, const ObjCMethodInfo &OMI) {
767772
uint8_t flags = 0;
768773
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
769774
flags = (flags << 1) | OMI.DesignatedInit;
770775
flags = (flags << 1) | OMI.RequiredInit;
776+
flags = (flags << 1) | static_cast<bool>(OMI.Self);
771777
writer.write<uint8_t>(flags);
772778

773779
emitFunctionInfo(OS, OMI);
780+
781+
if (OMI.Self)
782+
emitParamInfo(OS, *OMI.Self);
774783
}
775784
};
776785

@@ -793,12 +802,22 @@ class CXXMethodTableInfo
793802
return static_cast<size_t>(key.hashValue());
794803
}
795804

796-
unsigned getUnversionedInfoSize(const CXXMethodInfo &OMI) {
797-
return getFunctionInfoSize(OMI);
805+
unsigned getUnversionedInfoSize(const CXXMethodInfo &MI) {
806+
auto size = getFunctionInfoSize(MI) + 1;
807+
if (MI.This)
808+
size += getParamInfoSize(*MI.This);
809+
return size;
798810
}
799811

800-
void emitUnversionedInfo(raw_ostream &OS, const CXXMethodInfo &OMI) {
801-
emitFunctionInfo(OS, OMI);
812+
void emitUnversionedInfo(raw_ostream &OS, const CXXMethodInfo &MI) {
813+
uint8_t flags = 0;
814+
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
815+
flags = (flags << 1) | static_cast<bool>(MI.This);
816+
writer.write<uint8_t>(flags);
817+
818+
emitFunctionInfo(OS, MI);
819+
if (MI.This)
820+
emitParamInfo(OS, *MI.This);
802821
}
803822
};
804823
} // namespace

clang/lib/APINotes/APINotesYAMLCompiler.cpp

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "llvm/Support/VersionTuple.h"
2424
#include "llvm/Support/YAMLTraits.h"
2525
#include <optional>
26+
#include <type_traits>
2627
#include <vector>
2728

2829
using namespace clang;
@@ -68,7 +69,7 @@ template <> struct ScalarEnumerationTraits<MethodKind> {
6869

6970
namespace {
7071
struct Param {
71-
unsigned Position;
72+
int Position;
7273
std::optional<bool> NoEscape = false;
7374
std::optional<bool> Lifetimebound = false;
7475
std::optional<NullabilityKind> Nullability;
@@ -730,7 +731,8 @@ class YAMLConverter {
730731
}
731732
}
732733

733-
void convertParams(const ParamsSeq &Params, FunctionInfo &OutInfo) {
734+
void convertParams(const ParamsSeq &Params, FunctionInfo &OutInfo,
735+
std::optional<ParamInfo> &thisOrSelf) {
734736
for (const auto &P : Params) {
735737
ParamInfo PI;
736738
if (P.Nullability)
@@ -739,9 +741,14 @@ class YAMLConverter {
739741
PI.setLifetimebound(P.Lifetimebound);
740742
PI.setType(std::string(P.Type));
741743
PI.setRetainCountConvention(P.RetainCountConvention);
742-
if (OutInfo.Params.size() <= P.Position)
744+
if (static_cast<int>(OutInfo.Params.size()) <= P.Position)
743745
OutInfo.Params.resize(P.Position + 1);
744-
OutInfo.Params[P.Position] |= PI;
746+
if (P.Position == -1)
747+
thisOrSelf = PI;
748+
else if (P.Position >= 0)
749+
OutInfo.Params[P.Position] |= PI;
750+
else
751+
emitError("invalid parameter position " + llvm::itostr(P.Position));
745752
}
746753
}
747754

@@ -818,7 +825,7 @@ class YAMLConverter {
818825
MI.ResultType = std::string(M.ResultType);
819826

820827
// Translate parameter information.
821-
convertParams(M.Params, MI);
828+
convertParams(M.Params, MI, MI.Self);
822829

823830
// Translate nullability info.
824831
convertNullability(M.Nullability, M.NullabilityOfRet, MI, M.Selector);
@@ -926,11 +933,18 @@ class YAMLConverter {
926933
TheNamespace.Items, SwiftVersion);
927934
}
928935

929-
void convertFunction(const Function &Function, FunctionInfo &FI) {
936+
template <typename FuncOrMethodInfo>
937+
void convertFunction(const Function &Function, FuncOrMethodInfo &FI) {
930938
convertAvailability(Function.Availability, FI, Function.Name);
931939
FI.setSwiftPrivate(Function.SwiftPrivate);
932940
FI.SwiftName = std::string(Function.SwiftName);
933-
convertParams(Function.Params, FI);
941+
std::optional<ParamInfo> This;
942+
convertParams(Function.Params, FI, This);
943+
if constexpr (std::is_same_v<FuncOrMethodInfo, CXXMethodInfo>)
944+
FI.This = This;
945+
else if (This)
946+
emitError("implicit instance parameter is only permitted on C++ and "
947+
"Objective-C methods");
934948
convertNullability(Function.Nullability, Function.NullabilityOfRet, FI,
935949
Function.Name);
936950
FI.ResultType = std::string(Function.ResultType);

clang/lib/Sema/SemaAPINotes.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
#include "TypeLocBuilder.h"
1314
#include "clang/APINotes/APINotesReader.h"
1415
#include "clang/AST/Decl.h"
1516
#include "clang/AST/DeclCXX.h"
1617
#include "clang/AST/DeclObjC.h"
18+
#include "clang/AST/TypeLoc.h"
1719
#include "clang/Basic/SourceLocation.h"
1820
#include "clang/Lex/Lexer.h"
1921
#include "clang/Sema/SemaInternal.h"
@@ -567,6 +569,20 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc,
567569
static void ProcessAPINotes(Sema &S, CXXMethodDecl *Method,
568570
const api_notes::CXXMethodInfo &Info,
569571
VersionedInfoMetadata Metadata) {
572+
if (Info.This && Info.This->isLifetimebound()) {
573+
auto MethodType = Method->getType();
574+
auto *attr = ::new (S.Context)
575+
LifetimeBoundAttr(S.Context, getPlaceholderAttrInfo());
576+
QualType AttributedType =
577+
S.Context.getAttributedType(attr, MethodType, MethodType);
578+
TypeLocBuilder TLB;
579+
TLB.pushFullCopy(Method->getTypeSourceInfo()->getTypeLoc());
580+
AttributedTypeLoc TyLoc = TLB.push<AttributedTypeLoc>(AttributedType);
581+
TyLoc.setAttr(attr);
582+
Method->setType(AttributedType);
583+
Method->setTypeSourceInfo(TLB.getTypeSourceInfo(S.Context, AttributedType));
584+
}
585+
570586
ProcessAPINotes(S, (FunctionOrMethod)Method, Info, Metadata);
571587
}
572588

clang/test/APINotes/Inputs/Headers/Lifetimebound.apinotes

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ Functions:
88
Tags:
99
- Name: MyClass
1010
Methods:
11+
- Name: annotateThis
12+
Parameters:
13+
- Position: -1
14+
Lifetimebound: true
1115
- Name: methodToAnnotate
1216
Parameters:
1317
- Position: 0

clang/test/APINotes/Inputs/Headers/Lifetimebound.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
int *funcToAnnotate(int *p);
22

3-
// TODO: support annotating ctors and 'this'.
43
struct MyClass {
54
MyClass(int*);
65
int *annotateThis();

clang/test/APINotes/lifetimebound.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// RUN: rm -rf %t && mkdir -p %t
22
// 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++
33
// 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 annotateThis -x c++ | FileCheck --check-prefix=CHECK-METHOD-THIS %s
45
// 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
56
#include "Lifetimebound.h"
67

@@ -11,3 +12,5 @@
1112
// CHECK-METHOD: CXXMethodDecl {{.+}} methodToAnnotate
1213
// CHECK-METHOD-NEXT: ParmVarDecl {{.+}} p
1314
// CHECK-METHOD-NEXT: LifetimeBoundAttr
15+
16+
// CHECK-METHOD-THIS: CXXMethodDecl {{.+}} annotateThis 'int *() {{\[\[}}clang::lifetimebound{{\]\]}}'

0 commit comments

Comments
 (0)