Skip to content

Commit 2449073

Browse files
authored
Merge pull request #9594 from swiftlang/gaborh/apinotes-lifetimebound-this
[clang] Support 'this' position for lifetimebound attribute
2 parents 298d468 + 6116646 commit 2449073

File tree

12 files changed

+119
-22
lines changed

12 files changed

+119
-22
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 &);
@@ -667,7 +666,7 @@ class ObjCMethodInfo : public FunctionInfo {
667666
inline bool operator==(const ObjCMethodInfo &LHS, const ObjCMethodInfo &RHS) {
668667
return static_cast<const FunctionInfo &>(LHS) == RHS &&
669668
LHS.DesignatedInit == RHS.DesignatedInit &&
670-
LHS.RequiredInit == RHS.RequiredInit;
669+
LHS.RequiredInit == RHS.RequiredInit && LHS.Self == RHS.Self;
671670
}
672671

673672
inline bool operator!=(const ObjCMethodInfo &LHS, const ObjCMethodInfo &RHS) {
@@ -696,8 +695,20 @@ class FieldInfo : public VariableInfo {
696695
class CXXMethodInfo : public FunctionInfo {
697696
public:
698697
CXXMethodInfo() {}
698+
699+
std::optional<ParamInfo> This;
700+
701+
LLVM_DUMP_METHOD void dump(llvm::raw_ostream &OS);
699702
};
700703

704+
inline bool operator==(const CXXMethodInfo &LHS, const CXXMethodInfo &RHS) {
705+
return static_cast<const FunctionInfo &>(LHS) == RHS && LHS.This == RHS.This;
706+
}
707+
708+
inline bool operator!=(const CXXMethodInfo &LHS, const CXXMethodInfo &RHS) {
709+
return !(LHS == RHS);
710+
}
711+
701712
/// Describes API notes data for an enumerator.
702713
class EnumConstantInfo : public CommonEntityInfo {
703714
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
@@ -666,6 +666,7 @@ namespace {
666666
unsigned getVariableInfoSize(const VariableInfo &VI) {
667667
return 2 + getCommonEntityInfoSize(VI) + 2 + VI.getType().size();
668668
}
669+
unsigned getParamInfoSize(const ParamInfo &PI);
669670

670671
/// Emit a serialized representation of the variable information.
671672
void emitVariableInfo(raw_ostream &OS, const VariableInfo &VI) {
@@ -754,6 +755,7 @@ void APINotesWriter::Implementation::writeObjCPropertyBlock(
754755
namespace {
755756
unsigned getFunctionInfoSize(const FunctionInfo &);
756757
void emitFunctionInfo(llvm::raw_ostream &, const FunctionInfo &);
758+
void emitParamInfo(raw_ostream &OS, const ParamInfo &PI);
757759

758760
/// Used to serialize the on-disk Objective-C method table.
759761
class ObjCMethodTableInfo
@@ -777,17 +779,24 @@ class ObjCMethodTableInfo
777779
}
778780

779781
unsigned getUnversionedInfoSize(const ObjCMethodInfo &OMI) {
780-
return getFunctionInfoSize(OMI) + 1;
782+
auto size = getFunctionInfoSize(OMI) + 1;
783+
if (OMI.Self)
784+
size += getParamInfoSize(*OMI.Self);
785+
return size;
781786
}
782787

783788
void emitUnversionedInfo(raw_ostream &OS, const ObjCMethodInfo &OMI) {
784789
uint8_t flags = 0;
785790
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
786791
flags = (flags << 1) | OMI.DesignatedInit;
787792
flags = (flags << 1) | OMI.RequiredInit;
793+
flags = (flags << 1) | static_cast<bool>(OMI.Self);
788794
writer.write<uint8_t>(flags);
789795

790796
emitFunctionInfo(OS, OMI);
797+
798+
if (OMI.Self)
799+
emitParamInfo(OS, *OMI.Self);
791800
}
792801
};
793802

@@ -810,12 +819,22 @@ class CXXMethodTableInfo
810819
return static_cast<size_t>(key.hashValue());
811820
}
812821

813-
unsigned getUnversionedInfoSize(const CXXMethodInfo &OMI) {
814-
return getFunctionInfoSize(OMI);
822+
unsigned getUnversionedInfoSize(const CXXMethodInfo &MI) {
823+
auto size = getFunctionInfoSize(MI) + 1;
824+
if (MI.This)
825+
size += getParamInfoSize(*MI.This);
826+
return size;
815827
}
816828

817-
void emitUnversionedInfo(raw_ostream &OS, const CXXMethodInfo &OMI) {
818-
emitFunctionInfo(OS, OMI);
829+
void emitUnversionedInfo(raw_ostream &OS, const CXXMethodInfo &MI) {
830+
uint8_t flags = 0;
831+
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
832+
flags = (flags << 1) | static_cast<bool>(MI.This);
833+
writer.write<uint8_t>(flags);
834+
835+
emitFunctionInfo(OS, MI);
836+
if (MI.This)
837+
emitParamInfo(OS, *MI.This);
819838
}
820839
};
821840
} // 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;
@@ -181,7 +182,7 @@ template <> struct ScalarEnumerationTraits<MethodKind> {
181182

182183
namespace {
183184
struct Param {
184-
unsigned Position;
185+
int Position;
185186
std::optional<bool> NoEscape = false;
186187
std::optional<bool> Lifetimebound = false;
187188
std::optional<NullabilityKind> Nullability;
@@ -843,7 +844,8 @@ class YAMLConverter {
843844
}
844845
}
845846

846-
void convertParams(const ParamsSeq &Params, FunctionInfo &OutInfo) {
847+
void convertParams(const ParamsSeq &Params, FunctionInfo &OutInfo,
848+
std::optional<ParamInfo> &thisOrSelf) {
847849
for (const auto &P : Params) {
848850
ParamInfo PI;
849851
if (P.Nullability)
@@ -852,9 +854,14 @@ class YAMLConverter {
852854
PI.setLifetimebound(P.Lifetimebound);
853855
PI.setType(std::string(P.Type));
854856
PI.setRetainCountConvention(P.RetainCountConvention);
855-
if (OutInfo.Params.size() <= P.Position)
857+
if (static_cast<int>(OutInfo.Params.size()) <= P.Position)
856858
OutInfo.Params.resize(P.Position + 1);
857-
OutInfo.Params[P.Position] |= PI;
859+
if (P.Position == -1)
860+
thisOrSelf = PI;
861+
else if (P.Position >= 0)
862+
OutInfo.Params[P.Position] |= PI;
863+
else
864+
emitError("invalid parameter position " + llvm::itostr(P.Position));
858865
}
859866
}
860867

@@ -931,7 +938,7 @@ class YAMLConverter {
931938
MI.ResultType = std::string(M.ResultType);
932939

933940
// Translate parameter information.
934-
convertParams(M.Params, MI);
941+
convertParams(M.Params, MI, MI.Self);
935942

936943
// Translate nullability info.
937944
convertNullability(M.Nullability, M.NullabilityOfRet, MI, M.Selector);
@@ -1039,11 +1046,18 @@ class YAMLConverter {
10391046
TheNamespace.Items, SwiftVersion);
10401047
}
10411048

1042-
void convertFunction(const Function &Function, FunctionInfo &FI) {
1049+
template <typename FuncOrMethodInfo>
1050+
void convertFunction(const Function &Function, FuncOrMethodInfo &FI) {
10431051
convertAvailability(Function.Availability, FI, Function.Name);
10441052
FI.setSwiftPrivate(Function.SwiftPrivate);
10451053
FI.SwiftName = std::string(Function.SwiftName);
1046-
convertParams(Function.Params, FI);
1054+
std::optional<ParamInfo> This;
1055+
convertParams(Function.Params, FI, This);
1056+
if constexpr (std::is_same_v<FuncOrMethodInfo, CXXMethodInfo>)
1057+
FI.This = This;
1058+
else if (This)
1059+
emitError("implicit instance parameter is only permitted on C++ and "
1060+
"Objective-C methods");
10471061
convertNullability(Function.Nullability, Function.NullabilityOfRet, FI,
10481062
Function.Name);
10491063
FI.ResultType = std::string(Function.ResultType);

clang/lib/Sema/CheckExprLifetime.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ static void handleGslAnnotatedTypes(IndirectLocalPath &Path, Expr *Call,
386386
}
387387
}
388388

389-
static bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) {
389+
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) {
390390
const TypeSourceInfo *TSI = FD->getTypeSourceInfo();
391391
if (!TSI)
392392
return false;

clang/lib/Sema/CheckExprLifetime.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ void checkExprLifetime(Sema &SemaRef, const InitializedEntity &Entity,
3434
/// sufficient for assigning to the entity.
3535
void checkExprLifetime(Sema &SemaRef, const AssignedEntity &Entity, Expr *Init);
3636

37+
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD);
38+
3739
} // namespace clang::sema
3840

3941
#endif // LLVM_CLANG_SEMA_CHECK_EXPR_LIFETIME_H

clang/lib/Sema/SemaAPINotes.cpp

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

13+
#include "CheckExprLifetime.h"
14+
#include "TypeLocBuilder.h"
1315
#include "clang/APINotes/APINotesReader.h"
1416
#include "clang/AST/Decl.h"
1517
#include "clang/AST/DeclCXX.h"
1618
#include "clang/AST/DeclObjC.h"
19+
#include "clang/AST/TypeLoc.h"
1720
#include "clang/Basic/SourceLocation.h"
1821
#include "clang/Lex/Lexer.h"
1922
#include "clang/Sema/SemaInternal.h"
@@ -567,6 +570,21 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc,
567570
static void ProcessAPINotes(Sema &S, CXXMethodDecl *Method,
568571
const api_notes::CXXMethodInfo &Info,
569572
VersionedInfoMetadata Metadata) {
573+
if (Info.This && Info.This->isLifetimebound() &&
574+
!sema::implicitObjectParamIsLifetimeBound(Method)) {
575+
auto MethodType = Method->getType();
576+
auto *attr = ::new (S.Context)
577+
LifetimeBoundAttr(S.Context, getPlaceholderAttrInfo());
578+
QualType AttributedType =
579+
S.Context.getAttributedType(attr, MethodType, MethodType);
580+
TypeLocBuilder TLB;
581+
TLB.pushFullCopy(Method->getTypeSourceInfo()->getTypeLoc());
582+
AttributedTypeLoc TyLoc = TLB.push<AttributedTypeLoc>(AttributedType);
583+
TyLoc.setAttr(attr);
584+
Method->setType(AttributedType);
585+
Method->setTypeSourceInfo(TLB.getTypeSourceInfo(S.Context, AttributedType));
586+
}
587+
570588
ProcessAPINotes(S, (FunctionOrMethod)Method, Info, Metadata);
571589
}
572590

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

0 commit comments

Comments
 (0)