Skip to content

Commit 1fd3a79

Browse files
author
Gabor Horvath
committed
[APINotes] Support annotating safety of APIs
Swift supports a strictly memory safe mode where every expressions using an unsafe feature or API need to be annotatated with the unsafe effect. This sometimes requires adding safety annotations to the foreign APIs. This PR adds support to inject such safety annotations.
1 parent a271d07 commit 1fd3a79

File tree

11 files changed

+190
-8
lines changed

11 files changed

+190
-8
lines changed

clang/docs/APINotes.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,20 @@ declaration kind), all of which are optional:
229229
- Name: vector
230230
SwiftConformsTo: Cxx.CxxSequence
231231

232+
:SwiftSafety:
233+
234+
Import a declaration as ``@safe`` or ``@unsafe`` to Swift.
235+
236+
::
237+
238+
Tags:
239+
- Name: UnsafeType
240+
SwiftSafety: unsafe
241+
- Name: span
242+
Methods:
243+
- Name: size
244+
SwiftSafety: safe
245+
232246
:Availability, AvailabilityMsg:
233247

234248
A value of "nonswift" is equivalent to ``NS_SWIFT_UNAVAILABLE``. A value of

clang/include/clang/APINotes/Types.h

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ enum class SwiftNewTypeKind {
4646
Enum,
4747
};
4848

