Skip to content

Commit dc09594

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 dc09594

File tree

11 files changed

+126
-8
lines changed

11 files changed

+126
-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: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ enum class SwiftNewTypeKind {
4646
Enum,
4747
};
4848

49+
enum class SwiftSafetyKind {
50+
None,
51+
Safe,
52+
Unsafe,
53+
};
54+
4955
/// Describes API notes data for any entity.
5056
///
5157
/// This is used as the base of all API notes.
@@ -71,13 +77,16 @@ class CommonEntityInfo {
7177
LLVM_PREFERRED_TYPE(bool)
7278
unsigned SwiftPrivate : 1;
7379

80+
LLVM_PREFERRED_TYPE(SwiftSafetyKind)
81+
unsigned SwiftSafety : 2;
82+
7483
public:
7584
/// Swift name of this entity.
7685
std::string SwiftName;
7786

7887
CommonEntityInfo()
7988
: Unavailable(0), UnavailableInSwift(0), SwiftPrivateSpecified(0),
80-
SwiftPrivate(0) {}
89+
SwiftPrivate(0), SwiftSafety(0) {}
8190

8291
std::optional<bool> isSwiftPrivate() const {
8392
return SwiftPrivateSpecified ? std::optional<bool>(SwiftPrivate)
@@ -89,6 +98,14 @@ class CommonEntityInfo {
8998
SwiftPrivate = Private.value_or(0);
9099
}
91100

101+
SwiftSafetyKind getSwiftSafety() const {
102+
return static_cast<SwiftSafetyKind>(SwiftSafety);
103+
}
104+
105+
void setSwiftSafety(SwiftSafetyKind Safety) {
106+
SwiftSafety = static_cast<unsigned>(Safety);
107+
}
108+
92109
friend bool operator==(const CommonEntityInfo &, const CommonEntityInfo &);
93110

94111
CommonEntityInfo &operator|=(const CommonEntityInfo &RHS) {
@@ -108,6 +125,9 @@ class CommonEntityInfo {
108125
if (!SwiftPrivateSpecified)
109126
setSwiftPrivate(RHS.isSwiftPrivate());
110127

128+
if (!SwiftSafety)
129+
setSwiftSafety(RHS.getSwiftSafety());
130+
111131
if (SwiftName.empty())
112132
SwiftName = RHS.SwiftName;
113133

@@ -123,7 +143,8 @@ inline bool operator==(const CommonEntityInfo &LHS,
123143
LHS.Unavailable == RHS.Unavailable &&
124144
LHS.UnavailableInSwift == RHS.UnavailableInSwift &&
125145
LHS.SwiftPrivateSpecified == RHS.SwiftPrivateSpecified &&
126-
LHS.SwiftPrivate == RHS.SwiftPrivate && LHS.SwiftName == RHS.SwiftName;
146+
LHS.SwiftPrivate == RHS.SwiftPrivate &&
147+
LHS.SwiftSafety == RHS.SwiftSafety && LHS.SwiftName == RHS.SwiftName;
127148
}
128149

129150
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) & 0x03)
103+
Info.setSwiftSafety(
104+
static_cast<SwiftSafetyKind>((EncodedBits >> 4) & 0x03));
102105

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

clang/lib/APINotes/APINotesTypes.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ LLVM_DUMP_METHOD void CommonEntityInfo::dump(llvm::raw_ostream &OS) const {
1818
OS << "[UnavailableInSwift] ";
1919
if (SwiftPrivateSpecified)
2020
OS << (SwiftPrivate ? "[SwiftPrivate] " : "");
21+
if (SwiftSafety)
22+
OS << (getSwiftSafety() == SwiftSafetyKind::Safe ? "[Safe] " : "[Unsafe]");
2123
if (!SwiftName.empty())
2224
OS << "Swift Name: " << SwiftName << ' ';
2325
OS << '\n';

clang/lib/APINotes/APINotesWriter.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,9 @@ 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 (CEI.getSwiftSafety() != SwiftSafetyKind::None)
511+
payload = CEI.getSwiftSafety() == SwiftSafetyKind::Safe ? 0x01 : 0x02;
512+
payload <<= 2;
510513
if (auto swiftPrivate = CEI.isSwiftPrivate()) {
511514
payload |= 0x01;
512515
if (*swiftPrivate)

clang/lib/APINotes/APINotesYAMLCompiler.cpp

Lines changed: 32 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::None);
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,7 @@ class YAMLConverter {
797827
StringRef APIName) {
798828
convertAvailability(Common.Availability, Info, APIName);
799829
Info.setSwiftPrivate(Common.SwiftPrivate);
830+
Info.setSwiftSafety(Common.SafetyKind);
800831
Info.SwiftName = std::string(Common.SwiftName);
801832
}
802833

@@ -956,6 +987,7 @@ class YAMLConverter {
956987
void convertFunction(const Function &Function, FuncOrMethodInfo &FI) {
957988
convertAvailability(Function.Availability, FI, Function.Name);
958989
FI.setSwiftPrivate(Function.SwiftPrivate);
990+
FI.setSwiftSafety(Function.SafetyKind);
959991
FI.SwiftName = std::string(Function.SwiftName);
960992
std::optional<ParamInfo> This;
961993
convertParams(Function.Params, FI, This);

clang/lib/Sema/SemaAPINotes.cpp

Lines changed: 8 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,13 @@ static void ProcessAPINotes(Sema &S, Decl *D,
291292
});
292293
}
293294

295+
// swift_safety
296+
if (auto SafetyKind = Info.getSwiftSafety();
297+
SafetyKind != api_notes::SwiftSafetyKind::None)
298+
D->addAttr(SwiftAttrAttr::Create(
299+
S.Context,
300+
SafetyKind == api_notes::SwiftSafetyKind::Safe ? "safe" : "unsafe"));
301+
294302
// swift_name
295303
if (!Info.SwiftName.empty()) {
296304
handleAPINotedAttribute<SwiftNameAttr>(

clang/test/APINotes/Inputs/Headers/SwiftImportAs.apinotes

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,23 @@ Tags:
3535
- Name: NoncopyableWithDestroyType
3636
SwiftCopyable: false
3737
SwiftDestroyOp: NCDDestroy
38+
- Name: ImportAsUnsafeStruct
39+
SwiftSafety: unsafe
40+
- Name: StructWithUnsafeMethod
41+
Methods:
42+
- Name: ImportAsUnsafeMethod
43+
SwiftSafety: unsafe
44+
- Name: ImportAsUnsafeMethodActuallySafe
45+
SwiftSafety: safe
3846

3947
Functions:
4048
- Name: functionReturningFrt__
4149
- Name: functionReturningFrt_returns_unretained
4250
SwiftReturnOwnership: unretained
4351
- Name: functionReturningFrt_returns_retained
4452
SwiftReturnOwnership: retained
53+
- Name: ImportAsUnsafe
54+
SwiftSafety: unsafe
4555
Typedefs:
4656
- Name: WrappedOptions
4757
SwiftWrapper: struct

clang/test/APINotes/Inputs/Headers/SwiftImportAs.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,11 @@ struct NoncopyableWithDestroyType {
3636
};
3737

3838
void NCDDestroy(NoncopyableWithDestroyType instance);
39+
40+
void ImportAsUnsafe();
41+
struct ImportAsUnsafeStruct {
42+
};
43+
struct StructWithUnsafeMethod {
44+
void ImportAsUnsafeMethod();
45+
void ImportAsUnsafeMethodActuallySafe();
46+
};

0 commit comments

Comments
 (0)