Skip to content

Commit abd1fd9

Browse files
committed
[cxx-interop] Flip the switch: only import safe APIs.
1 parent 03e4e5c commit abd1fd9

40 files changed

+671
-189
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# C++ Interop User Manual
2+
3+
## Reference Types
4+
5+
Reference types have reference semantics and object identity. A reference type is a pointer (or “reference”) to some object which means there is a layer of indirection. When a reference type is copied, the pointer’s value is copied rather than the object’s storage. This means reference types can be used to represent non-copyable types in C++. Any C++ APIs that use reference types must have at least one layer of indirection to the type (a pointer or reference). Currently reference types must be immortal (never deallocated) or have manually managed lifetimes. You can specify a type has reference semantics by using the `import_reference` swift attribute.
6+
7+
## Owned Types
8+
9+
Owned types “own” some storage which can be copied and destroyed. An owned type must be copyable and destructible. The copy constructor must copy any storage that is owned by the type and the destructor must destroy that storage. Copies and destroys must balance out and these operations must not have side effects. Examples of owned types include `std::vector` and `std::string`. You can specify a type is an owned type by using the `import_owned` swift attribute.
10+
11+
## Iterator Types
12+
13+
An iterator represents a point in a range. Iterator types must provide a comparison operator and increment operator (increment or ++ operators are imported as a member called successor). Iterators can be returned by `begin` and `end` methods to form a range which will automatically be imported as a Sequence in Swift. Iterators can often be inferred by the compiler, but you can also specify a type is an iterator by using the `import_iterator` swift attribute.
14+
15+
## Trivial Types
16+
17+
Trivial types can be copied by copying the bits of a value of the trivial type and do not need any special destruction logic. Trivial types are inferred by the compiler and cannot be specified using an attribute. Trivial types own their storage, so rules below that apply to owned types also apply to trivial types (specifically regarding projections). Pointers are not trivial types. When Objective-C++ mode is enabled, C++ types that hold Objective-C classes are still considered trivial, even though they technically violate the above contract.
18+
19+
## Unsafe Types
20+
21+
All types which do not fall into one of the above categories are considered unsafe. Any unsafe API, including unsafe types that are copyable and destructible may be imported using the `import_unsafe` swift attribute. There are two kinds of unsafe types: **unsafe pointer types** and **unsafe lifetime types.**
22+
23+
**Unsafe Pointer Types:** are types which contain an un-owned pointer. This type is assumed to represent an unsafe memory projection when used as a return type for the method of an owned type.
24+
25+
**Unsafe Lifetime Types:** are types that have custom lifetime operations such as custom copy constructors or custom destructors. These custom lifetime operations are assumed to be unsafe in all contexts.
26+
27+
## Un-importable Types
28+
29+
Types that are not copyable or destructible cannot be represented in Swift, so they cannot be imported (even if they are marked with the `import_unsafe` attribute).
30+
31+
## API rules
32+
33+
The Swift compiler enforces certain API rules, not to ensure a completely safe C++ API interface, but to prevent especially unsafe patterns that will likely lead to bugs. Many of these unsafe patterns stem from the subtly different lifetime semantics in C++ and Swift and aim to prevent memory projections of owned types. The currently enforced rules are as follows:
34+
35+
* Unsafe lifetime types are not allowed to be used in any contexts.
36+
* Unsafe pointer types are not allowed to represent unsafe memory projections from owned types. Note: unsafe pointer types *are* allowed to represent memory projections from reference types. Note: the Swift compiler assumes that global and static functions do not return projections.
37+
* Iterators are not allowed to represent unsafe memory projections from owned types. Iterators may be used safely through the CxxIterator and CxxSequence protocols which iterators and ranges automatically conform to.
38+
* Members of an unsafe type that is explicitly imported using the `import_unsafe` swift attribute are still subject to the above rules. Each individual unsafe member must also have the `import_unsafe` swift attribute to be usable.
39+

include/swift/ClangImporter/ClangImporterRequests.h

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,109 @@ class ClangRecordMemberLookup
178178
evaluate(Evaluator &evaluator, ClangRecordMemberLookupDescriptor desc) const;
179179
};
180180

