Skip to content

Commit 848d9d4

Browse files
committed
[APINotes] Add support for bounds safety annotations
This adds support for annotating function parameters with __counted_by, __sized_by, __counted_by_or_null, __sized_by_or_null, and __ended_by, using API notes. The main content of handlePtrCountedByEndedByAttr is extracted to applyPtrCountedByEndedByAttr and decoupled from ParsedAttr. The helper function ParseBoundsAttributeArgFromString is added to make it possible to parse count expressions from SemaAPINotes. The current implementation of __terminated_by/__null_terminated makes it harder to extract from the iterative type processing, but since it doesn't require any extra context to parse the attribute, it can be applied using the normal Type override instead. rdar://139830881
1 parent 9f6c3d7 commit 848d9d4

18 files changed

+698
-91
lines changed

clang/include/clang/APINotes/Types.h

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

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

441503
public:
504+
std::optional<BoundsSafetyInfo> BoundsSafety;
505+
442506
ParamInfo()
443507
: NoEscapeSpecified(false), NoEscape(false),
444508
LifetimeboundSpecified(false), Lifetimebound(false),
445-
RawRetainCountConvention() {}
509+
RawRetainCountConvention(), BoundsSafety(std::nullopt) {}
446510

447511
std::optional<bool> isNoEscape() const {
448512
return NoEscapeSpecified ? std::optional<bool>(NoEscape) : std::nullopt;
@@ -488,6 +552,9 @@ class ParamInfo : public VariableInfo {
488552
if (!RawRetainCountConvention)
489553
RawRetainCountConvention = RHS.RawRetainCountConvention;
490554

555+
if (!BoundsSafety)
556+
BoundsSafety = RHS.BoundsSafety;
557+
491558
return *this;
492559
}
493560

@@ -502,7 +569,8 @@ inline bool operator==(const ParamInfo &LHS, const ParamInfo &RHS) {
502569
LHS.NoEscape == RHS.NoEscape &&
503570
LHS.LifetimeboundSpecified == RHS.LifetimeboundSpecified &&
504571
LHS.Lifetimebound == RHS.Lifetimebound &&
505-
LHS.RawRetainCountConvention == RHS.RawRetainCountConvention;
572+
LHS.RawRetainCountConvention == RHS.RawRetainCountConvention &&
573+
LHS.BoundsSafety == RHS.BoundsSafety;
506574
}
507575

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

clang/include/clang/Parse/Parser.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3924,6 +3924,10 @@ 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+
ExprResult ParseBoundsAttributeArgFromString(StringRef ExprStr,
3928+
StringRef Context,
3929+
Decl *ParentDecl,
3930+
SourceLocation IncludeLoc);
39273931

39283932
//===--------------------------------------------------------------------===//
39293933
// Modules

clang/include/clang/Sema/Sema.h

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

1134+
/// Callback to the parser to parse a type expressed as a string.
1135+
std::function<ExprResult(StringRef, StringRef, Decl *, SourceLocation)>
1136+
ParseBoundsAttributeArgFromStringCallback;
1137+
11341138
/// VAListTagName - The declaration name corresponding to __va_list_tag.
11351139
/// This is used as part of a hack to omit that class from ADL results.
11361140
DeclarationName VAListTagName;
@@ -15504,6 +15508,11 @@ class Sema final : public SemaBase {
1550415508
llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls, bool CountInBytes,
1550515509
bool OrNull);
1550615510

15511+
void applyPtrCountedByEndedByAttr(Decl *D, unsigned Level,
15512+
AttributeCommonInfo::Kind Kind,
15513+
Expr *AttrArg, SourceLocation Loc,
15514+
SourceRange Range, StringRef DiagName);
15515+
1550715516
/* TO_UPSTREAM(BoundsSafety) ON*/
1550815517
/// Perform Bounds Safety Semantic checks for assigning to a `__counted_by` or
1550915518
/// `__counted_by_or_null` pointer type \param LHSTy.

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: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,44 @@ class FieldTableInfo
322322
}
323323
};
324324