49+
enum class SwiftSafetyKind { Unspecified, Safe, Unsafe, None };
50+
4951
/// Describes API notes data for any entity.
5052
///
5153
/// This is used as the base of all API notes.
@@ -71,13 +73,19 @@ class CommonEntityInfo {
7173
LLVM_PREFERRED_TYPE(bool)
7274
unsigned SwiftPrivate : 1;
7375

76+
LLVM_PREFERRED_TYPE(bool)
77+
unsigned SwiftSafetyAudited : 1;
78+
79+
LLVM_PREFERRED_TYPE(SwiftSafetyKind)
80+
unsigned SwiftSafety : 2;
81+
7482
public:
7583
/// Swift name of this entity.
7684
std::string SwiftName;
7785

7886
CommonEntityInfo()
7987
: Unavailable(0), UnavailableInSwift(0), SwiftPrivateSpecified(0),
80-
SwiftPrivate(0) {}
88+
SwiftPrivate(0), SwiftSafetyAudited(0), SwiftSafety(0) {}
8189

8290
std::optional<bool> isSwiftPrivate() const {
8391
return SwiftPrivateSpecified ? std::optional<bool>(SwiftPrivate)
@@ -89,6 +97,17 @@ class CommonEntityInfo {
8997
SwiftPrivate = Private.value_or(0);
9098
}
9199

100+
std::optional<SwiftSafetyKind> getSwiftSafety() const {
101+
return SwiftSafetyAudited ? std::optional<SwiftSafetyKind>(
102+
static_cast<SwiftSafetyKind>(SwiftSafety))
103+
: std::nullopt;
104+
}
105+
106+
void setSwiftSafety(SwiftSafetyKind Safety) {
107+
SwiftSafetyAudited = 1;
108+
SwiftSafety = static_cast<unsigned>(Safety);
109+
}
110+
92111
friend bool operator==(const CommonEntityInfo &, const CommonEntityInfo &);
93112

94113
CommonEntityInfo &operator|=(const CommonEntityInfo &RHS) {
@@ -108,6 +127,9 @@ class CommonEntityInfo {
108127
if (!SwiftPrivateSpecified)
109128
setSwiftPrivate(RHS.isSwiftPrivate());
110129

130+
if (!SwiftSafetyAudited && RHS.SwiftSafetyAudited)
131+
setSwiftSafety(*RHS.getSwiftSafety());
132+
111133
if (SwiftName.empty())
112134
SwiftName = RHS.SwiftName;
113135

@@ -123,7 +145,9 @@ inline bool operator==(const CommonEntityInfo &LHS,
123145
LHS.Unavailable == RHS.Unavailable &&
124146
LHS.UnavailableInSwift == RHS.UnavailableInSwift &&
125147
LHS.SwiftPrivateSpecified == RHS.SwiftPrivateSpecified &&
126-
LHS.SwiftPrivate == RHS.SwiftPrivate && LHS.SwiftName == RHS.SwiftName;
148+
LHS.SwiftPrivate == RHS.SwiftPrivate &&
149+
LHS.SwiftSafetyAudited == RHS.SwiftSafetyAudited &&
150+
LHS.SwiftSafety == RHS.SwiftSafety && LHS.SwiftName == RHS.SwiftName;
127151
}
128152

129153
inline bool operator!=(const CommonEntityInfo &LHS,

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 = 37; // SwiftDestroyOp
27+
const uint16_t VERSION_MINOR = 38; // SwiftSafety
2828

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

clang/lib/APINotes/APINotesReader.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,14 @@ class VersionedTableInfo {
9494

9595
/// Read serialized CommonEntityInfo.
9696
void ReadCommonEntityInfo(const uint8_t *&Data, CommonEntityInfo &Info) {
97-
uint8_t UnavailableBits = *Data++;
98-
Info.Unavailable = (UnavailableBits >> 1) & 0x01;
99-
Info.UnavailableInSwift = UnavailableBits & 0x01;
100-
if ((UnavailableBits >> 2) & 0x01)
101-
Info.setSwiftPrivate(static_cast<bool>((UnavailableBits >> 3) & 0x01));
97+
uint8_t EncodedBits = *Data++;
98+
Info.Unavailable = (EncodedBits >> 1) & 0x01;
99+
Info.UnavailableInSwift = EncodedBits & 0x01;
100+
if ((EncodedBits >> 2) & 0x01)
101+
Info.setSwiftPrivate(static_cast<bool>((EncodedBits >> 3) & 0x01));
102+
if ((EncodedBits >> 4) & 0x01)
103+
Info.setSwiftSafety(
104+
static_cast<SwiftSafetyKind>((EncodedBits >> 5) & 0x03));
102105

103106
unsigned MsgLength =
104107
endian::readNext<uint16_t, llvm::endianness::little>(Data);

clang/lib/APINotes/APINotesTypes.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,21 @@ LLVM_DUMP_METHOD void CommonEntityInfo::dump(llvm::raw_ostream &OS) const {
1818
OS << "[UnavailableInSwift] ";
1919
if (SwiftPrivateSpecified)
2020
OS << (SwiftPrivate ? "[SwiftPrivate] " : "");
21+
if (SwiftSafetyAudited) {
22+
switch (*getSwiftSafety()) {
23+
case SwiftSafetyKind::Safe:
24+
OS << "[Safe] ";
25+
break;
26+
case SwiftSafetyKind::Unsafe:
27+
OS << "[Unsafe] ";
28+
break;
29+
case SwiftSafetyKind::Unspecified:
30+
OS << "[Unspecified] ";
31+
break;
32+
case SwiftSafetyKind::None:
33+
break;
34+
}
35+
}
2136
if (!SwiftName.empty())
2237
OS << "Swift Name: " << SwiftName << ' ';
2338
OS << '\n';

clang/lib/APINotes/APINotesWriter.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,12 @@ void emitCommonEntityInfo(raw_ostream &OS, const CommonEntityInfo &CEI) {
507507
llvm::support::endian::Writer writer(OS, llvm::endianness::little);
508508

509509
uint8_t payload = 0;
510+
if (auto safety = CEI.getSwiftSafety()) {
511+
payload = static_cast<unsigned>(*safety);
512+
payload <<= 1;
513+
payload |= 0x01;
514+
}
515+
payload <<= 2;
510516
if (auto swiftPrivate = CEI.isSwiftPrivate()) {
511517
payload |= 0x01;
512518
if (*swiftPrivate)

clang/lib/APINotes/APINotesYAMLCompiler.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,18 @@
2929
using namespace clang;
3030
using namespace api_notes;
3131

32+
namespace llvm {
33+
namespace yaml {
34+
template <> struct ScalarEnumerationTraits<SwiftSafetyKind> {
35+
static void enumeration(IO &IO, SwiftSafetyKind &SK) {
36+
IO.enumCase(SK, "unspecified", SwiftSafetyKind::Unspecified);
37+
IO.enumCase(SK, "safe", SwiftSafetyKind::Safe);
38+
IO.enumCase(SK, "unsafe", SwiftSafetyKind::Unsafe);
39+
}
40+
};
41+
} // namespace yaml
42+
} // namespace llvm
43+
3244
namespace {
3345
enum class APIAvailability {
3446
Available = 0,
@@ -163,6 +175,7 @@ struct Method {
163175
bool Required = false;
164176
StringRef ResultType;
165177
StringRef SwiftReturnOwnership;
178+
SwiftSafetyKind SafetyKind = SwiftSafetyKind::None;
166179
};
167180

168181
typedef std::vector<Method> MethodsSeq;
@@ -199,6 +212,7 @@ template <> struct MappingTraits<Method> {
199212
IO.mapOptional("ResultType", M.ResultType, StringRef(""));
200213
IO.mapOptional("SwiftReturnOwnership", M.SwiftReturnOwnership,
201214
StringRef(""));
215+
IO.mapOptional("SwiftSafety", M.SafetyKind, SwiftSafetyKind::None);
202216
}
203217
};
204218
} // namespace yaml
@@ -214,6 +228,7 @@ struct Property {
214228
StringRef SwiftName;
215229
std::optional<bool> SwiftImportAsAccessors;
216230
StringRef Type;
231+
SwiftSafetyKind SafetyKind = SwiftSafetyKind::None;
217232
};
218233

219234
typedef std::vector<Property> PropertiesSeq;
@@ -235,6 +250,7 @@ template <> struct MappingTraits<Property> {
235250
IO.mapOptional("SwiftName", P.SwiftName, StringRef(""));
236251
IO.mapOptional("SwiftImportAsAccessors", P.SwiftImportAsAccessors);
237252
IO.mapOptional("Type", P.Type, StringRef(""));
253+
IO.mapOptional("SwiftSafety", P.SafetyKind, SwiftSafetyKind::None);
238254
}
239255
};
240256
} // namespace yaml
@@ -254,6 +270,7 @@ struct Class {
254270
std::optional<std::string> SwiftConformance;
255271
MethodsSeq Methods;
256272
PropertiesSeq Properties;
273+
SwiftSafetyKind SafetyKind = SwiftSafetyKind::None;
257274
};
258275

259276
typedef std::vector<Class> ClassesSeq;
@@ -279,6 +296,7 @@ template <> struct MappingTraits<Class> {
279296
IO.mapOptional("SwiftConformsTo", C.SwiftConformance);
280297
IO.mapOptional("Methods", C.Methods);
281298
IO.mapOptional("Properties", C.Properties);
299+
IO.mapOptional("SwiftSafety", C.SafetyKind, SwiftSafetyKind::None);
282300
}
283301
};
284302
} // namespace yaml
@@ -297,6 +315,7 @@ struct Function {
297315
StringRef Type;
298316
StringRef ResultType;
299317
StringRef SwiftReturnOwnership;
318+
SwiftSafetyKind SafetyKind = SwiftSafetyKind::None;
300319
};
301320

302321
typedef std::vector<Function> FunctionsSeq;
@@ -321,6 +340,7 @@ template <> struct MappingTraits<Function> {
321340
IO.mapOptional("ResultType", F.ResultType, StringRef(""));
322341
IO.mapOptional("SwiftReturnOwnership", F.SwiftReturnOwnership,
323342
StringRef(""));
343+
IO.mapOptional("SwiftSafety", F.SafetyKind, SwiftSafetyKind::None);
324344
}
325345
};
326346
} // namespace yaml
@@ -334,6 +354,7 @@ struct GlobalVariable {
334354
std::optional<bool> SwiftPrivate;
335355
StringRef SwiftName;
336356
StringRef Type;
357+
SwiftSafetyKind SafetyKind = SwiftSafetyKind::None;
337358
};
338359

339360
typedef std::vector<GlobalVariable> GlobalVariablesSeq;
@@ -353,6 +374,7 @@ template <> struct MappingTraits<GlobalVariable> {
353374
IO.mapOptional("SwiftPrivate", GV.SwiftPrivate);
354375
IO.mapOptional("SwiftName", GV.SwiftName, StringRef(""));
355376
IO.mapOptional("Type", GV.Type, StringRef(""));
377+
IO.mapOptional("SwiftSafety", GV.SafetyKind, SwiftSafetyKind::None);
356378
}
357379
};
358380
} // namespace yaml
@@ -364,6 +386,7 @@ struct EnumConstant {
364386
AvailabilityItem Availability;
365387
std::optional<bool> SwiftPrivate;
366388
StringRef SwiftName;
389+
SwiftSafetyKind SafetyKind = SwiftSafetyKind::None;
367390
};
368391

369392
typedef std::vector<EnumConstant> EnumConstantsSeq;
@@ -381,6 +404,7 @@ template <> struct MappingTraits<EnumConstant> {
381404
IO.mapOptional("AvailabilityMsg", EC.Availability.Msg, StringRef(""));
382405
IO.mapOptional("SwiftPrivate", EC.SwiftPrivate);
383406
IO.mapOptional("SwiftName", EC.SwiftName, StringRef(""));
407+
IO.mapOptional("SwiftSafety", EC.SafetyKind, SwiftSafetyKind::None);
384408
}
385409
};
386410
} // namespace yaml
@@ -424,6 +448,7 @@ struct Field {
424448
std::optional<bool> SwiftPrivate;
425449
StringRef SwiftName;
426450
StringRef Type;
451+
SwiftSafetyKind SafetyKind = SwiftSafetyKind::None;
427452
};
428453