181+
enum class CxxRecordSemanticsKind {
182+
Trivial,
183+
Owned,
184+
Reference,
185+
Iterator,
186+
// An API that has be annotated as explicitly unsafe, but still importable.
187+
// TODO: we should rename these APIs.
188+
ExplicitlyUnsafe,
189+
// A record that has custom lifetime operations which we cannot prove are
190+
// safe.
191+
UnsafeLifetimeOperation,
192+
// A record that contains a pointer (aka non-trivial type).
193+
UnsafePointerMember
194+
};
195+
196+
struct CxxRecordSemanticsDescriptor final {
197+
const clang::CXXRecordDecl *decl;
198+
199+
CxxRecordSemanticsDescriptor(const clang::CXXRecordDecl *decl) : decl(decl) {}
200+
201+
friend llvm::hash_code hash_value(const CxxRecordSemanticsDescriptor &desc) {
202+
return llvm::hash_combine(desc.decl);
203+
}
204+
205+
friend bool operator==(const CxxRecordSemanticsDescriptor &lhs,
206+
const CxxRecordSemanticsDescriptor &rhs) {
207+
return lhs.decl == rhs.decl;
208+
}
209+
210+
friend bool operator!=(const CxxRecordSemanticsDescriptor &lhs,
211+
const CxxRecordSemanticsDescriptor &rhs) {
212+
return !(lhs == rhs);
213+
}
214+
};
215+
216+
void simple_display(llvm::raw_ostream &out, CxxRecordSemanticsDescriptor desc);
217+
SourceLoc extractNearestSourceLoc(CxxRecordSemanticsDescriptor desc);
218+
219+
/// What pattern does this C++ API fit? Uses attributes such as
220+
/// import_owned and import_as_reference to determine the pattern.
221+
class CxxRecordSemantics
222+
: public SimpleRequest<CxxRecordSemantics,
223+
CxxRecordSemanticsKind(CxxRecordSemanticsDescriptor),
224+
RequestFlags::Cached> {
225+
public:
226+
using SimpleRequest::SimpleRequest;
227+
228+
// Caching
229+
bool isCached() const { return true; }
230+
231+
// Source location
232+
SourceLoc getNearestLoc() const { return SourceLoc(); };
233+
234+
private:
235+
friend SimpleRequest;
236+
237+
// Evaluation.
238+
CxxRecordSemanticsKind evaluate(Evaluator &evaluator,
239+
CxxRecordSemanticsDescriptor) const;
240+
};
241+
242+
struct SafeUseOfCxxDeclDescriptor final {
243+
const clang::Decl *decl;
244+
245+
SafeUseOfCxxDeclDescriptor(const clang::Decl *decl) : decl(decl) {}
246+
247+
friend llvm::hash_code hash_value(const SafeUseOfCxxDeclDescriptor &desc) {
248+
return llvm::hash_combine(desc.decl);
249+
}
250+
251+
friend bool operator==(const SafeUseOfCxxDeclDescriptor &lhs,
252+
const SafeUseOfCxxDeclDescriptor &rhs) {
253+
return lhs.decl == rhs.decl;
254+
}
255+
256+
friend bool operator!=(const SafeUseOfCxxDeclDescriptor &lhs,
257+
const SafeUseOfCxxDeclDescriptor &rhs) {
258+
return !(lhs == rhs);
259+
}
260+
};
261+
262+
void simple_display(llvm::raw_ostream &out, SafeUseOfCxxDeclDescriptor desc);
263+
SourceLoc extractNearestSourceLoc(SafeUseOfCxxDeclDescriptor desc);
264+
265+
class IsSafeUseOfCxxDecl
266+
: public SimpleRequest<IsSafeUseOfCxxDecl, bool(SafeUseOfCxxDeclDescriptor),
267+
RequestFlags::Cached> {
268+
public:
269+
using SimpleRequest::SimpleRequest;
270+
271+
// Caching
272+
bool isCached() const { return true; }
273+
274+
// Source location
275+
SourceLoc getNearestLoc() const { return SourceLoc(); };
276+
277+
private:
278+
friend SimpleRequest;
279+
280+
// Evaluation.
281+
bool evaluate(Evaluator &evaluator, SafeUseOfCxxDeclDescriptor desc) const;
282+
};
283+
181284
#define SWIFT_TYPEID_ZONE ClangImporter
182285
#define SWIFT_TYPEID_HEADER "swift/ClangImporter/ClangImporterTypeIDZone.def"
183286
#include "swift/Basic/DefineTypeIDZone.h"

include/swift/ClangImporter/ClangImporterTypeIDZone.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,9 @@ SWIFT_REQUEST(ClangImporter, CXXNamespaceMemberLookup,
2424
SWIFT_REQUEST(ClangImporter, ClangRecordMemberLookup,
2525
Decl *(ClangRecordMemberLookupDescriptor), Uncached,
2626
NoLocationInfo)
27+
SWIFT_REQUEST(ClangImporter, CxxRecordSemantics,
28+
CxxRecordSemanticsKind(const clang::CXXRecordDecl *), Cached,
29+
NoLocationInfo)
30+
SWIFT_REQUEST(ClangImporter, IsSafeUseOfCxxDecl,
31+
bool(SafeUseOfCxxRecordDescriptor), Cached,
32+
NoLocationInfo)

0 commit comments

Comments
 (0)