Skip to content

Commit e5e04cd

Browse files
committed
Revert "Revert "[APINotes] Add support for bounds safety annotations""
This reverts commit 7cbd1e7.
1 parent 2bef1d1 commit e5e04cd

20 files changed

+1081
-102
lines changed

clang/include/clang/APINotes/Types.h

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,72 @@ inline bool operator!=(const ContextInfo &LHS, const ContextInfo &RHS) {
300300
return !(LHS == RHS);
301301
}
302302

303+
/* TO_UPSTREAM(BoundsSafety) ON */
304+
class BoundsSafetyInfo {
305+
public:
306+
enum class BoundsSafetyKind {
307+
CountedBy = 0,
308+
CountedByOrNull,
309+
SizedBy,
310+
SizedByOrNull,
311+
EndedBy,
312+
};
313+
314+
private:
315+
/// Whether this property has been audited for nullability.
316+
LLVM_PREFERRED_TYPE(bool)
317+
unsigned KindAudited : 1;
318+
319+
/// The kind of nullability for this property. Only valid if the nullability
320+
/// has been audited.
321+
LLVM_PREFERRED_TYPE(BoundsSafetyKind)
322+
unsigned Kind : 3;
323+
324+
LLVM_PREFERRED_TYPE(bool)
325+
unsigned LevelAudited : 1;
326+
327+
unsigned Level : 3;
328+
329+
public:
330+
std::string ExternalBounds;
331+
332+
BoundsSafetyInfo()
333+
: KindAudited(false), Kind(0), LevelAudited(false), Level(0),
334+
ExternalBounds("") {}
335+
336+
std::optional<BoundsSafetyKind> getKind() const {
337+
return KindAudited ? std::optional<BoundsSafetyKind>(
338+
static_cast<BoundsSafetyKind>(Kind))
339+
: std::nullopt;
340+
}
341+
342+
void setKindAudited(BoundsSafetyKind kind) {
343+
KindAudited = true;
344+
Kind = static_cast<unsigned>(kind);
345+
}
346+
347+
std::optional<unsigned> getLevel() const {
348+
return LevelAudited ? std::optional<unsigned>(Level) : std::nullopt;
349+
}
350+
351+
void setLevelAudited(unsigned level) {
352+
LevelAudited = true;
353+
Level = level;
354+
}
355+
356+
friend bool operator==(const BoundsSafetyInfo &, const BoundsSafetyInfo &);
357+
358+
LLVM_DUMP_METHOD void dump(llvm::raw_ostream &OS) const;
359+
};
360+
361+
inline bool operator==(const BoundsSafetyInfo &LHS,
362+
const BoundsSafetyInfo &RHS) {
363+
return LHS.KindAudited == RHS.KindAudited && LHS.Kind == RHS.Kind &&
364+
LHS.LevelAudited == RHS.LevelAudited && LHS.Level == RHS.Level &&
365+
LHS.ExternalBounds == RHS.ExternalBounds;
366+
}
367+
/* TO_UPSTREAM(BoundsSafety) OFF */
368+
303369
/// API notes for a variable/property.
304370
class VariableInfo : public CommonEntityInfo {
305371
/// Whether this property has been audited for nullability.
@@ -439,10 +505,14 @@ class ParamInfo : public VariableInfo {
439505
unsigned RawRetainCountConvention : 3;
440506

441507
public:
508+
/* TO_UPSTREAM(BoundsSafety) ON */
509+
std::optional<BoundsSafetyInfo> BoundsSafety;
510+
/* TO_UPSTREAM(BoundsSafety) OFF */
511+
442512
ParamInfo()
443513
: NoEscapeSpecified(false), NoEscape(false),
444514
LifetimeboundSpecified(false), Lifetimebound(false),
445-
RawRetainCountConvention() {}
515+
RawRetainCountConvention(), BoundsSafety(std::nullopt) {}
446516

447517
std::optional<bool> isNoEscape() const {
448518
return NoEscapeSpecified ? std::optional<bool>(NoEscape) : std::nullopt;
@@ -488,6 +558,11 @@ class ParamInfo : public VariableInfo {
488558
if (!RawRetainCountConvention)
489559
RawRetainCountConvention = RHS.RawRetainCountConvention;
490560

561+
/* TO_UPSTREAM(BoundsSafety) ON */
562+
if (!BoundsSafety)
563+
BoundsSafety = RHS.BoundsSafety;
564+
/* TO_UPSTREAM(BoundsSafety) OFF */
565+
491566
return *this;
492567
}
493568

@@ -502,7 +577,10 @@ inline bool operator==(const ParamInfo &LHS, const ParamInfo &RHS) {
502577
LHS.NoEscape == RHS.NoEscape &&
503578
LHS.LifetimeboundSpecified == RHS.LifetimeboundSpecified &&
504579
LHS.Lifetimebound == RHS.Lifetimebound &&
505-
LHS.RawRetainCountConvention == RHS.RawRetainCountConvention;
580+
LHS.RawRetainCountConvention == RHS.RawRetainCountConvention &&
581+
/* TO_UPSTREAM(BoundsSafety) ON */
582+
LHS.BoundsSafety == RHS.BoundsSafety;
583+
/* TO_UPSTREAM(BoundsSafety) OFF */
506584
}
507585

508586
inline bool operator!=(const ParamInfo &LHS, const ParamInfo &RHS) {

clang/include/clang/Parse/Parser.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2021,7 +2021,7 @@ class Parser : public CodeCompletionHandler {
20212021
/// Parse a __builtin_bit_cast(T, E), used to implement C++2a std::bit_cast.
20222022
ExprResult ParseBuiltinBitCast();
20232023

2024-
/* TO_UPSTREAM(BoundsSafety) ON*/
2024+
/* TO_UPSTREAM(BoundsSafety) ON */
20252025
//===--------------------------------------------------------------------===//
20262026
/// BoundsSafety: __builtin_unsafe_forge_bidi_indexable(expr, size)
20272027
ExprResult ParseUnsafeForgeBidiIndexable();
@@ -3924,6 +3924,25 @@ class Parser : public CodeCompletionHandler {
39243924
/// \param IncludeLoc The location at which this parse was triggered.
39253925
TypeResult ParseTypeFromString(StringRef TypeStr, StringRef Context,
39263926
SourceLocation IncludeLoc);
3927+
/* TO_UPSTREAM(BoundsSafety) ON */
3928+
/// Parse the given string as an expression in the argument position for a
3929+
/// bounds safety attribute.
3930+
///
3931+
/// This is a dangerous utility function currently employed only by API notes.
3932+
/// It is not a general entry-point for safely parsing expressions from
3933+
/// strings.
3934+
///
3935+
/// \param ExprStr The string to be parsed as an expression.
3936+
/// \param Context The name of the context in which this string is being
3937+
/// parsed, which will be used in diagnostics.
3938+
/// \param ParentDecl If a function or method is provided, the parameters are
3939+
/// added to the current parsing context.
3940+
/// \param IncludeLoc The location at which this parse was triggered.
3941+
ExprResult ParseBoundsAttributeArgFromString(StringRef ExprStr,
3942+
StringRef Context,
3943+
Decl *ParentDecl,
3944+
SourceLocation IncludeLoc);
3945+
/* TO_UPSTREAM(BoundsSafety) OFF */
39273946

39283947
//===--------------------------------------------------------------------===//
39293948
// Modules

clang/include/clang/Sema/Sema.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1131,6 +1131,12 @@ class Sema final : public SemaBase {
11311131
std::function<TypeResult(StringRef, StringRef, SourceLocation)>
11321132
ParseTypeFromStringCallback;
11331133

1134+
/* TO_UPSTREAM(BoundsSafety) ON */
1135+
/// Callback to the parser to parse a type expressed as a string.
1136+
std::function<ExprResult(StringRef, StringRef, Decl *, SourceLocation)>
1137+
ParseBoundsAttributeArgFromStringCallback;
1138+
/* TO_UPSTREAM(BoundsSafety) OFF */
1139+
11341140
/// VAListTagName - The declaration name corresponding to __va_list_tag.
11351141
/// This is used as part of a hack to omit that class from ADL results.
11361142
DeclarationName VAListTagName;
@@ -15498,7 +15504,13 @@ class Sema final : public SemaBase {
1549815504
llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls, bool CountInBytes,
1549915505
bool OrNull);
1550015506

15501-
/* TO_UPSTREAM(BoundsSafety) ON*/
15507+
/* TO_UPSTREAM(BoundsSafety) ON */
15508+
void applyPtrCountedByEndedByAttr(Decl *D, unsigned Level,
15509+
AttributeCommonInfo::Kind Kind,
15510+
Expr *AttrArg, SourceLocation Loc,
15511+
SourceRange Range, StringRef DiagName,
15512+
bool OriginatesInAPINotes = false);
15513+
1550215514
/// Perform Bounds Safety Semantic checks for assigning to a `__counted_by` or
1550315515
/// `__counted_by_or_null` pointer type \param LHSTy.
1550415516
///

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 = 34; // SwiftReturnOwnership
27+
const uint16_t VERSION_MINOR = 35; // BoundsSafety
2828

2929
const uint8_t kSwiftConforms = 1;
3030
const uint8_t kSwiftDoesNotConform = 2;

clang/lib/APINotes/APINotesReader.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,31 @@ class FieldTableInfo
322322
}
323323
};
324324

325+
/* TO_UPSTREAM(BoundsSafety) ON */
326+
/// Read serialized BoundsSafetyInfo.
327+
void ReadBoundsSafetyInfo(const uint8_t *&Data, BoundsSafetyInfo &Info) {
328+
uint8_t Payload = endian::readNext<uint8_t, llvm::endianness::little>(Data);
329+
330+
if (Payload & 0x01) {
331+
uint8_t Level = (Payload >> 1) & 0x7;
332+
Info.setLevelAudited(Level);
333+
}
334+
Payload >>= 4;
335+
336+
if (Payload & 0x01) {
337+
uint8_t Kind = (Payload >> 1) & 0x7;
338+
assert(Kind >= (uint8_t)BoundsSafetyInfo::BoundsSafetyKind::CountedBy);
339+
assert(Kind <= (uint8_t)BoundsSafetyInfo::BoundsSafetyKind::EndedBy);
340+
Info.setKindAudited((BoundsSafetyInfo::BoundsSafetyKind)Kind);
341+
}
342+
343+
uint16_t ExternalBoundsLen =
344+
endian::readNext<uint16_t, llvm::endianness::little>(Data);
345+
Info.ExternalBounds = std::string(Data, Data + ExternalBoundsLen);
346+
Data += ExternalBoundsLen;
347+
}
348+
/* TO_UPSTREAM(BoundsSafety) OFF */
349+
325350
/// Read serialized ParamInfo.
326351
void ReadParamInfo(const uint8_t *&Data, ParamInfo &Info) {
327352
ReadVariableInfo(Data, Info);
@@ -338,7 +363,13 @@ void ReadParamInfo(const uint8_t *&Data, ParamInfo &Info) {
338363
if (Payload & 0x01)
339364
Info.setNoEscape(Payload & 0x02);
340365
Payload >>= 2;
341-
assert(Payload == 0 && "Bad API notes");
366+
/* TO_UPSTREAM(BoundsSafety) ON */
367+
if (Payload & 0x01) {
368+
BoundsSafetyInfo BSI;
369+
ReadBoundsSafetyInfo(Data, BSI);
370+
Info.BoundsSafety = BSI;
371+
}
372+
/* TO_UPSTREAM(BoundsSafety) OFF */
342373
}
343374

344375
/// Read serialized FunctionInfo.

clang/lib/APINotes/APINotesTypes.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,34 @@ LLVM_DUMP_METHOD void ObjCPropertyInfo::dump(llvm::raw_ostream &OS) const {
6161
OS << '\n';
6262
}
6363

64+
LLVM_DUMP_METHOD void BoundsSafetyInfo::dump(llvm::raw_ostream &OS) const {
65+
if (KindAudited) {
66+
assert((BoundsSafetyKind)Kind >= BoundsSafetyKind::CountedBy);
67+
assert((BoundsSafetyKind)Kind <= BoundsSafetyKind::EndedBy);
68+
switch ((BoundsSafetyKind)Kind) {
69+
case BoundsSafetyKind::CountedBy:
70+
OS << "[counted_by] ";
71+
break;
72+
case BoundsSafetyKind::CountedByOrNull:
73+
OS << "[counted_by_or_null] ";
74+
break;
75+
case BoundsSafetyKind::SizedBy:
76+
OS << "[sized_by] ";
77+
break;
78+
case BoundsSafetyKind::SizedByOrNull:
79+
OS << "[sized_by_or_null] ";
80+
break;
81+
case BoundsSafetyKind::EndedBy:
82+
OS << "[ended_by] ";
83+
break;
84+
}
85+
}
86+
if (LevelAudited)
87+
OS << "Level: " << Level << " ";
88+
OS << "ExternalBounds: "
89+
<< (ExternalBounds.empty() ? "<missing>" : ExternalBounds) << '\n';
90+
}
91+
6492
LLVM_DUMP_METHOD void ParamInfo::dump(llvm::raw_ostream &OS) const {
6593
static_cast<const VariableInfo &>(*this).dump(OS);
6694
if (NoEscapeSpecified)
@@ -69,6 +97,8 @@ LLVM_DUMP_METHOD void ParamInfo::dump(llvm::raw_ostream &OS) const {
6997
OS << (Lifetimebound ? "[Lifetimebound] " : "");
7098
OS << "RawRetainCountConvention: " << RawRetainCountConvention << ' ';
7199
OS << '\n';
100+
if (BoundsSafety)
101+
BoundsSafety->dump(OS);
72102
}
73103

74104
LLVM_DUMP_METHOD void FunctionInfo::dump(llvm::raw_ostream &OS) const {

clang/lib/APINotes/APINotesWriter.cpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1074,15 +1074,48 @@ void APINotesWriter::Implementation::writeGlobalVariableBlock(
10741074
}
10751075
}
10761076

1077+
/* TO_UPSTREAM(BoundsSafety) ON */
10771078
namespace {
1079+
void emitBoundsSafetyInfo(raw_ostream &OS, const BoundsSafetyInfo &BSI) {
1080+
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
1081+
uint8_t flags = 0;
1082+
if (auto kind = BSI.getKind()) {
1083+
flags |= 0x01; // 1 bit
1084+
flags |= (uint8_t)*kind << 1; // 3 bits
1085+
}
1086+
flags <<= 4;
1087+
if (auto level = BSI.getLevel()) {
1088+
flags |= 0x01; // 1 bit
1089+
flags |= *level << 1; // 3 bits
1090+
}
1091+
1092+
writer.write<uint8_t>(flags);
1093+
writer.write<uint16_t>(BSI.ExternalBounds.size());
1094+
writer.write(
1095+
ArrayRef<char>{BSI.ExternalBounds.data(), BSI.ExternalBounds.size()});
1096+
}
1097+
1098+
unsigned getBoundsSafetyInfoSize(const BoundsSafetyInfo &BSI) {
1099+
return 1 + sizeof(uint16_t) + BSI.ExternalBounds.size();
1100+
}
1101+
/* TO_UPSTREAM(BoundsSafety) OFF */
1102+
10781103
unsigned getParamInfoSize(const ParamInfo &PI) {
1079-
return getVariableInfoSize(PI) + 1;
1104+
unsigned BSISize = 0;
1105+
/* TO_UPSTREAM(BoundsSafety) ON */
1106+
if (auto BSI = PI.BoundsSafety)
1107+
BSISize = getBoundsSafetyInfoSize(*BSI);
1108+
/* TO_UPSTREAM(BoundsSafety) OFF */
1109+
return getVariableInfoSize(PI) + 1 + BSISize;
10801110
}
10811111

10821112
void emitParamInfo(raw_ostream &OS, const ParamInfo &PI) {
10831113
emitVariableInfo(OS, PI);
10841114

10851115
uint8_t flags = 0;
1116+
if (PI.BoundsSafety)
1117+
flags |= 0x01;
1118+
flags <<= 2;
10861119
if (auto noescape = PI.isNoEscape()) {
10871120
flags |= 0x01;
10881121
if (*noescape)
@@ -1100,6 +1133,10 @@ void emitParamInfo(raw_ostream &OS, const ParamInfo &PI) {
11001133

11011134
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
11021135
writer.write<uint8_t>(flags);
1136+
/* TO_UPSTREAM(BoundsSafety) ON */
1137+
if (auto BSI = PI.BoundsSafety)
1138+
emitBoundsSafetyInfo(OS, *PI.BoundsSafety);
1139+
/* TO_UPSTREAM(BoundsSafety) OFF */
11031140
}
11041141

11051142
/// Retrieve the serialized size of the given FunctionInfo, for use in on-disk

0 commit comments

Comments
 (0)