429454
typedef std::vector<Field> FieldsSeq;
@@ -443,6 +468,7 @@ template <> struct MappingTraits<Field> {
443468
IO.mapOptional("SwiftPrivate", F.SwiftPrivate);
444469
IO.mapOptional("SwiftName", F.SwiftName, StringRef(""));
445470
IO.mapOptional("Type", F.Type, StringRef(""));
471+
IO.mapOptional("SwiftSafety", F.SafetyKind, SwiftSafetyKind::None);
446472
}
447473
};
448474
} // namespace yaml
@@ -470,6 +496,7 @@ struct Tag {
470496
std::optional<EnumConvenienceAliasKind> EnumConvenienceKind;
471497
std::optional<bool> SwiftCopyable;
472498
std::optional<bool> SwiftEscapable;
499+
SwiftSafetyKind SafetyKind = SwiftSafetyKind::None;
473500
FunctionsSeq Methods;
474501
FieldsSeq Fields;
475502

@@ -515,6 +542,7 @@ template <> struct MappingTraits<Tag> {
515542
IO.mapOptional("Methods", T.Methods);
516543
IO.mapOptional("Fields", T.Fields);
517544
IO.mapOptional("Tags", T.Tags);
545+
IO.mapOptional("SwiftSafety", T.SafetyKind, SwiftSafetyKind::None);
518546
}
519547
};
520548
} // namespace yaml
@@ -530,6 +558,7 @@ struct Typedef {
530558
std::optional<StringRef> NSErrorDomain;
531559
std::optional<SwiftNewTypeKind> SwiftType;
532560
std::optional<std::string> SwiftConformance;
561+
const SwiftSafetyKind SafetyKind = SwiftSafetyKind::None;
533562
};
534563