325+
BoundsSafetyInfo::BoundsSafetyKind readKindFlag(uint8_t kind) {
326+
switch (kind) {
327+
case 0x00:
328+
return BoundsSafetyInfo::BoundsSafetyKind::CountedBy;
329+
case 0x01:
330+
return BoundsSafetyInfo::BoundsSafetyKind::CountedByOrNull;
331+
case 0x02:
332+
return BoundsSafetyInfo::BoundsSafetyKind::SizedBy;
333+
case 0x03:
334+
return BoundsSafetyInfo::BoundsSafetyKind::SizedByOrNull;
335+
case 0x04:
336+
return BoundsSafetyInfo::BoundsSafetyKind::EndedBy;
337+
default:
338+
llvm_unreachable("invalid bounds safety kind");
339+
}
340+
}
341+
342+
/// Read serialized BoundsSafetyInfo.
343+
void ReadBoundsSafetyInfo(const uint8_t *&Data, BoundsSafetyInfo &Info) {
344+
uint8_t Payload = endian::readNext<uint8_t, llvm::endianness::little>(Data);
345+
346+
if (Payload & 0x01) {
347+
uint8_t Level = (Payload >> 1) & 0x7;
348+
Info.setLevelAudited(Level);
349+
}
350+
Payload >>= 4;
351+
352+
if (Payload & 0x01) {
353+
uint8_t Kind = (Payload >> 1) & 0x7;
354+
Info.setKindAudited(readKindFlag(Kind));
355+
}
356+
357+
uint16_t ExternalBoundsLen =
358+
endian::readNext<uint16_t, llvm::endianness::little>(Data);
359+
Info.ExternalBounds = std::string(Data, Data + ExternalBoundsLen);
360+
Data += ExternalBoundsLen;
361+
}
362+
325363
/// Read serialized ParamInfo.
326364
void ReadParamInfo(const uint8_t *&Data, ParamInfo &Info) {
327365
ReadVariableInfo(Data, Info);
@@ -338,7 +376,11 @@ void ReadParamInfo(const uint8_t *&Data, ParamInfo &Info) {
338376
if (Payload & 0x01)
339377
Info.setNoEscape(Payload & 0x02);
340378
Payload >>= 2;
341-
assert(Payload == 0 && "Bad API notes");
379+
if (Payload & 0x01) {
380+
BoundsSafetyInfo BSI;
381+
ReadBoundsSafetyInfo(Data, BSI);
382+
Info.BoundsSafety = BSI;
383+
}
342384
}
343385

344386
/// Read serialized FunctionInfo.

clang/lib/APINotes/APINotesWriter.cpp

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1075,14 +1075,58 @@ void APINotesWriter::Implementation::writeGlobalVariableBlock(
10751075
}
10761076

10771077
namespace {
1078+
uint8_t getKindFlag(BoundsSafetyInfo::BoundsSafetyKind kind) {
1079+
switch (kind) {
1080+
case BoundsSafetyInfo::BoundsSafetyKind::CountedBy:
1081+
return 0x00;
1082+
case BoundsSafetyInfo::BoundsSafetyKind::CountedByOrNull:
1083+
return 0x01;
1084+
case BoundsSafetyInfo::BoundsSafetyKind::SizedBy:
1085+
return 0x02;
1086+
case BoundsSafetyInfo::BoundsSafetyKind::SizedByOrNull:
1087+
return 0x03;
1088+
case BoundsSafetyInfo::BoundsSafetyKind::EndedBy:
1089+
return 0x04;
1090+
}
1091+
}
1092+
1093+
void emitBoundsSafetyInfo(raw_ostream &OS, const BoundsSafetyInfo &BSI) {
1094+
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
1095+
uint8_t flags = 0;
1096+
if (auto kind = BSI.getKind()) {
1097+
flags |= 0x01; // 1 bit
1098+
flags |= getKindFlag(*kind) << 1; // 3 bits
1099+
}
1100+
flags <<= 4;
1101+
if (auto level = BSI.getLevel()) {
1102+
flags |= 0x01; // 1 bit
1103+
flags |= *level << 1; // 3 bits
1104+
}
1105+
1106+
writer.write<uint8_t>(flags);
1107+
writer.write<uint16_t>(BSI.ExternalBounds.size());
1108+
writer.write(
1109+
ArrayRef<char>{BSI.ExternalBounds.data(), BSI.ExternalBounds.size()});
1110+
}
1111+
1112+
unsigned getBoundsSafetyInfoSize(const BoundsSafetyInfo &BSI) {
1113+
return 1 + sizeof(uint16_t) + BSI.ExternalBounds.size();
1114+
}
1115+
10781116
unsigned getParamInfoSize(const ParamInfo &PI) {
1079-
return getVariableInfoSize(PI) + 1;
1117+
unsigned BSISize = 0;
1118+
if (auto BSI = PI.BoundsSafety)
1119+
BSISize = getBoundsSafetyInfoSize(*BSI);
1120+
return getVariableInfoSize(PI) + 1 + BSISize;
10801121
}
10811122

10821123
void emitParamInfo(raw_ostream &OS, const ParamInfo &PI) {
10831124
emitVariableInfo(OS, PI);
10841125

10851126
uint8_t flags = 0;
1127+
if (PI.BoundsSafety)
1128+
flags |= 0x01;
1129+
flags <<= 2;
10861130
if (auto noescape = PI.isNoEscape()) {
10871131
flags |= 0x01;
10881132
if (*noescape)
@@ -1100,6 +1144,8 @@ void emitParamInfo(raw_ostream &OS, const ParamInfo &PI) {
11001144

11011145
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
11021146
writer.write<uint8_t>(flags);
1147+
if (auto BSI = PI.BoundsSafety)
1148+
emitBoundsSafetyInfo(OS, *PI.BoundsSafety);
11031149
}
11041150

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

clang/lib/APINotes/APINotesYAMLCompiler.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,31 @@ enum class APIAvailability {
150150
};
151151
} // namespace
152152

153+
namespace {
154+
struct BoundsSafety {
155+
BoundsSafetyInfo::BoundsSafetyKind Kind;
156+
unsigned Level = 0;
157+
StringRef BoundsExpr = "";
158+
};
159+
} // namespace
160+
161+
namespace llvm {
162+
namespace yaml {
163+
template <> struct ScalarEnumerationTraits<BoundsSafetyInfo::BoundsSafetyKind> {
164+
static void enumeration(IO &IO, BoundsSafetyInfo::BoundsSafetyKind &AA) {
165+
IO.enumCase(AA, "counted_by",
166+
BoundsSafetyInfo::BoundsSafetyKind::CountedBy);
167+
IO.enumCase(AA, "counted_by_or_null",
168+
BoundsSafetyInfo::BoundsSafetyKind::CountedByOrNull);
169+
IO.enumCase(AA, "sized_by", BoundsSafetyInfo::BoundsSafetyKind::SizedBy);
170+
IO.enumCase(AA, "sized_by_or_null",
171+
BoundsSafetyInfo::BoundsSafetyKind::SizedByOrNull);
172+
IO.enumCase(AA, "ended_by", BoundsSafetyInfo::BoundsSafetyKind::EndedBy);
173+
}
174+
};
175+
} // namespace yaml
176+
} // namespace llvm
177+
153178
namespace llvm {
154179
namespace yaml {
155180
template <> struct ScalarEnumerationTraits<APIAvailability> {
@@ -188,6 +213,7 @@ struct Param {
188213
std::optional<NullabilityKind> Nullability;
189214
std::optional<RetainCountConventionKind> RetainCountConvention;
190215
StringRef Type;
216+
BoundsSafety BoundsSafety;
191217
};
192218

193219
typedef std::vector<Param> ParamsSeq;
@@ -238,6 +264,15 @@ template <> struct MappingTraits<Param> {
238264
IO.mapOptional("NoEscape", P.NoEscape);
239265
IO.mapOptional("Lifetimebound", P.Lifetimebound);
240266
IO.mapOptional("Type", P.Type, StringRef(""));
267+
IO.mapOptional("BoundsSafety", P.BoundsSafety);
268+
}
269+
};
270+
271+
template <> struct MappingTraits<BoundsSafety> {
272+
static void mapping(IO &IO, BoundsSafety &BS) {
273+
IO.mapRequired("Kind", BS.Kind);
274+
IO.mapRequired("BoundedBy", BS.BoundsExpr);
275+
IO.mapOptional("Level", BS.Level, 0);
241276
}
242277
};
243278
} // namespace yaml
@@ -862,6 +897,11 @@ class YAMLConverter {
862897
PI.setLifetimebound(P.Lifetimebound);
863898
PI.setType(std::string(P.Type));
864899
PI.setRetainCountConvention(P.RetainCountConvention);
900+
BoundsSafetyInfo BSI;
901+
BSI.setKindAudited(P.BoundsSafety.Kind);
902+
BSI.setLevelAudited(P.BoundsSafety.Level);
903+
BSI.ExternalBounds = P.BoundsSafety.BoundsExpr.str();
904+
PI.BoundsSafety = BSI;
865905
if (static_cast<int>(OutInfo.Params.size()) <= P.Position)
866906
OutInfo.Params.resize(P.Position + 1);
867907
if (P.Position == -1)

0 commit comments

Comments
 (0)