Skip to content

Commit b7df4ea

Browse files
committed
[BoundsSafety][APINotes] Add support for bounds attribute on return value
Previously you could only attach bounds attributes to function parameters. Now return values are also supported, although not yet for ObjectiveC methods. Adds test case for bounds safety API notes in C++, and fixes bug where late parsed bounds attributes on C++ method parameters are parsed twice, due to not being removed from the late attributes list after parsing. rdar://146332875
1 parent d2ed2d2 commit b7df4ea

18 files changed

+631
-35
lines changed

clang/include/clang/APINotes/Types.h

+5
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,11 @@ class FunctionInfo : public CommonEntityInfo {
623623
/// Ownership convention for return value
624624
std::string SwiftReturnOwnership;
625625

626+
/* TO_UPSTREAM(BoundsSafety) ON */
627+
/// Bounds annotations for the return value
628+
std::optional<BoundsSafetyInfo> ReturnBoundsSafety;
629+
/* TO_UPSTREAM(BoundsSafety) OFF */
630+
626631
/// The function parameters.
627632
std::vector<ParamInfo> Params;
628633

clang/include/clang/Parse/Parser.h

+8-4
Original file line numberDiff line numberDiff line change
@@ -1655,6 +1655,9 @@ class Parser : public CodeCompletionHandler {
16551655
ParsedAttributes *OutAttrs = nullptr);
16561656
void ParseLexedAttribute(LateParsedAttribute &LA,
16571657
bool EnterScope, bool OnDefinition);
1658+
/// Note: it's important the the LateParsedAttribute is removed after parsing
1659+
/// to prevent being reparsed later in the wrong context. This is handled
1660+
/// automatically by Parser::ParseLexedCAttributeList.
16581661
void ParseLexedCAttribute(LateParsedAttribute &LA, bool EnterScope,
16591662
ParsedAttributes *OutAttrs = nullptr);
16601663
void ParseLexedMethodDeclarations(ParsingClass &Class);
@@ -3944,10 +3947,11 @@ class Parser : public CodeCompletionHandler {
39443947
/// \param ParentDecl If a function or method is provided, the parameters are
39453948
/// added to the current parsing context.
39463949
/// \param IncludeLoc The location at which this parse was triggered.
3947-
ExprResult ParseBoundsAttributeArgFromString(StringRef ExprStr,
3948-
StringRef Context,
3949-
Decl *ParentDecl,
3950-
SourceLocation IncludeLoc);
3950+
/// \param Payload Info to be passed along to the completion handler before
3951+
/// leaving the current context
3952+
void ParseBoundsAttributeArgFromString(
3953+
StringRef ExprStr, StringRef Context, Decl *ParentDecl,
3954+
SourceLocation IncludeLoc, Sema::IncompleteBoundsAttributeInfo Payload);
39513955
/* TO_UPSTREAM(BoundsSafety) OFF */
39523956

39533957
//===--------------------------------------------------------------------===//

clang/include/clang/Sema/Sema.h

+12-1
Original file line numberDiff line numberDiff line change
@@ -1132,8 +1132,19 @@ class Sema final : public SemaBase {
11321132
ParseTypeFromStringCallback;
11331133

11341134
/* TO_UPSTREAM(BoundsSafety) ON */
1135+
// Info for completing the processing of the attribute after parsing its
1136+
// argument
1137+
struct IncompleteBoundsAttributeInfo {
1138+
AttributeCommonInfo::Kind Kind;
1139+
unsigned Level;
1140+
Decl *D;
1141+
};
1142+
/// Complete the suspended bounds attribute after argument has been parsed
1143+
void CompleteBoundsAttribute(Expr *ParsedExpr,
1144+
IncompleteBoundsAttributeInfo Info);
11351145
/// Callback to the parser to parse a type expressed as a string.
1136-
std::function<ExprResult(StringRef, StringRef, Decl *, SourceLocation)>
1146+
std::function<void(StringRef, StringRef, Decl *, SourceLocation,
1147+
IncompleteBoundsAttributeInfo Info)>
11371148
ParseBoundsAttributeArgFromStringCallback;
11381149
/* TO_UPSTREAM(BoundsSafety) OFF */
11391150

clang/lib/APINotes/APINotesReader.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,14 @@ void ReadFunctionInfo(const uint8_t *&Data, FunctionInfo &Info) {
384384
Payload >>= 3;
385385
Info.NullabilityAudited = Payload & 0x1;
386386
Payload >>= 1;
387+
/* TO_UPSTREAM(BoundsSafety) ON */
388+
if (Payload & 0x01) {
389+
BoundsSafetyInfo BSI;
390+
ReadBoundsSafetyInfo(Data, BSI);
391+
Info.ReturnBoundsSafety = BSI;
392+
}
393+
/* TO_UPSTREAM(BoundsSafety) OFF */
394+
Payload >>= 1;
387395
assert(Payload == 0 && "Bad API notes");
388396

389397
Info.NumAdjustedNullable =

clang/lib/APINotes/APINotesTypes.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ LLVM_DUMP_METHOD void ObjCPropertyInfo::dump(llvm::raw_ostream &OS) const {
6161
OS << '\n';
6262
}
6363

64+
/* TO_UPSTREAM(BoundsSafety) ON */
6465
LLVM_DUMP_METHOD void BoundsSafetyInfo::dump(llvm::raw_ostream &OS) const {
6566
if (KindAudited) {
6667
assert((BoundsSafetyKind)Kind >= BoundsSafetyKind::CountedBy);
@@ -88,6 +89,7 @@ LLVM_DUMP_METHOD void BoundsSafetyInfo::dump(llvm::raw_ostream &OS) const {
8889
OS << "ExternalBounds: "
8990
<< (ExternalBounds.empty() ? "<missing>" : ExternalBounds) << '\n';
9091
}
92+
/* TO_UPSTREAM(BoundsSafety) OFF */
9193

9294
LLVM_DUMP_METHOD void ParamInfo::dump(llvm::raw_ostream &OS) const {
9395
static_cast<const VariableInfo &>(*this).dump(OS);
@@ -97,8 +99,10 @@ LLVM_DUMP_METHOD void ParamInfo::dump(llvm::raw_ostream &OS) const {
9799
OS << (Lifetimebound ? "[Lifetimebound] " : "");
98100
OS << "RawRetainCountConvention: " << RawRetainCountConvention << ' ';
99101
OS << '\n';
102+
/* TO_UPSTREAM(BoundsSafety) ON */
100103
if (BoundsSafety)
101104
BoundsSafety->dump(OS);
105+
/* TO_UPSTREAM(BoundsSafety) OFF */
102106
}
103107

104108
LLVM_DUMP_METHOD void FunctionInfo::dump(llvm::raw_ostream &OS) const {
@@ -109,6 +113,12 @@ LLVM_DUMP_METHOD void FunctionInfo::dump(llvm::raw_ostream &OS) const {
109113
OS << "Result Type: " << ResultType << ' ';
110114
if (!SwiftReturnOwnership.empty())
111115
OS << "SwiftReturnOwnership: " << SwiftReturnOwnership << ' ';
116+
/* TO_UPSTREAM(BoundsSafety) ON */
117+
if (ReturnBoundsSafety) {
118+
OS << "Result bounds: ";
119+
ReturnBoundsSafety->dump(OS);
120+
}
121+
/* TO_UPSTREAM(BoundsSafety) OFF */
112122
if (!Params.empty())
113123
OS << '\n';
114124
for (auto &PI : Params)

clang/lib/APINotes/APINotesWriter.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,10 @@ unsigned getFunctionInfoSize(const FunctionInfo &FI) {
11481148
size += getParamInfoSize(P);
11491149
size += sizeof(uint16_t) + FI.ResultType.size();
11501150
size += sizeof(uint16_t) + FI.SwiftReturnOwnership.size();
1151+
/* TO_UPSTREAM(BoundsSafety) ON */
1152+
if (auto BSI = FI.ReturnBoundsSafety)
1153+
size += getBoundsSafetyInfoSize(*BSI);
1154+
/* TO_UPSTREAM(BoundsSafety) OFF */
11511155
return size;
11521156
}
11531157

@@ -1156,6 +1160,11 @@ void emitFunctionInfo(raw_ostream &OS, const FunctionInfo &FI) {
11561160
emitCommonEntityInfo(OS, FI);
11571161

11581162
uint8_t flags = 0;
1163+
/* TO_UPSTREAM(BoundsSafety) ON */
1164+
if (FI.ReturnBoundsSafety)
1165+
flags |= 0x01;
1166+
flags <<= 0x01;
1167+
/* TO_UPSTREAM(BoundsSafety) OFF */
11591168
flags |= FI.NullabilityAudited;
11601169
flags <<= 3;
11611170
if (auto RCC = FI.getRetainCountConvention())
@@ -1164,6 +1173,10 @@ void emitFunctionInfo(raw_ostream &OS, const FunctionInfo &FI) {
11641173
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
11651174

11661175
writer.write<uint8_t>(flags);
1176+
/* TO_UPSTREAM(BoundsSafety) ON */
1177+
if (auto BSI = FI.ReturnBoundsSafety)
1178+
emitBoundsSafetyInfo(OS, *FI.ReturnBoundsSafety);
1179+
/* TO_UPSTREAM(BoundsSafety) OFF */
11671180
writer.write<uint8_t>(FI.NumAdjustedNullable);
11681181
writer.write<uint64_t>(FI.NullabilityPayload);
11691182

clang/lib/APINotes/APINotesYAMLCompiler.cpp

+23-2
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,9 @@ struct Method {
319319
bool Required = false;
320320
StringRef ResultType;
321321
StringRef SwiftReturnOwnership;
322+
/* TO_UPSTREAM(BoundsSafety) ON */
323+
std::optional<BoundsSafetyNotes> ReturnBoundsSafety;
324+
/* TO_UPSTREAM(BoundsSafety) OFF */
322325
};
323326

324327
typedef std::vector<Method> MethodsSeq;
@@ -355,6 +358,9 @@ template <> struct MappingTraits<Method> {
355358
IO.mapOptional("ResultType", M.ResultType, StringRef(""));
356359
IO.mapOptional("SwiftReturnOwnership", M.SwiftReturnOwnership,
357360
StringRef(""));
361+
/* TO_UPSTREAM(BoundsSafety) ON */
362+
IO.mapOptional("BoundsSafety", M.ReturnBoundsSafety);
363+
/* TO_UPSTREAM(BoundsSafety) OFF */
358364
}
359365
};
360366
} // namespace yaml
@@ -451,6 +457,9 @@ struct Function {
451457
StringRef Type;
452458
StringRef ResultType;
453459
StringRef SwiftReturnOwnership;
460+
/* TO_UPSTREAM(BoundsSafety) ON */
461+
std::optional<BoundsSafetyNotes> ReturnBoundsSafety;
462+
/* TO_UPSTREAM(BoundsSafety) OFF */
454463
};
455464

456465
typedef std::vector<Function> FunctionsSeq;
@@ -475,6 +484,9 @@ template <> struct MappingTraits<Function> {
475484
IO.mapOptional("ResultType", F.ResultType, StringRef(""));
476485
IO.mapOptional("SwiftReturnOwnership", F.SwiftReturnOwnership,
477486
StringRef(""));
487+
/* TO_UPSTREAM(BoundsSafety) ON */
488+
IO.mapOptional("BoundsSafety", F.ReturnBoundsSafety);
489+
/* TO_UPSTREAM(BoundsSafety) OFF */
478490
}
479491
};
480492
} // namespace yaml
@@ -905,14 +917,14 @@ class YAMLConverter {
905917
PI.setLifetimebound(P.Lifetimebound);
906918
PI.setType(std::string(P.Type));
907919
PI.setRetainCountConvention(P.RetainCountConvention);
908-
BoundsSafetyInfo BSI;
909920
/* TO_UPSTREAM(BoundsSafety) ON */
910921
if (P.BoundsSafety) {
922+
BoundsSafetyInfo BSI;
911923
BSI.setKindAudited(P.BoundsSafety->Kind);
912924
BSI.setLevelAudited(P.BoundsSafety->Level);
913925
BSI.ExternalBounds = P.BoundsSafety->BoundsExpr.str();
926+
PI.BoundsSafety = BSI;
914927
}
915-
PI.BoundsSafety = BSI;
916928
/* TO_UPSTREAM(BoundsSafety) OFF */
917929
if (static_cast<int>(OutInfo.Params.size()) <= P.Position)
918930
OutInfo.Params.resize(P.Position + 1);
@@ -1112,6 +1124,15 @@ class YAMLConverter {
11121124
convertAvailability(Function.Availability, FI, Function.Name);
11131125
FI.setSwiftPrivate(Function.SwiftPrivate);
11141126
FI.SwiftName = std::string(Function.SwiftName);
1127+
/* TO_UPSTREAM(BoundsSafety) ON */
1128+
if (Function.ReturnBoundsSafety) {
1129+
BoundsSafetyInfo BSI;
1130+
BSI.setKindAudited(Function.ReturnBoundsSafety->Kind);
1131+
BSI.setLevelAudited(Function.ReturnBoundsSafety->Level);
1132+
BSI.ExternalBounds = Function.ReturnBoundsSafety->BoundsExpr.str();
1133+
FI.ReturnBoundsSafety = BSI;
1134+
}
1135+
/* TO_UPSTREAM(BoundsSafety) OFF */
11151136
std::optional<ParamInfo> This;
11161137
convertParams(Function.Params, FI, This);
11171138
if constexpr (std::is_same_v<FuncOrMethodInfo, CXXMethodInfo>)

clang/lib/Parse/ParseDecl.cpp

+8-9
Original file line numberDiff line numberDiff line change
@@ -7893,7 +7893,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
78937893
StartLoc = LParenLoc;
78947894

78957895
/* TO_UPSTREAM(BoundsSafety) ON */
7896-
LateParsedAttrList LateParamAttrs(/*PSoon=*/false,
7896+
LateParsedAttrList LateParamAttrs(/*PSoon=*/true,
78977897
/*LateAttrParseExperimentalExtOnly=*/true);
78987898
/* TO_UPSTREAM(BoundsSafety) OFF */
78997899

@@ -8018,9 +8018,7 @@ void Parser::ParseFunctionDeclarator(Declarator &D,
80188018
}
80198019

80208020
/* TO_UPSTREAM(BoundsSafety) ON*/
8021-
for (auto LateParamAttr : LateParamAttrs) {
8022-
ParseLexedCAttribute(*LateParamAttr, true);
8023-
}
8021+
ParseLexedCAttributeList(LateParamAttrs, true, nullptr);
80248022
/* TO_UPSTREAM(BoundsSafety) OFF*/
80258023

80268024
// Collect non-parameter declarations from the prototype if this is a function
@@ -8996,10 +8994,9 @@ TypeResult Parser::ParseTypeFromString(StringRef TypeStr, StringRef Context,
89968994
}
89978995

89988996
/* TO_UPSTREAM(BoundsSafety) ON */
8999-
ExprResult
9000-
Parser::ParseBoundsAttributeArgFromString(StringRef ExprStr, StringRef Context,
9001-
Decl *ParentDecl,
9002-
SourceLocation IncludeLoc) {
8997+
void Parser::ParseBoundsAttributeArgFromString(
8998+
StringRef ExprStr, StringRef Context, Decl *ParentDecl,
8999+
SourceLocation IncludeLoc, Sema::IncompleteBoundsAttributeInfo Payload) {
90039000
// Consume (unexpanded) tokens up to the end-of-directive.
90049001
SmallVector<Token, 4> Tokens;
90059002
{
@@ -9073,9 +9070,11 @@ Parser::ParseBoundsAttributeArgFromString(StringRef ExprStr, StringRef Context,
90739070
// Consume the end token.
90749071
if (Tok.is(tok::eof) && Tok.getEofData() == ExprStr.data())
90759072
ConsumeAnyToken();
9073+
// Need to create the type before leaving function context
9074+
if (Result.isUsable())
9075+
Actions.CompleteBoundsAttribute(Result.get(), Payload);
90769076
if (EnterScope)
90779077
Actions.ActOnExitFunctionContext();
9078-
return Result;
90799078
}
90809079
/* TO_UPSTREAM(BoundsSafety) OFF */
90819080

clang/lib/Parse/Parser.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,10 @@ Parser::Parser(Preprocessor &pp, Sema &actions, bool skipFunctionBodies)
7979
/* TO_UPSTREAM(BoundsSafety) ON */
8080
Actions.ParseBoundsAttributeArgFromStringCallback =
8181
[this](StringRef ExprStr, StringRef Context, Decl *Parent,
82-
SourceLocation IncludeLoc) {
82+
SourceLocation IncludeLoc,
83+
Sema::IncompleteBoundsAttributeInfo Payload) {
8384
return this->ParseBoundsAttributeArgFromString(ExprStr, Context, Parent,
84-
IncludeLoc);
85+
IncludeLoc, Payload);
8586
};
8687
/* TO_UPSTREAM(BoundsSafety) OFF */
8788
}

clang/lib/Sema/SemaAPINotes.cpp

+40-16
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,34 @@ static void ProcessAPINotes(Sema &S, Decl *D,
410410
}
411411

412412
/* TO_UPSTREAM(BoundsSafety) ON */
413+
void Sema::CompleteBoundsAttribute(Expr *ParsedExpr,
414+
IncompleteBoundsAttributeInfo Info) {
415+
assert(ParsedExpr);
416+
std::string AttrName;
417+
switch (Info.Kind) {
418+
case AttributeCommonInfo::AT_CountedBy:
419+
AttrName = "__counted_by";
420+
break;
421+
case AttributeCommonInfo::AT_CountedByOrNull:
422+
AttrName = "__counted_by_or_null";
423+
break;
424+
case AttributeCommonInfo::AT_SizedBy:
425+
AttrName = "__sized_by";
426+
break;
427+
case AttributeCommonInfo::AT_SizedByOrNull:
428+
AttrName = "__sized_by_or_null";
429+
break;
430+
case AttributeCommonInfo::AT_PtrEndedBy:
431+
AttrName = "__ended_by";
432+
break;
433+
default:
434+
llvm_unreachable("invalid bounds attribute kind in API notes");
435+
}
436+
applyPtrCountedByEndedByAttr(Info.D, Info.Level, Info.Kind, ParsedExpr,
437+
Info.D->getLocation(), Info.D->getSourceRange(),
438+
AttrName,
439+
/* originates in API notes */ true);
440+
}
413441
static void applyBoundsSafety(Sema &S, ValueDecl *D,
414442
const api_notes::BoundsSafetyInfo &Info,
415443
VersionedInfoMetadata Metadata) {
@@ -420,41 +448,29 @@ static void applyBoundsSafety(Sema &S, ValueDecl *D,
420448
if (auto ParmDecl = dyn_cast<ParmVarDecl>(ScopeDecl)) {
421449
ScopeDecl = dyn_cast<FunctionDecl>(ParmDecl->getDeclContext());
422450
}
423-
auto ParsedExpr = S.ParseBoundsAttributeArgFromStringCallback(
424-
Info.ExternalBounds, "<API Notes>", ScopeDecl, D->getLocation());
425-
if (ParsedExpr.isInvalid())
426-
return;
427-
428-
std::string AttrName;
429-
AttributeCommonInfo::Kind Kind;
430451
assert(*Info.getKind() >= BoundsSafetyKind::CountedBy);
431452
assert(*Info.getKind() <= BoundsSafetyKind::EndedBy);
453+
AttributeCommonInfo::Kind Kind;
432454
switch (*Info.getKind()) {
433455
case BoundsSafetyKind::CountedBy:
434-
AttrName = "__counted_by";
435456
Kind = AttributeCommonInfo::AT_CountedBy;
436457
break;
437458
case BoundsSafetyKind::CountedByOrNull:
438-
AttrName = "__counted_by_or_null";
439459
Kind = AttributeCommonInfo::AT_CountedByOrNull;
440460
break;
441461
case BoundsSafetyKind::SizedBy:
442-
AttrName = "__sized_by";
443462
Kind = AttributeCommonInfo::AT_SizedBy;
444463
break;
445464
case BoundsSafetyKind::SizedByOrNull:
446-
AttrName = "__sized_by_or_null";
447465
Kind = AttributeCommonInfo::AT_SizedByOrNull;
448466
break;
449467
case BoundsSafetyKind::EndedBy:
450-
AttrName = "__ended_by";
451468
Kind = AttributeCommonInfo::AT_PtrEndedBy;
452469
break;
453470
}
454-
455-
S.applyPtrCountedByEndedByAttr(
456-
D, *Info.getLevel(), Kind, ParsedExpr.get(), D->getLocation(),
457-
D->getSourceRange(), AttrName, /* originates in API notes */ true);
471+
S.ParseBoundsAttributeArgFromStringCallback(
472+
Info.ExternalBounds, "<API Notes>", ScopeDecl, D->getLocation(),
473+
{Kind, *Info.getLevel(), D});
458474
}
459475
}
460476
/* TO_UPSTREAM(BoundsSafety) OFF */
@@ -597,6 +613,14 @@ static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc,
597613
}
598614
}
599615

616+
/* TO_UPSTREAM(BoundsSafety) ON */
617+
// FIXME(hnrklssn): apply to ObjC methods when support has landed
618+
if (FD && Info.ReturnBoundsSafety.has_value())
619+
// applyPtrCountedByEndedByAttr rebuilds FunctionType,
620+
// no need to set AnyTypeChanged
621+
applyBoundsSafety(S, FD, *Info.ReturnBoundsSafety, Metadata);
622+
/* TO_UPSTREAM(BoundsSafety) OFF */
623+
600624
// If the result type or any of the parameter types changed for a function
601625
// declaration, we have to rebuild the type.
602626
if (FD && AnyTypeChanged) {

0 commit comments

Comments
 (0)