535564
typedef std::vector<Typedef> TypedefsSeq;
@@ -602,6 +631,7 @@ struct Namespace {
602631
StringRef SwiftName;
603632
std::optional<bool> SwiftPrivate;
604633
TopLevelItems Items;
634+
const SwiftSafetyKind SafetyKind = SwiftSafetyKind::None;
605635
};
606636
} // namespace
607637

@@ -797,6 +827,8 @@ class YAMLConverter {
797827
StringRef APIName) {
798828
convertAvailability(Common.Availability, Info, APIName);
799829
Info.setSwiftPrivate(Common.SwiftPrivate);
830+
if (Common.SafetyKind != SwiftSafetyKind::None)
831+
Info.setSwiftSafety(Common.SafetyKind);
800832
Info.SwiftName = std::string(Common.SwiftName);
801833
}
802834

@@ -956,6 +988,8 @@ class YAMLConverter {
956988
void convertFunction(const Function &Function, FuncOrMethodInfo &FI) {
957989
convertAvailability(Function.Availability, FI, Function.Name);
958990
FI.setSwiftPrivate(Function.SwiftPrivate);
991+
if (Function.SafetyKind != SwiftSafetyKind::None)
992+
FI.setSwiftSafety(Function.SafetyKind);
959993
FI.SwiftName = std::string(Function.SwiftName);
960994
std::optional<ParamInfo> This;
961995
convertParams(Function.Params, FI, This);

clang/lib/Sema/SemaAPINotes.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "CheckExprLifetime.h"
1414
#include "TypeLocBuilder.h"
1515
#include "clang/APINotes/APINotesReader.h"
16+
#include "clang/APINotes/Types.h"
1617
#include "clang/AST/Decl.h"
1718
#include "clang/AST/DeclCXX.h"
1819
#include "clang/AST/DeclObjC.h"
@@ -291,6 +292,29 @@ static void ProcessAPINotes(Sema &S, Decl *D,
291292
});
292293
}
293294

295+
// swift_safety
296+
if (auto SafetyKind = Info.getSwiftSafety()) {
297+
bool Addition = *SafetyKind != api_notes::SwiftSafetyKind::Unspecified;
298+
handleAPINotedAttribute<SwiftAttrAttr>(
299+
S, D, Addition, Metadata,
300+
[&] {
301+
return SwiftAttrAttr::Create(
302+
S.Context, *SafetyKind == api_notes::SwiftSafetyKind::Safe
303+
? "safe"
304+
: "unsafe");
305+
},
306+
[](const Decl *D) {
307+
return llvm::find_if(D->attrs(), [](const Attr *attr) {
308+
if (const auto *swiftAttr = dyn_cast<SwiftAttrAttr>(attr)) {
309+
if (swiftAttr->getAttribute() == "safe" ||
310+
swiftAttr->getAttribute() == "unsafe")
311+
return true;
312+
}
313+
return false;
314+
});
315+
});
316+
}
317+
294318
// swift_name
295319
if (!Info.SwiftName.empty()) {
296320
handleAPINotedAttribute<SwiftNameAttr>(

0 commit comments

Comments
 (0)